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 });