tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_quicksuggest_realtime_flight_status.js (29376B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 const TEST_MERINO_SINGLE = [
      5  {
      6    provider: "flightaware",
      7    is_sponsored: false,
      8    score: 0,
      9    title: "Flight Suggestion",
     10    custom_details: {
     11      flightaware: {
     12        values: [
     13          {
     14            flight_number: "A1",
     15            airline: {
     16              name: null,
     17              code: null,
     18              icon: null,
     19            },
     20            origin: {
     21              city: "Origin",
     22              code: "O",
     23            },
     24            destination: {
     25              city: "Destination",
     26              code: "D",
     27            },
     28            departure: {
     29              scheduled_time: "2025-09-17T14:05:00Z",
     30            },
     31            arrival: {
     32              scheduled_time: "2025-09-17T18:30:00Z",
     33            },
     34            status: "Scheduled",
     35            url: "https://example.com/A1",
     36          },
     37        ],
     38      },
     39    },
     40  },
     41 ];
     42 
     43 const TEST_MERINO_MULTI = [
     44  {
     45    provider: "flightaware",
     46    is_sponsored: false,
     47    score: 0,
     48    title: "Flight Suggestion",
     49    custom_details: {
     50      flightaware: {
     51        values: [
     52          {
     53            flight_number: "A1",
     54            airline: {
     55              name: "A Air",
     56              code: "A",
     57              icon: "chrome://browser/skin/urlbar/market-up.svg",
     58            },
     59            origin: {
     60              city: "Origin 1",
     61              code: "O1",
     62            },
     63            destination: {
     64              city: "Destination 1",
     65              code: "D1",
     66            },
     67            departure: {
     68              scheduled_time: "2025-09-01T14:01:00Z",
     69            },
     70            arrival: {
     71              scheduled_time: "2025-09-01T18:31:00Z",
     72            },
     73            status: "Scheduled",
     74            url: "https://example.com/A1",
     75          },
     76          {
     77            flight_number: "A2",
     78            airline: {
     79              name: null,
     80              code: null,
     81              icon: null,
     82            },
     83            origin: {
     84              city: "Origin 2",
     85              code: "O2",
     86            },
     87            destination: {
     88              city: "Destination 2",
     89              code: "D2",
     90            },
     91            departure: {
     92              scheduled_time: "2025-09-02T14:02:00+02:00",
     93            },
     94            arrival: {
     95              scheduled_time: "2025-09-02T18:32:00-02:00",
     96            },
     97            status: "En Route",
     98            progress_percent: 0,
     99            time_left_minutes: 1,
    100            url: "https://example.com/A2",
    101          },
    102          {
    103            flight_number: "A3",
    104            airline: {
    105              name: null,
    106              code: null,
    107              icon: null,
    108            },
    109            origin: {
    110              city: "Origin 3",
    111              code: "O3",
    112            },
    113            destination: {
    114              city: "Destination 3",
    115              code: "D3",
    116            },
    117            departure: {
    118              scheduled_time: "2025-09-03T14:03:00+0300",
    119            },
    120            arrival: {
    121              scheduled_time: "2025-09-03T18:33:00-0300",
    122            },
    123            status: "Arrived",
    124            time_left_minutes: 0,
    125            url: "https://example.com/A3",
    126          },
    127          {
    128            flight_number: "A4",
    129            airline: {
    130              name: null,
    131              code: null,
    132              icon: null,
    133            },
    134            origin: {
    135              city: "Origin 4",
    136              code: "O4",
    137            },
    138            destination: {
    139              city: "Destination 4",
    140              code: "D4",
    141            },
    142            departure: {
    143              scheduled_time: "2025-09-04T14:04:00+10:30",
    144            },
    145            arrival: {
    146              scheduled_time: "2025-09-04T18:34:00-10:30",
    147            },
    148            status: "Cancelled",
    149            url: "https://example.com/A4",
    150          },
    151        ],
    152      },
    153    },
    154  },
    155 ];
    156 
    157 add_setup(async function () {
    158  await SearchTestUtils.installSearchExtension({}, { setAsDefault: true });
    159  registerCleanupFunction(async () => {
    160    await PlacesUtils.history.clear();
    161  });
    162 
    163  await QuickSuggestTestUtils.ensureQuickSuggestInit({
    164    merinoSuggestions: TEST_MERINO_SINGLE,
    165    prefs: [
    166      ["flightStatus.featureGate", true],
    167      ["suggest.flightStatus", true],
    168      ["suggest.quicksuggest.all", true],
    169    ],
    170  });
    171 });
    172 
    173 add_task(async function ui_basic() {
    174  MerinoTestUtils.server.response.body.suggestions = TEST_MERINO_MULTI;
    175 
    176  const expectedList = [
    177    {
    178      flight_number: "A1, A Air",
    179      image: "chrome://browser/skin/urlbar/market-up.svg",
    180      departure_time: "2:01 PM",
    181      departure_date: "Mon, Sep 1",
    182      arrival_time: "6:31 PM",
    183      origin_airport: "Origin 1 (O1)",
    184      destination_airport: "Destination 1 (D1)",
    185      status: "On time",
    186      url: "https://example.com/A1",
    187    },
    188    {
    189      flight_number: "A2",
    190      image: "",
    191      departure_time: "2:02 PM",
    192      departure_date: "Tue, Sep 2",
    193      arrival_time: "6:32 PM",
    194      origin_airport: "Origin 2 (O2)",
    195      destination_airport: "Destination 2 (D2)",
    196      status: "In flight",
    197      time_left: "1 min left",
    198      url: "https://example.com/A2",
    199    },
    200    {
    201      flight_number: "A3",
    202      image: "chrome://browser/skin/urlbar/flight-airline.svg",
    203      departure_time: "2:03 PM",
    204      departure_date: "Wed, Sep 3",
    205      arrival_time: "6:33 PM",
    206      origin_airport: "Origin 3 (O3)",
    207      destination_airport: "Destination 3 (D3)",
    208      status: "Arrived",
    209      url: "https://example.com/A3",
    210    },
    211    {
    212      flight_number: "A4",
    213      image: "chrome://browser/skin/urlbar/flight-airline.svg",
    214      departure_time: "2:04 PM",
    215      departure_date: "Thu, Sep 4",
    216      arrival_time: "6:34 PM",
    217      origin_airport: "Origin 4 (O4)",
    218      destination_airport: "Destination 4 (D4)",
    219      status: "Cancelled",
    220      url: "https://example.com/A4",
    221    },
    222  ];
    223 
    224  await UrlbarTestUtils.promiseAutocompleteResultPopup({
    225    window,
    226    value: "only match the Merino suggestion",
    227  });
    228  let { element } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
    229 
    230  await assertUI({ row: element.row, expectedList });
    231 
    232  await UrlbarTestUtils.promisePopupClose(window);
    233  gURLBar.handleRevert();
    234 });
    235 
    236 add_task(async function ui_delayed() {
    237  MerinoTestUtils.server.response.body.suggestions = [
    238    {
    239      provider: "flightaware",
    240      is_sponsored: false,
    241      score: 0,
    242      title: "Flight Suggestion",
    243      custom_details: {
    244        flightaware: {
    245          values: [
    246            {
    247              flight_number: "D1",
    248              airline: {
    249                name: "Delay Air",
    250                code: "D",
    251                icon: "chrome://browser/skin/urlbar/market-unchanged.svg",
    252              },
    253              origin: {
    254                city: "Origin D1",
    255                code: "OD1",
    256              },
    257              destination: {
    258                city: "Destination D1",
    259                code: "DD1",
    260              },
    261              departure: {
    262                scheduled_time: "2025-09-05T14:05:00+1130",
    263                estimated_time: "2025-09-05T15:05:00-1130",
    264              },
    265              arrival: {
    266                scheduled_time: "2025-09-05T18:35:00Z",
    267                estimated_time: "2025-09-05T19:35:00Z",
    268              },
    269              status: "Delayed",
    270              delayed: true,
    271              url: "https://example.com/D1",
    272            },
    273            {
    274              flight_number: "D2",
    275              airline: {
    276                name: null,
    277                code: null,
    278                icon: null,
    279              },
    280              origin: {
    281                city: "Origin D2",
    282                code: "OD2",
    283              },
    284              destination: {
    285                city: "Destination D2",
    286                code: "DD2",
    287              },
    288              departure: {
    289                scheduled_time: "2025-09-06T14:06:00Z",
    290                estimated_time: "2025-09-06T15:06:00Z",
    291              },
    292              arrival: {
    293                scheduled_time: "2025-09-06T18:36:00Z",
    294                estimated_time: "2025-09-06T19:36:00Z",
    295              },
    296              status: "En Route",
    297              progress_percent: 18,
    298              time_left_minutes: 583,
    299              delayed: true,
    300              url: "https://example.com/D2",
    301            },
    302            {
    303              flight_number: "D3",
    304              airline: {
    305                name: null,
    306                code: null,
    307                icon: null,
    308              },
    309              origin: {
    310                city: "Origin D3",
    311                code: "OD3",
    312              },
    313              destination: {
    314                city: "Destination D3",
    315                code: "DD3",
    316              },
    317              departure: {
    318                scheduled_time: "2025-09-07T14:07:00Z",
    319                estimated_time: "2025-09-07T15:07:00Z",
    320              },
    321              arrival: {
    322                scheduled_time: "2025-09-07T18:37:00Z",
    323                estimated_time: "2025-09-07T19:37:00Z",
    324              },
    325              status: "Arrived",
    326              delayed: true,
    327              time_left_minutes: 0,
    328              url: "https://example.com/D3",
    329            },
    330          ],
    331        },
    332      },
    333    },
    334  ];
    335 
    336  const expectedList = [
    337    {
    338      flight_number: "D1, Delay Air",
    339      image: "chrome://browser/skin/urlbar/market-unchanged.svg",
    340      departure_time: "2:05 PM",
    341      departure_date: "Fri, Sep 5",
    342      arrival_time: "6:35 PM",
    343      origin_airport: "Origin D1 (OD1)",
    344      destination_airport: "Destination D1 (DD1)",
    345      status: "Delayed until 3:05 PM",
    346      url: "https://example.com/D1",
    347    },
    348    {
    349      flight_number: "D2",
    350      image: "",
    351      departure_time: "3:06 PM",
    352      departure_date: "Sat, Sep 6",
    353      arrival_time: "7:36 PM",
    354      origin_airport: "Origin D2 (OD2)",
    355      destination_airport: "Destination D2 (DD2)",
    356      status: "In flight",
    357      time_left: "9 hr, 43 min left",
    358      url: "https://example.com/D2",
    359    },
    360    {
    361      flight_number: "D3",
    362      image: "chrome://browser/skin/urlbar/flight-airline.svg",
    363      departure_time: "3:07 PM",
    364      departure_date: "Sun, Sep 7",
    365      arrival_time: "7:37 PM",
    366      origin_airport: "Origin D3 (OD3)",
    367      destination_airport: "Destination D3 (DD3)",
    368      status: "Arrived",
    369      url: "https://example.com/D3",
    370    },
    371  ];
    372 
    373  await UrlbarTestUtils.promiseAutocompleteResultPopup({
    374    window,
    375    value: "only match the Merino suggestion",
    376  });
    377  let { element } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
    378 
    379  await assertUI({ row: element.row, expectedList });
    380 
    381  await UrlbarTestUtils.promisePopupClose(window);
    382  gURLBar.handleRevert();
    383 });
    384 
    385 add_task(async function ui_inflight() {
    386  MerinoTestUtils.server.response.body.suggestions = [
    387    {
    388      provider: "flightaware",
    389      is_sponsored: false,
    390      score: 0,
    391      title: "Flight Suggestion",
    392      custom_details: {
    393        flightaware: {
    394          values: [
    395            {
    396              flight_number: "I1",
    397              airline: {
    398                name: "In flight Air",
    399                code: "I",
    400                icon: "chrome://browser/skin/urlbar/flight-airline.svg",
    401                color: "#f00",
    402              },
    403              origin: {
    404                city: "Origin I1",
    405                code: "OI1",
    406              },
    407              destination: {
    408                city: "Destination I1",
    409                code: "DI1",
    410              },
    411              departure: {
    412                scheduled_time: "2025-09-05T14:06:00+1130",
    413                estimated_time: "2025-09-05T15:06:00-1130",
    414              },
    415              arrival: {
    416                scheduled_time: "2025-09-05T18:36:00Z",
    417                estimated_time: "2025-09-05T19:36:00Z",
    418              },
    419              status: "En Route",
    420              progress_percent: 0,
    421              url: "https://example.com/I1",
    422            },
    423            {
    424              flight_number: "I2",
    425              airline: {
    426                name: "In flight Up Air",
    427                code: "IUP",
    428                icon: "chrome://browser/skin/urlbar/market-up.svg",
    429                color: null,
    430              },
    431              origin: {
    432                city: "Origin I2",
    433                code: "OI2",
    434              },
    435              destination: {
    436                city: "Destination I2",
    437                code: "DI2",
    438              },
    439              departure: {
    440                scheduled_time: "2025-09-05T14:07:00+1130",
    441                estimated_time: "2025-09-05T15:07:00-1130",
    442              },
    443              arrival: {
    444                scheduled_time: "2025-09-05T18:37:00Z",
    445                estimated_time: "2025-09-05T19:37:00Z",
    446              },
    447              status: "En Route",
    448              progress_percent: 19,
    449              url: "https://example.com/I2",
    450            },
    451            {
    452              flight_number: "I3",
    453              airline: {
    454                name: "In flight Unchanged Air",
    455                code: "IUN",
    456                icon: "chrome://browser/skin/urlbar/market-unchanged.svg",
    457                color: "#0f0",
    458              },
    459              origin: {
    460                city: "Origin I3",
    461                code: "OI3",
    462              },
    463              destination: {
    464                city: "Destination I3",
    465                code: "DI3",
    466              },
    467              departure: {
    468                scheduled_time: "2025-09-05T14:08:00+1130",
    469                estimated_time: "2025-09-05T15:08:00-1130",
    470              },
    471              arrival: {
    472                scheduled_time: "2025-09-05T18:38:00Z",
    473                estimated_time: "2025-09-05T19:38:00Z",
    474              },
    475              status: "En Route",
    476              progress_percent: 20,
    477              url: "https://example.com/I3",
    478            },
    479            {
    480              flight_number: "I4",
    481              airline: {
    482                name: "In flight No Color Air",
    483                code: "INC",
    484                icon: "chrome://browser/skin/urlbar/flight-airline.svg",
    485                color: null,
    486              },
    487              origin: {
    488                city: "Origin I4",
    489                code: "OI4",
    490              },
    491              destination: {
    492                city: "Destination I4",
    493                code: "DI4",
    494              },
    495              departure: {
    496                scheduled_time: "2025-09-05T14:09:00+1130",
    497                estimated_time: "2025-09-05T15:09:00-1130",
    498              },
    499              arrival: {
    500                scheduled_time: "2025-09-05T18:39:00Z",
    501                estimated_time: "2025-09-05T19:39:00Z",
    502              },
    503              status: "En Route",
    504              progress_percent: 39,
    505              url: "https://example.com/I4",
    506            },
    507            {
    508              flight_number: "I5",
    509              airline: {
    510                name: null,
    511                code: null,
    512                icon: null,
    513                color: "#00f",
    514              },
    515              origin: {
    516                city: "Origin I5",
    517                code: "OI5",
    518              },
    519              destination: {
    520                city: "Destination I5",
    521                code: "DI5",
    522              },
    523              departure: {
    524                scheduled_time: "2025-09-05T14:10:00+1130",
    525                estimated_time: "2025-09-05T15:10:00-1130",
    526              },
    527              arrival: {
    528                scheduled_time: "2025-09-05T18:40:00Z",
    529                estimated_time: "2025-09-05T19:40:00Z",
    530              },
    531              status: "En Route",
    532              progress_percent: 40,
    533              url: "https://example.com/I5",
    534            },
    535            {
    536              flight_number: "I6",
    537              airline: {
    538                name: null,
    539                code: null,
    540                icon: null,
    541                color: null,
    542              },
    543              origin: {
    544                city: "Origin I6",
    545                code: "OI6",
    546              },
    547              destination: {
    548                city: "Destination I6",
    549                code: "DI6",
    550              },
    551              departure: {
    552                scheduled_time: "2025-09-05T14:11:00+1130",
    553                estimated_time: "2025-09-05T15:11:00-1130",
    554              },
    555              arrival: {
    556                scheduled_time: "2025-09-05T18:41:00Z",
    557                estimated_time: "2025-09-05T19:41:00Z",
    558              },
    559              status: "En Route",
    560              progress_percent: 59,
    561              url: "https://example.com/I6",
    562            },
    563            {
    564              flight_number: "I7",
    565              airline: {
    566                name: null,
    567                code: null,
    568                icon: null,
    569                color: "#ff0",
    570              },
    571              origin: {
    572                city: "Origin I7",
    573                code: "OI7",
    574              },
    575              destination: {
    576                city: "Destination I7",
    577                code: "DI7",
    578              },
    579              departure: {
    580                scheduled_time: "2025-09-05T14:12:00+1130",
    581                estimated_time: "2025-09-05T15:12:00-1130",
    582              },
    583              arrival: {
    584                scheduled_time: "2025-09-05T18:42:00Z",
    585                estimated_time: "2025-09-05T19:42:00Z",
    586              },
    587              status: "En Route",
    588              progress_percent: 60,
    589              url: "https://example.com/I7",
    590            },
    591            {
    592              flight_number: "I8",
    593              airline: {
    594                name: null,
    595                code: null,
    596                icon: null,
    597                color: null,
    598              },
    599              origin: {
    600                city: "Origin I8",
    601                code: "OI8",
    602              },
    603              destination: {
    604                city: "Destination I8",
    605                code: "DI8",
    606              },
    607              departure: {
    608                scheduled_time: "2025-09-05T14:13:00+1130",
    609                estimated_time: "2025-09-05T15:13:00-1130",
    610              },
    611              arrival: {
    612                scheduled_time: "2025-09-05T18:43:00Z",
    613                estimated_time: "2025-09-05T19:43:00Z",
    614              },
    615              status: "En Route",
    616              progress_percent: 79,
    617              url: "https://example.com/I8",
    618            },
    619            {
    620              flight_number: "I9",
    621              airline: {
    622                name: null,
    623                code: null,
    624                icon: null,
    625                color: "#0ff",
    626              },
    627              origin: {
    628                city: "Origin I9",
    629                code: "OI9",
    630              },
    631              destination: {
    632                city: "Destination I9",
    633                code: "DI9",
    634              },
    635              departure: {
    636                scheduled_time: "2025-09-05T14:14:00+1130",
    637                estimated_time: "2025-09-05T15:14:00-1130",
    638              },
    639              arrival: {
    640                scheduled_time: "2025-09-05T18:44:00Z",
    641                estimated_time: "2025-09-05T19:44:00Z",
    642              },
    643              status: "En Route",
    644              progress_percent: 80,
    645              url: "https://example.com/I9",
    646            },
    647            {
    648              flight_number: "I10",
    649              airline: {
    650                name: null,
    651                code: null,
    652                icon: null,
    653                color: null,
    654              },
    655              origin: {
    656                city: "Origin I10",
    657                code: "OI10",
    658              },
    659              destination: {
    660                city: "Destination I10",
    661                code: "DI10",
    662              },
    663              departure: {
    664                scheduled_time: "2025-09-05T14:15:00+1130",
    665                estimated_time: "2025-09-05T15:15:00-1130",
    666              },
    667              arrival: {
    668                scheduled_time: "2025-09-05T18:45:00Z",
    669                estimated_time: "2025-09-05T19:45:00Z",
    670              },
    671              status: "En Route",
    672              progress_percent: 100,
    673              url: "https://example.com/I10",
    674            },
    675          ],
    676        },
    677      },
    678    },
    679  ];
    680 
    681  const expectedList = [
    682    {
    683      background: {
    684        image: "chrome://browser/skin/urlbar/flight-inflight-progress-0.svg",
    685        fill: "rgb(255, 0, 0)",
    686        stroke: "rgb(255, 0, 0)",
    687      },
    688      foreground: {
    689        image: "chrome://browser/skin/urlbar/flight-airline.svg",
    690      },
    691    },
    692    {
    693      background: {
    694        image: "chrome://browser/skin/urlbar/flight-inflight-progress-0.svg",
    695      },
    696      foreground: {
    697        image: "chrome://browser/skin/urlbar/market-up.svg",
    698      },
    699    },
    700    {
    701      background: {
    702        image: "chrome://browser/skin/urlbar/flight-inflight-progress-1.svg",
    703        fill: "rgb(0, 255, 0)",
    704        stroke: "rgb(0, 255, 0)",
    705      },
    706      foreground: {
    707        image: "chrome://browser/skin/urlbar/market-unchanged.svg",
    708      },
    709    },
    710    {
    711      background: {
    712        image: "chrome://browser/skin/urlbar/flight-inflight-progress-1.svg",
    713      },
    714      foreground: {
    715        image: "chrome://browser/skin/urlbar/flight-airline.svg",
    716      },
    717    },
    718    {
    719      background: {
    720        image: "chrome://browser/skin/urlbar/flight-inflight-progress-2.svg",
    721        fill: "rgb(0, 0, 255)",
    722        stroke: "rgb(0, 0, 255)",
    723      },
    724      foreground: {
    725        image: "",
    726      },
    727    },
    728    {
    729      background: {
    730        image: "chrome://browser/skin/urlbar/flight-inflight-progress-2.svg",
    731      },
    732      foreground: {
    733        image: "",
    734      },
    735    },
    736    {
    737      background: {
    738        image: "chrome://browser/skin/urlbar/flight-inflight-progress-3.svg",
    739        fill: "rgb(255, 255, 0)",
    740        stroke: "rgb(255, 255, 0)",
    741      },
    742      foreground: {
    743        image: "",
    744      },
    745    },
    746    {
    747      background: {
    748        image: "chrome://browser/skin/urlbar/flight-inflight-progress-3.svg",
    749      },
    750      foreground: {
    751        image: "",
    752      },
    753    },
    754    {
    755      background: {
    756        image: "chrome://browser/skin/urlbar/flight-inflight-progress-4.svg",
    757        fill: "rgb(0, 255, 255)",
    758        stroke: "rgb(0, 255, 255)",
    759      },
    760      foreground: {
    761        image: "",
    762      },
    763    },
    764    {
    765      background: {
    766        image: "chrome://browser/skin/urlbar/flight-inflight-progress-4.svg",
    767      },
    768      foreground: {
    769        image: "",
    770      },
    771    },
    772  ];
    773 
    774  await UrlbarTestUtils.promiseAutocompleteResultPopup({
    775    window,
    776    value: "only match the Merino suggestion",
    777  });
    778  let { element } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
    779 
    780  let items = element.row.querySelectorAll(".urlbarView-realtime-item");
    781  Assert.equal(items.length, expectedList.length);
    782 
    783  for (let i = 0; i < items.length; i++) {
    784    info(`Check the item[${i}]`);
    785    let item = items[i];
    786    let expected = expectedList[i];
    787 
    788    await TestUtils.waitForCondition(
    789      () => item.querySelector(`[name=status_${i}]`).textContent == "In flight"
    790    );
    791    let backgroundStyle = window.getComputedStyle(
    792      item.querySelector(".urlbarView-realtime-image-container")
    793    );
    794    Assert.equal(
    795      backgroundStyle.backgroundImage,
    796      `url("${expected.background.image}")`
    797    );
    798 
    799    // The fill and stroke uses currentColor as the default.
    800    let defaultColor = backgroundStyle.color;
    801    let expectedFillColor = expected.background.fill ?? defaultColor;
    802    let expectedStrokeColor = expected.background.stroke ?? defaultColor;
    803    Assert.equal(backgroundStyle.fill, expectedFillColor);
    804    Assert.equal(backgroundStyle.stroke, expectedStrokeColor);
    805    Assert.equal(
    806      item.querySelector(".urlbarView-realtime-image").src,
    807      expected.foreground.image
    808    );
    809  }
    810 
    811  await UrlbarTestUtils.promisePopupClose(window);
    812  gURLBar.handleRevert();
    813 });
    814 
    815 add_task(async function activate_single() {
    816  MerinoTestUtils.server.response.body.suggestions = TEST_MERINO_SINGLE;
    817 
    818  for (let mouse of [true, false]) {
    819    await UrlbarTestUtils.promiseAutocompleteResultPopup({
    820      window,
    821      value: "only match the Merino suggestion",
    822    });
    823    let { element } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
    824    let { row } = element;
    825 
    826    let target = TEST_MERINO_SINGLE[0].custom_details.flightaware.values[0];
    827    let expectedURL = target.url;
    828    let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, expectedURL);
    829 
    830    if (mouse) {
    831      info("Activate by mouse");
    832      let root = row.querySelector(".urlbarView-realtime-root");
    833      EventUtils.synthesizeMouseAtCenter(root, {});
    834    } else {
    835      info("Activate by key");
    836      EventUtils.synthesizeKey("KEY_Tab");
    837      Assert.equal(
    838        UrlbarTestUtils.getSelectedRow(window),
    839        row,
    840        "The row should be selected"
    841      );
    842      EventUtils.synthesizeKey("KEY_Enter");
    843    }
    844 
    845    let newTab = await newTabOpened;
    846    Assert.ok(true, `Expected URL is loaded [${expectedURL}]`);
    847 
    848    await UrlbarTestUtils.promisePopupClose(window);
    849    BrowserTestUtils.removeTab(newTab);
    850    await PlacesUtils.history.clear();
    851  }
    852 });
    853 
    854 add_task(async function activate_multi() {
    855  MerinoTestUtils.server.response.body.suggestions = TEST_MERINO_MULTI;
    856 
    857  for (let [
    858    index,
    859    value,
    860  ] of TEST_MERINO_MULTI[0].custom_details.flightaware.values.entries()) {
    861    info(`Activate item[${index}] by mouse`);
    862    await UrlbarTestUtils.promiseAutocompleteResultPopup({
    863      window,
    864      value: "only match the Merino suggestion",
    865    });
    866    let { element } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
    867 
    868    let items = element.row.querySelectorAll(".urlbarView-realtime-item");
    869    let item = items[index];
    870 
    871    let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, value.url);
    872    await EventUtils.synthesizeMouseAtCenter(item, {}, item.ownerGlobal);
    873 
    874    let newTab = await newTabOpened;
    875    Assert.ok(true, `Expected URL is loaded [${value.url}]`);
    876    BrowserTestUtils.removeTab(newTab);
    877    await PlacesUtils.history.clear();
    878  }
    879 
    880  for (let [
    881    index,
    882    value,
    883  ] of TEST_MERINO_MULTI[0].custom_details.flightaware.values.entries()) {
    884    info(`Activate item[${index}] by key`);
    885    await UrlbarTestUtils.promiseAutocompleteResultPopup({
    886      window,
    887      value: "only match the Merino suggestion",
    888    });
    889    let { element } = await UrlbarTestUtils.getDetailsOfResultAt(window, 1);
    890 
    891    for (let i = 0; i < index + 1; i++) {
    892      EventUtils.synthesizeKey("KEY_Tab");
    893    }
    894 
    895    let items = element.row.querySelectorAll(".urlbarView-realtime-item");
    896    let item = items[index];
    897 
    898    let newTabOpened = BrowserTestUtils.waitForNewTab(gBrowser, value.url);
    899    await EventUtils.synthesizeMouseAtCenter(item, {}, item.ownerGlobal);
    900    let newTab = await newTabOpened;
    901    Assert.ok(true, `Expected URL is loaded [${value.url}]`);
    902    BrowserTestUtils.removeTab(newTab);
    903    await PlacesUtils.history.clear();
    904  }
    905 
    906  await UrlbarTestUtils.promisePopupClose(window);
    907  gURLBar.handleRevert();
    908 });
    909 
    910 async function assertUI({ row, expectedList }) {
    911  if (expectedList.length > 1) {
    912    Assert.deepEqual(
    913      document.l10n.getAttributes(row._content),
    914      {
    915        id: "urlbar-result-aria-group-flight-status",
    916        args: null,
    917      },
    918      "ARIA group label should be set on the row inner"
    919    );
    920  } else {
    921    Assert.deepEqual(
    922      document.l10n.getAttributes(row._content),
    923      {
    924        id: null,
    925        args: null,
    926      },
    927      "ARIA group label should not be set on the row inner"
    928    );
    929  }
    930 
    931  let items = row.querySelectorAll(".urlbarView-realtime-item");
    932  Assert.equal(items.length, expectedList.length);
    933 
    934  // Select the row, which will select the first item if there are multiple
    935  // items.
    936  UrlbarTestUtils.setSelectedRowIndex(window, 1);
    937 
    938  for (let i = 0; i < items.length; i++) {
    939    info(`Check the item[${i}]`);
    940    let item = items[i];
    941    let expected = expectedList[i];
    942 
    943    await TestUtils.waitForCondition(
    944      () =>
    945        item.querySelector(`[name=status_${i}]`).textContent == expected.status,
    946      "Wait until the status text will be applied by Fluent"
    947    );
    948    Assert.equal(
    949      item.querySelector(".urlbarView-realtime-image").src,
    950      expected.image
    951    );
    952    Assert.equal(
    953      item.querySelector(`[name=departure_time_${i}]`).textContent,
    954      expected.departure_time
    955    );
    956    Assert.equal(
    957      item.querySelector(`[name=departure_date_${i}]`).textContent,
    958      expected.departure_date
    959    );
    960    Assert.equal(
    961      item.querySelector(`[name=arrival_time_${i}]`).textContent,
    962      expected.arrival_time
    963    );
    964    Assert.equal(
    965      item.querySelector(`[name=origin_airport_${i}]`).textContent,
    966      expected.origin_airport
    967    );
    968    Assert.equal(
    969      item.querySelector(`[name=destination_airport_${i}]`).textContent,
    970      expected.destination_airport
    971    );
    972    Assert.equal(
    973      item.querySelector(`[name=flight_number_${i}]`).textContent,
    974      expected.flight_number
    975    );
    976 
    977    let timeLeftMinutes = item.querySelector(`[name=time_left_${i}]`);
    978    if (typeof expected.time_left != "undefined") {
    979      Assert.equal(timeLeftMinutes.textContent, expected.time_left);
    980    } else {
    981      Assert.equal(timeLeftMinutes.textContent, "");
    982      let previousSeparator = timeLeftMinutes.previousElementSibling;
    983      Assert.ok(BrowserTestUtils.isHidden(previousSeparator));
    984    }
    985 
    986    Assert.equal(
    987      gURLBar.value,
    988      expected.url,
    989      "Input value should be the expected URL"
    990    );
    991 
    992    // Select the next item.
    993    EventUtils.synthesizeKey("KEY_Tab");
    994  }
    995 
    996  // Clear the selection.
    997  UrlbarTestUtils.setSelectedRowIndex(window, -1);
    998 }