tor-browser

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

browser_title_case_menus.js (4802B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 /**
      7 * This test file checks that our en-US builds use APA-style Title Case strings
      8 * where appropriate.
      9 */
     10 
     11 // MINOR_WORDS are words that are okay to not be capitalized when they're
     12 // mid-string.
     13 //
     14 // Source: https://apastyle.apa.org/style-grammar-guidelines/capitalization/title-case
     15 const MINOR_WORDS = [
     16  "a",
     17  "an",
     18  "and",
     19  "as",
     20  "at",
     21  "but",
     22  "by",
     23  "for",
     24  "if",
     25  "in",
     26  "nor",
     27  "of",
     28  "off",
     29  "on",
     30  "or",
     31  "per",
     32  "so",
     33  "the",
     34  "to",
     35  "up",
     36  "via",
     37  "yet",
     38 ];
     39 
     40 /**
     41 * Returns a generator that will yield all of the <xul:menupopups>
     42 * beneath <xul:menu> elements within a given <xul:menubar>. Each
     43 * <xul:menupopup> will have the "popupshowing" and "popupshown"
     44 * event fired on them to give them an opportunity to fully populate
     45 * themselves before being yielded.
     46 *
     47 * @generator
     48 * @param {<xul:menubar>} menubar The <xul:menubar> to get <xul:menupopup>s
     49 *        for.
     50 * @yields {<xul:menupopup>} The next <xul:menupopup> under the <xul:menubar>.
     51 */
     52 async function* iterateMenuPopups(menubar) {
     53  let menus = menubar.querySelectorAll("menu");
     54 
     55  for (let menu of menus) {
     56    for (let menupopup of menu.querySelectorAll("menupopup")) {
     57      // We fake the popupshowing and popupshown events to give the menupopups
     58      // an opportunity to fully populate themselves. We don't actually open
     59      // the menupopups because this is not possible on macOS.
     60      menupopup.dispatchEvent(
     61        new MouseEvent("popupshowing", { bubbles: true })
     62      );
     63      menupopup.dispatchEvent(new MouseEvent("popupshown", { bubbles: true }));
     64 
     65      yield menupopup;
     66 
     67      // Just for good measure, we'll fire the popuphiding/popuphidden events
     68      // after we close the menupopups.
     69      menupopup.dispatchEvent(new MouseEvent("popuphiding", { bubbles: true }));
     70      menupopup.dispatchEvent(new MouseEvent("popuphidden", { bubbles: true }));
     71    }
     72  }
     73 }
     74 
     75 /**
     76 * Given a <xul:menupopup>, checks all of the child elements with label
     77 * properties to see if those labels are Title Cased. Skips any elements that
     78 * have an empty or undefined label property.
     79 *
     80 * @param {<xul:menupopup>} menupopup The <xul:menupopup> to check.
     81 */
     82 function checkMenuItems(menupopup) {
     83  info("Checking menupopup with id " + menupopup.id);
     84  for (let child of menupopup.children) {
     85    if (child.label) {
     86      info("Checking menupopup child with id " + child.id);
     87      checkTitleCase(child.label, child.id);
     88    }
     89  }
     90 }
     91 
     92 /**
     93 * Given a string, checks that the string is in Title Case.
     94 *
     95 * @param {string} string The string to check.
     96 * @param {string} elementID The ID of the element associated with the string.
     97 *        This is included in the assertion message.
     98 */
     99 function checkTitleCase(string, elementID) {
    100  if (!string || !elementID /* document this */) {
    101    return;
    102  }
    103 
    104  let words = string.trim().split(/\s+/);
    105 
    106  // We extract the first word, and always expect it to be capitalized,
    107  // even if it's a short word like one of MINOR_WORDS.
    108  let firstWord = words.shift();
    109  let result = hasExpectedCapitalization(firstWord, true);
    110  if (result) {
    111    for (let word of words) {
    112      if (word) {
    113        let expectCapitalized = !MINOR_WORDS.includes(word);
    114        result = hasExpectedCapitalization(word, expectCapitalized);
    115        if (!result) {
    116          break;
    117        }
    118      }
    119    }
    120  }
    121 
    122  Assert.ok(result, `${string} for ${elementID} should have Title Casing.`);
    123 }
    124 
    125 /**
    126 * On Windows, macOS and GTK/KDE Linux, menubars are expected to be in Title
    127 * Case in order to feel native. This test iterates the menuitem labels of the
    128 * main menubar to ensure the en-US strings are all in Title Case.
    129 *
    130 * We use APA-style Title Case for the menubar, rather than Photon-style Title
    131 * Case (https://design.firefox.com/photon/copy/capitalization.html) to match
    132 * the native platform conventions.
    133 */
    134 add_task(async function apa_test_title_case_menubar() {
    135  let newWin = await BrowserTestUtils.openNewBrowserWindow();
    136  let menuToolbar = newWin.document.getElementById("main-menubar");
    137 
    138  for await (const menupopup of iterateMenuPopups(menuToolbar)) {
    139    checkMenuItems(menupopup);
    140  }
    141 
    142  await BrowserTestUtils.closeWindow(newWin);
    143 });
    144 
    145 /**
    146 * This test iterates the menuitem labels of the macOS dock menu for the
    147 * application to ensure the en-US strings are all in Title Case.
    148 */
    149 add_task(async function apa_test_title_case_macos_dock_menu() {
    150  if (AppConstants.platform != "macosx") {
    151    return;
    152  }
    153 
    154  let hiddenWindow = Services.appShell.hiddenDOMWindow;
    155  Assert.ok(hiddenWindow, "Could get at hidden window");
    156  let menupopup = hiddenWindow.document.getElementById("menu_mac_dockmenu");
    157  checkMenuItems(menupopup);
    158 });