tor-browser

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

test_fxview_tab_list.html (14435B)


      1 <!DOCTYPE HTML>
      2 <html>
      3 <head>
      4  <meta charset="utf-8">
      5  <title>FxviewTabList Tests</title>
      6  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
      7  <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
      8  <link rel="localization" href="browser/firefoxView.ftl">
      9  <link rel="localization" href="browser/fxviewTabList.ftl">
     10  <link rel="localization" href="browser/places.ftl">
     11  <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
     12  <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
     13  <script type="module" src="chrome://browser/content/firefoxview/fxview-tab-list.mjs"></script>
     14 </head>
     15 <body>
     16 <p id="display"></p>
     17 <div id="content" style="max-width: 750px">
     18  <fxview-tab-list class="history" .dateTimeFormat="relative" .hasPopup="menu">
     19    <panel-list slot="menu">
     20      <panel-item data-l10n-id="fxviewtabrow-delete"></panel-item>
     21      <panel-item data-l10n-id="fxviewtabrow-forget-about-this-site"></panel-item>
     22      <hr />
     23      <panel-item data-l10n-id="fxviewtabrow-open-in-window"></panel-item>
     24      <panel-item data-l10n-id="fxviewtabrow-open-in-private-window"></panel-item>
     25      <hr />
     26      <panel-item data-l10n-id="fxviewtabrow-add-bookmark"></panel-item>
     27      <panel-item data-l10n-id="fxviewtabrow-save-to-pocket"></panel-item>
     28      <panel-item data-l10n-id="fxviewtabrow-copy-link"></panel-item>
     29    </panel-list>
     30  </fxview-tab-list>
     31 </div>
     32 <pre id="test">
     33 <script class="testbody" type="application/javascript">
     34  Services.scriptloader.loadSubScript(
     35    "chrome://browser/content/utilityOverlay.js",
     36    this
     37  );
     38 
     39  const { BrowserTestUtils } = ChromeUtils.importESModule(
     40    "resource://testing-common/BrowserTestUtils.sys.mjs"
     41  );
     42  const { PlacesQuery } = ChromeUtils.importESModule(
     43    "resource://gre/modules/PlacesQuery.sys.mjs"
     44  );
     45  const { PlacesUtils } = ChromeUtils.importESModule(
     46    "resource://gre/modules/PlacesUtils.sys.mjs"
     47  );
     48  const { PlacesUIUtils } = ChromeUtils.importESModule(
     49    "moz-src:///browser/components/places/PlacesUIUtils.sys.mjs"
     50  );
     51  const { PlacesTestUtils } = ChromeUtils.importESModule(
     52    "resource://testing-common/PlacesTestUtils.sys.mjs"
     53  );
     54 
     55  const fxviewTabList = document.querySelector("fxview-tab-list");
     56  let tabItems = [];
     57  const placesQuery = new PlacesQuery();
     58 
     59  const URLs = [
     60    "http://mochi.test:8888/browser/",
     61    "https://www.example.com/",
     62    "https://example.net/",
     63    "https://example.org/",
     64    "https://www.mozilla.org/"
     65  ];
     66 
     67  async function addHistoryItems() {
     68    await PlacesUtils.history.clear();
     69    let history = await placesQuery.getHistory();
     70 
     71    const now = new Date();
     72    await PlacesUtils.history.insert({
     73      url: URLs[0],
     74      title: "Example Domain 1",
     75      visits: [{ date: now }],
     76    });
     77    let historyUpdated = Promise.withResolvers();
     78    placesQuery.observeHistory(newHistory => {
     79      history = newHistory;
     80      historyUpdated.resolve();
     81    });
     82    await PlacesUtils.history.insert({
     83      url: URLs[1],
     84      title: "Example Domain 2",
     85      visits: [{ date: now }],
     86    });
     87    await historyUpdated.promise;
     88    historyUpdated = Promise.withResolvers();
     89    placesQuery.observeHistory(newHistory => {
     90      history = newHistory;
     91      historyUpdated.resolve();
     92    });
     93    await PlacesUtils.history.insert({
     94      url: URLs[2],
     95      title: "Example Domain 3",
     96      visits: [{ date: now }],
     97    });
     98    await historyUpdated.promise;
     99    historyUpdated = Promise.withResolvers();
    100    placesQuery.observeHistory(newHistory => {
    101      history = newHistory;
    102      historyUpdated.resolve();
    103    });
    104    await PlacesUtils.history.insert({
    105      url: URLs[3],
    106      title: "Example Domain 4",
    107      visits: [{ date: now }],
    108    });
    109    await historyUpdated.promise;
    110 
    111    fxviewTabList.tabItems = Array.from(history.values()).flat().map(visit => ({
    112      ...visit,
    113      time: visit.date.getTime(),
    114      title: visit.title || visit.url,
    115      icon: `page-icon:${visit.url}`,
    116      primaryL10nId: "fxviewtabrow-tabs-list-tab",
    117      primaryL10nArgs: JSON.stringify({
    118        targetURI: visit.url,
    119      }),
    120      secondaryL10nId: "fxviewtabrow-options-menu-button",
    121      secondaryL10nArgs: JSON.stringify({
    122        tabTitle: visit.title || visit.url,
    123      }),
    124    }));
    125 
    126    await fxviewTabList.getUpdateComplete();
    127    tabItems = Array.from(fxviewTabList.rowEls);
    128  }
    129 
    130  function getCurrentDisplayDate() {
    131    let lastItemMainEl = tabItems[tabItems.length - 1].mainEl;
    132    return lastItemMainEl.querySelector("#fxview-tab-row-date span:not([hidden])")?.textContent.trim() ?? "";
    133  }
    134 
    135  function getCurrentDisplayTime() {
    136    let lastItemMainEl = tabItems[tabItems.length - 1].mainEl;
    137    return lastItemMainEl.querySelector("#fxview-tab-row-time")?.textContent.trim() ?? "";
    138  }
    139 
    140  function isActiveElement(expectedLinkEl) {
    141    return expectedLinkEl.getRootNode().activeElement == expectedLinkEl;
    142  }
    143 
    144  function onPrimaryAction(e) {
    145    let gBrowser = BrowserWindowTracker.getTopWindow().top.gBrowser;
    146    gBrowser.addTrustedTab(e.originalTarget.url);
    147  }
    148 
    149  function onSecondaryAction(e) {
    150    e.target.querySelector("panel-list").toggle(e.detail.originalEvent);
    151  }
    152 
    153  add_setup(function setup() {
    154    fxviewTabList.addEventListener("fxview-tab-list-primary-action", onPrimaryAction);
    155    fxviewTabList.addEventListener("fxview-tab-list-secondary-action", onSecondaryAction);
    156    fxviewTabList.updatesPaused = false;
    157  });
    158 
    159  /**
    160  * Tests that history items are loaded in the expected order
    161  */
    162  add_task(async function test_list_ordering() {
    163    await addHistoryItems();
    164    is(
    165      tabItems.length,
    166      4,
    167      "Four history items are shown in the list."
    168    );
    169 
    170    // Check ordering
    171    ok(
    172      tabItems[0].title === "Example Domain 4",
    173      "First history item in fxview-tab-list is in the correct order."
    174    )
    175 
    176    ok(
    177      tabItems[3].title === "Example Domain 1",
    178      "Last history item in fxview-tab-list is in the correct order."
    179    )
    180  });
    181 
    182  /**
    183  * Tests the primary action function is triggered when selecting the main row element
    184  */
    185  add_task(async function test_primary_action(){
    186    await addHistoryItems();
    187    let gBrowser = BrowserWindowTracker.getTopWindow().top.gBrowser;
    188    let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, tabItems[0].url);
    189    tabItems[0].mainEl.click();
    190    await newTabPromise;
    191 
    192    is(
    193      tabItems.length,
    194      4,
    195      "Four history items are still shown in the list."
    196    );
    197 
    198    await BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
    199  });
    200 
    201  /**
    202  * Tests that a max tabs length value can be given to fxview-tab-list
    203  */
    204  add_task(async function test_max_list_items() {
    205    const mockMaxTabsLength = 3;
    206 
    207    // override this value for testing purposes
    208    fxviewTabList.maxTabsLength = mockMaxTabsLength;
    209    await addHistoryItems();
    210 
    211    is(
    212      tabItems.length,
    213      mockMaxTabsLength,
    214      `fxview-tabs-list should have ${mockMaxTabsLength} list items`
    215    );
    216 
    217    // Add new history items
    218    let history = await placesQuery.getHistory();
    219 
    220    const now = new Date();
    221    await PlacesUtils.history.insert({
    222      url: URLs[4],
    223      title: "Internet for people, not profits - Mozilla",
    224      visits: [{ date: now }],
    225    });
    226    let historyUpdated = Promise.withResolvers();
    227    placesQuery.observeHistory(newHistory => {
    228      history = newHistory;
    229      historyUpdated.resolve();
    230    });
    231    await historyUpdated.promise;
    232 
    233    ok(
    234      [...history.values()].reduce((acc, {length}) => acc + length, 0) === 5,
    235      "Five total history items after inserting another node"
    236    );
    237 
    238    // Update fxview-tab-list component with latest history data
    239    fxviewTabList.tabItems = [...history.values()].flat();
    240    await fxviewTabList.getUpdateComplete();
    241    tabItems = Array.from(fxviewTabList.rowEls);
    242 
    243    is(
    244      tabItems.length,
    245      mockMaxTabsLength,
    246      `fxview-tabs-list should have ${mockMaxTabsLength} list items`
    247    );
    248 
    249    ok(
    250      tabItems[0].title === "Internet for people, not profits - Mozilla",
    251      "History list has been updated with the expected maxTabsLength."
    252    )
    253    fxviewTabList.maxTabsLength = 25;
    254  });
    255 
    256    /**
    257   * Tests keyboard navigation of the fxview-tab-list component
    258   */
    259  add_task(async function test_keyboard_navigation() {
    260    const arrowDown = async () => {
    261      info("Arrow down");
    262      synthesizeKey("KEY_ArrowDown", {});
    263      await fxviewTabList.getUpdateComplete();
    264    };
    265    const arrowUp = async () => {
    266      info("Arrow up");
    267      synthesizeKey("KEY_ArrowUp", {});
    268      await fxviewTabList.getUpdateComplete();
    269    };
    270    const arrowRight = async () => {
    271      info("Arrow right");
    272      synthesizeKey("KEY_ArrowRight", {});
    273      await fxviewTabList.getUpdateComplete();
    274    };
    275    const arrowLeft = async () => {
    276      info("Arrow left");
    277      synthesizeKey("KEY_ArrowLeft", {});
    278      await fxviewTabList.getUpdateComplete();
    279    };
    280 
    281    await addHistoryItems();
    282    tabItems[0].mainEl.focus();
    283    ok(
    284      isActiveElement(tabItems[0].mainEl),
    285      "Focus should be on the first main element of the list"
    286    );
    287 
    288    // Arrow down/up the list
    289    await arrowDown();
    290    ok(
    291      isActiveElement(tabItems[1].mainEl),
    292      "Focus should be on the second main element of the list"
    293    );
    294    await arrowDown();
    295    ok(
    296      isActiveElement(tabItems[2].mainEl),
    297      "Focus should be on the third main element of the list"
    298    );
    299    await arrowDown();
    300    ok(
    301      isActiveElement(tabItems[3].mainEl),
    302      "Focus should be on the fourth main element of the list"
    303    );
    304    await arrowUp();
    305    ok(
    306      isActiveElement(tabItems[2].mainEl),
    307      "Focus should be on the third main element of the list"
    308    );
    309    await arrowUp();
    310    ok(
    311      isActiveElement(tabItems[1].mainEl),
    312      "Focus should be on the second main element of the list"
    313    );
    314    await arrowUp();
    315    ok(
    316      isActiveElement(tabItems[0].mainEl),
    317      "Focus should be on the first main element of the list"
    318    );
    319    await arrowRight();
    320    ok(
    321      isActiveElement(tabItems[0].secondaryButtonEl),
    322      "Focus should be on the first row's context menu button element of the list"
    323    );
    324    await arrowDown();
    325    ok(
    326      isActiveElement(tabItems[1].secondaryButtonEl),
    327      "Focus should be on the second row's context menu button element of the list"
    328    );
    329    await arrowLeft();
    330    ok(
    331      isActiveElement(tabItems[1].mainEl),
    332      "Focus should be on the second main element of the list"
    333    );
    334    await arrowUp();
    335    ok(
    336      isActiveElement(tabItems[0].mainEl),
    337      "Focus should be on the first main element of the list"
    338    );
    339  });
    340 
    341  /**
    342  * Tests relative time format for the fxview-tab-list component
    343  */
    344  add_task(async function test_relative_format() {
    345    await addHistoryItems();
    346    ok(
    347      getCurrentDisplayDate().includes("Just now"),
    348      "Current dateTime format is 'relative' and date displays 'Just now' initially"
    349    );
    350    ok(
    351      !getCurrentDisplayTime().length,
    352      "Current dateTime format is 'relative' and time displays an empty string"
    353    );
    354  });
    355 
    356  /**
    357  * Tests date only format for the fxview-tab-list component
    358  */
    359  add_task(async function test_date_only_format() {
    360    await addHistoryItems();
    361 
    362    // Check date only format
    363    fxviewTabList.dateTimeFormat = "date";
    364    await fxviewTabList.getUpdateComplete();
    365    await BrowserTestUtils.waitForCondition(() => {
    366      return getCurrentDisplayDate().includes("/");
    367    });
    368    ok(
    369      getCurrentDisplayDate().includes("/"),
    370      "Current dateTime format is 'date' and displays the current date"
    371    );
    372    ok(
    373      !getCurrentDisplayTime().length,
    374      "Current dateTime format is 'date' and time displays an empty string"
    375    );
    376  });
    377 
    378  /**
    379  * Tests time only format for the fxview-tab-list component
    380  */
    381  add_task(async function test_time_only_format() {
    382    await addHistoryItems();
    383 
    384    // Check time only format
    385    fxviewTabList.dateTimeFormat = "time";
    386    await fxviewTabList.getUpdateComplete();
    387    await BrowserTestUtils.waitForCondition(() => {
    388      return getCurrentDisplayTime().includes("AM") || getCurrentDisplayTime().includes("PM");
    389    });
    390    ok(
    391      !getCurrentDisplayDate().length,
    392      "Current dateTime format is 'time' and date displays an empty string"
    393    );
    394    ok(
    395      getCurrentDisplayTime().includes("AM") || getCurrentDisplayTime().includes("PM"),
    396      "Current dateTime format is 'time' and displays the current time"
    397    );
    398  });
    399 
    400  /**
    401  * Tests date and time format for the fxview-tab-list component
    402  */
    403  add_task(async function test_date_and_time_format() {
    404    await addHistoryItems();
    405 
    406    // Check date and time format
    407    fxviewTabList.dateTimeFormat = "dateTime";
    408    await fxviewTabList.getUpdateComplete();
    409    await BrowserTestUtils.waitForCondition(() => {
    410      return getCurrentDisplayDate().includes("/") &&
    411      (getCurrentDisplayTime().includes("AM") || getCurrentDisplayTime().includes("PM"));
    412    });
    413    ok(
    414      getCurrentDisplayDate().includes("/"),
    415      "Current dateTime format is 'dateTime' and date displays the current date"
    416    );
    417    ok(
    418      getCurrentDisplayTime().includes("AM") || getCurrentDisplayTime().includes("PM"),
    419      "Current dateTime format is 'dateTime' and displays the current time"
    420    );
    421 
    422    // Reset dateTimeFormat to relative before next test
    423    fxviewTabList.dateTimeFormat = "relative";
    424    await fxviewTabList.getUpdateComplete();
    425  });
    426 
    427  /**
    428  * Tests that relative time updates properly for the fxview-tab-list component
    429  */
    430  add_task(async function test_relative_time_updates() {
    431    await addHistoryItems();
    432 
    433    await BrowserTestUtils.waitForCondition(() => {
    434      return getCurrentDisplayDate().includes("Just now");
    435    });
    436 
    437    ok(
    438      getCurrentDisplayDate().includes("Just now"),
    439      "Current date element displays 'Just now' initially"
    440    );
    441 
    442    // Set the updateTimeMs pref to something low to check that relative time updates properly
    443    const TAB_UPDATE_TIME_MS = 500;
    444    await SpecialPowers.pushPrefEnv({
    445      set: [["browser.tabs.firefox-view.updateTimeMs", TAB_UPDATE_TIME_MS]],
    446    });
    447    await BrowserTestUtils.waitForCondition(() => {
    448      return !getCurrentDisplayDate().includes("now");
    449    });
    450    info("Currently displayed date is something other than 'Just now'");
    451 
    452    await SpecialPowers.popPrefEnv();
    453  });
    454 </script>
    455 </pre>
    456 </body>
    457 </html>