forked from mirrors/gecko-dev
		
	 91709586ba
			
		
	
	
		91709586ba
		
	
	
	
	
		
			
			Migrated `testing/modules/Sinon.sys.mjs` to an ES module. `testing` should now be 100% ESM 🎉 Differential Revision: https://phabricator.services.mozilla.com/D173643
		
			
				
	
	
		
			334 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			334 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* Any copyright is dedicated to the Public Domain.
 | |
|    http://creativecommons.org/publicdomain/zero/1.0/ */
 | |
| 
 | |
| "use strict";
 | |
| 
 | |
| let contextMenu;
 | |
| 
 | |
| const { sinon } = ChromeUtils.importESModule(
 | |
|   "resource://testing-common/Sinon.sys.mjs"
 | |
| );
 | |
| 
 | |
| const example_base =
 | |
|   // eslint-disable-next-line @microsoft/sdl/no-insecure-url
 | |
|   "http://example.com/browser/browser/base/content/test/contextMenu/";
 | |
| const MAIN_URL = example_base + "subtst_contextmenu_input.html";
 | |
| 
 | |
| add_task(async function test_setup() {
 | |
|   await BrowserTestUtils.openNewForegroundTab(gBrowser, MAIN_URL);
 | |
| 
 | |
|   const chrome_base =
 | |
|     "chrome://mochitests/content/browser/browser/base/content/test/contextMenu/";
 | |
|   const contextmenu_common = chrome_base + "contextmenu_common.js";
 | |
|   /* import-globals-from contextmenu_common.js */
 | |
|   Services.scriptloader.loadSubScript(contextmenu_common, this);
 | |
| });
 | |
| 
 | |
| add_task(async function test_text_input_spellcheck() {
 | |
|   await test_contextmenu(
 | |
|     "#input_spellcheck_no_value",
 | |
|     [
 | |
|       "context-undo",
 | |
|       false,
 | |
|       "context-redo",
 | |
|       false,
 | |
|       "---",
 | |
|       null,
 | |
|       "context-cut",
 | |
|       null, // ignore the enabled/disabled states; there are race conditions
 | |
|       // in the edit commands but they're not relevant for what we're testing.
 | |
|       "context-copy",
 | |
|       null,
 | |
|       "context-paste",
 | |
|       null, // ignore clipboard state
 | |
|       "context-delete",
 | |
|       null,
 | |
|       "context-selectall",
 | |
|       null,
 | |
|       "---",
 | |
|       null,
 | |
|       "spell-check-enabled",
 | |
|       true,
 | |
|       "spell-dictionaries",
 | |
|       true,
 | |
|       [
 | |
|         "spell-check-dictionary-en-US",
 | |
|         true,
 | |
|         "---",
 | |
|         null,
 | |
|         "spell-add-dictionaries",
 | |
|         true,
 | |
|       ],
 | |
|       null,
 | |
|     ],
 | |
|     {
 | |
|       waitForSpellCheck: true,
 | |
|       async preCheckContextMenuFn() {
 | |
|         await SpecialPowers.spawn(
 | |
|           gBrowser.selectedBrowser,
 | |
|           [],
 | |
|           async function() {
 | |
|             let doc = content.document;
 | |
|             let input = doc.getElementById("input_spellcheck_no_value");
 | |
|             input.setAttribute("spellcheck", "true");
 | |
|             input.clientTop; // force layout flush
 | |
|           }
 | |
|         );
 | |
|       },
 | |
|     }
 | |
|   );
 | |
| });
 | |
| 
 | |
| add_task(async function test_text_input_spellcheckwrong() {
 | |
|   await test_contextmenu(
 | |
|     "#input_spellcheck_incorrect",
 | |
|     [
 | |
|       "*prodigality",
 | |
|       true, // spelling suggestion
 | |
|       "spell-add-to-dictionary",
 | |
|       true,
 | |
|       "---",
 | |
|       null,
 | |
|       "context-undo",
 | |
|       null,
 | |
|       "context-redo",
 | |
|       null,
 | |
|       "---",
 | |
|       null,
 | |
|       "context-cut",
 | |
|       null,
 | |
|       "context-copy",
 | |
|       null,
 | |
|       "context-paste",
 | |
|       null, // ignore clipboard state
 | |
|       "context-delete",
 | |
|       null,
 | |
|       "context-selectall",
 | |
|       null,
 | |
|       "---",
 | |
|       null,
 | |
|       "spell-check-enabled",
 | |
|       true,
 | |
|       "spell-dictionaries",
 | |
|       true,
 | |
|       [
 | |
|         "spell-check-dictionary-en-US",
 | |
|         true,
 | |
|         "---",
 | |
|         null,
 | |
|         "spell-add-dictionaries",
 | |
|         true,
 | |
|       ],
 | |
|       null,
 | |
|     ],
 | |
|     { waitForSpellCheck: true }
 | |
|   );
 | |
| });
 | |
| 
 | |
| const kCorrectItems = [
 | |
|   "context-undo",
 | |
|   false,
 | |
|   "context-redo",
 | |
|   false,
 | |
|   "---",
 | |
|   null,
 | |
|   "context-cut",
 | |
|   null,
 | |
|   "context-copy",
 | |
|   null,
 | |
|   "context-paste",
 | |
|   null, // ignore clipboard state
 | |
|   "context-delete",
 | |
|   null,
 | |
|   "context-selectall",
 | |
|   null,
 | |
|   "---",
 | |
|   null,
 | |
|   "spell-check-enabled",
 | |
|   true,
 | |
|   "spell-dictionaries",
 | |
|   true,
 | |
|   [
 | |
|     "spell-check-dictionary-en-US",
 | |
|     true,
 | |
|     "---",
 | |
|     null,
 | |
|     "spell-add-dictionaries",
 | |
|     true,
 | |
|   ],
 | |
|   null,
 | |
| ];
 | |
| 
 | |
| add_task(async function test_text_input_spellcheckcorrect() {
 | |
|   await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
 | |
|     waitForSpellCheck: true,
 | |
|   });
 | |
| });
 | |
| 
 | |
| add_task(async function test_text_input_spellcheck_deadactor() {
 | |
|   await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
 | |
|     waitForSpellCheck: true,
 | |
|     keepMenuOpen: true,
 | |
|   });
 | |
|   let wgp = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
 | |
| 
 | |
|   // Now the menu is open, and spellcheck is running, switch to another tab and
 | |
|   // close the original:
 | |
|   let tab = gBrowser.selectedTab;
 | |
|   await BrowserTestUtils.openNewForegroundTab(gBrowser, "https://example.org");
 | |
|   BrowserTestUtils.removeTab(tab);
 | |
|   // Ensure we've invalidated the actor
 | |
|   await TestUtils.waitForCondition(
 | |
|     () => wgp.isClosed,
 | |
|     "Waiting for actor to be dead after tab closes"
 | |
|   );
 | |
|   contextMenu.hidePopup();
 | |
| 
 | |
|   // Now go back to the input testcase:
 | |
|   BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, MAIN_URL);
 | |
|   await BrowserTestUtils.browserLoaded(
 | |
|     gBrowser.selectedBrowser,
 | |
|     false,
 | |
|     MAIN_URL
 | |
|   );
 | |
| 
 | |
|   // Check the menu still looks the same, keep it open again:
 | |
|   await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
 | |
|     waitForSpellCheck: true,
 | |
|     keepMenuOpen: true,
 | |
|   });
 | |
| 
 | |
|   // Now navigate the tab, after ensuring there's an unload listener, so
 | |
|   // we don't end up in bfcache:
 | |
|   await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function() {
 | |
|     content.document.body.setAttribute("onunload", "");
 | |
|   });
 | |
|   wgp = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
 | |
| 
 | |
|   const NEW_URL = MAIN_URL.replace(".com", ".org");
 | |
|   BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, NEW_URL);
 | |
|   await BrowserTestUtils.browserLoaded(
 | |
|     gBrowser.selectedBrowser,
 | |
|     false,
 | |
|     NEW_URL
 | |
|   );
 | |
|   // Ensure we've invalidated the actor
 | |
|   await TestUtils.waitForCondition(
 | |
|     () => wgp.isClosed,
 | |
|     "Waiting for actor to be dead after onunload"
 | |
|   );
 | |
|   contextMenu.hidePopup();
 | |
| 
 | |
|   // Check the menu *still* looks the same (and keep it open again):
 | |
|   await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
 | |
|     waitForSpellCheck: true,
 | |
|     keepMenuOpen: true,
 | |
|   });
 | |
| 
 | |
|   // Check what happens if the actor stays alive by loading the same page
 | |
|   // again; now the context menu stuff should be destroyed by the menu
 | |
|   // hiding, nothing else.
 | |
|   wgp = gBrowser.selectedBrowser.browsingContext.currentWindowGlobal;
 | |
|   BrowserTestUtils.loadURIString(gBrowser.selectedBrowser, NEW_URL);
 | |
|   await BrowserTestUtils.browserLoaded(
 | |
|     gBrowser.selectedBrowser,
 | |
|     false,
 | |
|     NEW_URL
 | |
|   );
 | |
|   contextMenu.hidePopup();
 | |
| 
 | |
|   // Check the menu still looks the same:
 | |
|   await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
 | |
|     waitForSpellCheck: true,
 | |
|   });
 | |
|   // And test it a last time without any navigation:
 | |
|   await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
 | |
|     waitForSpellCheck: true,
 | |
|   });
 | |
| });
 | |
| 
 | |
| add_task(async function test_text_input_spellcheck_multilingual() {
 | |
|   if (AppConstants.platform == "macosx") {
 | |
|     todo(
 | |
|       false,
 | |
|       "Need macOS support for closemenu attributes in order to " +
 | |
|         "stop the spellcheck menu closing, see bug 1796007."
 | |
|     );
 | |
|     return;
 | |
|   }
 | |
|   let sandbox = sinon.createSandbox();
 | |
|   registerCleanupFunction(() => sandbox.restore());
 | |
| 
 | |
|   // We need to mock InlineSpellCheckerUI.mRemote's properties, but
 | |
|   // InlineSpellCheckerUI.mRemote won't exist until we initialize the context
 | |
|   // menu, so do that and then manually reinit the spellcheck bits so
 | |
|   // we control them:
 | |
|   await test_contextmenu("#input_spellcheck_correct", kCorrectItems, {
 | |
|     waitForSpellCheck: true,
 | |
|     keepMenuOpen: true,
 | |
|   });
 | |
|   sandbox
 | |
|     .stub(InlineSpellCheckerUI.mRemote, "dictionaryList")
 | |
|     .get(() => ["en-US", "nl-NL"]);
 | |
|   let setterSpy = sandbox.spy();
 | |
|   sandbox
 | |
|     .stub(InlineSpellCheckerUI.mRemote, "currentDictionaries")
 | |
|     .get(() => ["en-US"])
 | |
|     .set(setterSpy);
 | |
|   // Re-init the spellcheck items:
 | |
|   InlineSpellCheckerUI.clearDictionaryListFromMenu();
 | |
|   gContextMenu.initSpellingItems();
 | |
| 
 | |
|   let dictionaryMenu = document.getElementById("spell-dictionaries-menu");
 | |
|   let menuOpen = BrowserTestUtils.waitForPopupEvent(dictionaryMenu, "shown");
 | |
|   dictionaryMenu.parentNode.openMenu(true);
 | |
|   await menuOpen;
 | |
|   checkMenu(dictionaryMenu, [
 | |
|     "spell-check-dictionary-nl-NL",
 | |
|     true,
 | |
|     "spell-check-dictionary-en-US",
 | |
|     true,
 | |
|     "---",
 | |
|     null,
 | |
|     "spell-add-dictionaries",
 | |
|     true,
 | |
|   ]);
 | |
|   is(
 | |
|     dictionaryMenu.children.length,
 | |
|     4,
 | |
|     "Should have 2 dictionaries, a separator and 'add more dictionaries' item in the menu."
 | |
|   );
 | |
| 
 | |
|   let dictionaryEventPromise = BrowserTestUtils.waitForEvent(
 | |
|     document,
 | |
|     "spellcheck-changed"
 | |
|   );
 | |
|   dictionaryMenu.activateItem(
 | |
|     dictionaryMenu.querySelector("[data-locale-code*=nl]")
 | |
|   );
 | |
|   let event = await dictionaryEventPromise;
 | |
|   Assert.deepEqual(
 | |
|     event.detail?.dictionaries,
 | |
|     ["en-US", "nl-NL"],
 | |
|     "Should have sent right dictionaries with event."
 | |
|   );
 | |
|   ok(setterSpy.called, "Should have set currentDictionaries");
 | |
|   Assert.deepEqual(
 | |
|     setterSpy.firstCall?.args,
 | |
|     [["en-US", "nl-NL"]],
 | |
|     "Should have called setter with single argument array of 2 dictionaries."
 | |
|   );
 | |
|   // Allow for the menu to potentially close:
 | |
|   await new Promise(r => Services.tm.dispatchToMainThread(r));
 | |
|   // Check it hasn't:
 | |
|   is(
 | |
|     dictionaryMenu.closest("menupopup").state,
 | |
|     "open",
 | |
|     "Main menu should still be open."
 | |
|   );
 | |
|   contextMenu.hidePopup();
 | |
| });
 | |
| 
 | |
| add_task(async function test_cleanup() {
 | |
|   BrowserTestUtils.removeTab(gBrowser.selectedTab);
 | |
| });
 |