From 5b710519cfffef88a023f3a6da2c5a5f4f1cbd86 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 12 Mar 2021 16:35:57 +0900 Subject: [PATCH] test: reliably wait for spellchecker to load (#28118) --- shell/common/api/electron_api_v8_util.cc | 5 ++ spec-main/spellchecker-spec.ts | 74 +++++++++++------------- typings/internal-ambient.d.ts | 1 + 3 files changed, 41 insertions(+), 39 deletions(-) diff --git a/shell/common/api/electron_api_v8_util.cc b/shell/common/api/electron_api_v8_util.cc index 1879d302a5..a0133ec525 100644 --- a/shell/common/api/electron_api_v8_util.cc +++ b/shell/common/api/electron_api_v8_util.cc @@ -138,6 +138,10 @@ void TriggerFatalErrorForTesting(v8::Isolate* isolate) { v8::ExtensionConfiguration config(1, bDeps); v8::Context::New(isolate, &config); } + +void RunUntilIdle() { + base::RunLoop().RunUntilIdle(); +} #endif void Initialize(v8::Local exports, @@ -158,6 +162,7 @@ void Initialize(v8::Local exports, dict.SetMethod("getWeaklyTrackedValues", &GetWeaklyTrackedValues); dict.SetMethod("clearWeaklyTrackedValues", &ClearWeaklyTrackedValues); dict.SetMethod("weaklyTrackValue", &WeaklyTrackValue); + dict.SetMethod("runUntilIdle", &RunUntilIdle); #endif } diff --git a/spec-main/spellchecker-spec.ts b/spec-main/spellchecker-spec.ts index 7a46cb21a8..e4ef5ae81e 100644 --- a/spec-main/spellchecker-spec.ts +++ b/spec-main/spellchecker-spec.ts @@ -7,10 +7,36 @@ import { emittedOnce } from './events-helpers'; import { ifit, ifdescribe, delay } from './spec-helpers'; const features = process._linkedBinding('electron_common_features'); +const v8Util = process._linkedBinding('electron_common_v8_util'); ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { let w: BrowserWindow; + async function rightClick () { + const contextMenuPromise = emittedOnce(w.webContents, 'context-menu'); + w.webContents.sendInputEvent({ + type: 'mouseDown', + button: 'right', + x: 43, + y: 42 + }); + return (await contextMenuPromise)[1] as Electron.ContextMenuParams; + } + + // When the page is just loaded, the spellchecker might not be ready yet. Since + // there is no event to know the state of spellchecker, the only reliable way + // to detect spellchecker is to keep checking with a busy loop. + async function rightClickUntil (fn: (params: Electron.ContextMenuParams) => boolean) { + const now = Date.now(); + const timeout = 10 * 1000; + let contextMenuParams = await rightClick(); + while (!fn(contextMenuParams) && (Date.now() - now < timeout)) { + await delay(100); + contextMenuParams = await rightClick(); + } + return contextMenuParams; + } + beforeEach(async () => { w = new BrowserWindow({ show: false, @@ -28,25 +54,13 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { await closeWindow(w); }); - // Context menu test can not run on Windows, and it is not reliable on ARM - // CI machines. - const shouldRun = process.platform !== 'win32' && - process.arch !== 'arm' && - process.arch !== 'arm64'; + // Context menu test can not run on Windows. + const shouldRun = process.platform !== 'win32'; ifit(shouldRun)('should detect correctly spelled words as correct', async () => { await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautiful and lovely"'); await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()'); - const contextMenuPromise = emittedOnce(w.webContents, 'context-menu'); - // Wait for spellchecker to load - await delay(500); - w.webContents.sendInputEvent({ - type: 'mouseDown', - button: 'right', - x: 43, - y: 42 - }); - const contextMenuParams: Electron.ContextMenuParams = (await contextMenuPromise)[1]; + const contextMenuParams = await rightClickUntil((contextMenuParams) => contextMenuParams.selectionText.length > 0); expect(contextMenuParams.misspelledWord).to.eq(''); expect(contextMenuParams.dictionarySuggestions).to.have.lengthOf(0); }); @@ -54,16 +68,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { ifit(shouldRun)('should detect incorrectly spelled words as incorrect', async () => { await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautifulllll asd asd"'); await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()'); - const contextMenuPromise = emittedOnce(w.webContents, 'context-menu'); - // Wait for spellchecker to load - await delay(500); - w.webContents.sendInputEvent({ - type: 'mouseDown', - button: 'right', - x: 43, - y: 42 - }); - const contextMenuParams: Electron.ContextMenuParams = (await contextMenuPromise)[1]; + const contextMenuParams = await rightClickUntil((contextMenuParams) => contextMenuParams.misspelledWord.length > 0); expect(contextMenuParams.misspelledWord).to.eq('Beautifulllll'); expect(contextMenuParams.dictionarySuggestions).to.have.length.of.at.least(1); }); @@ -74,16 +79,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { w.webContents.session.setSpellCheckerLanguages(['en-US']); await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautifulllll asd asd"'); await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()'); - const contextMenuPromise = emittedOnce(w.webContents, 'context-menu'); - // Wait for spellchecker to load - await delay(500); - w.webContents.sendInputEvent({ - type: 'mouseDown', - button: 'right', - x: 43, - y: 42 - }); - const contextMenuParams: Electron.ContextMenuParams = (await contextMenuPromise)[1]; + const contextMenuParams = await rightClickUntil((contextMenuParams) => contextMenuParams.misspelledWord.length > 0); expect(contextMenuParams.misspelledWord).to.eq('Beautifulllll'); expect(contextMenuParams.dictionarySuggestions).to.have.length.of.at.least(1); }); @@ -91,8 +87,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { ifit(shouldRun)('should expose webFrame spellchecker correctly', async () => { await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautifulllll asd asd"'); await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()'); - // Wait for spellchecker to load - await delay(500); + await rightClickUntil((contextMenuParams) => contextMenuParams.misspelledWord.length > 0); const callWebFrameFn = (expr: string) => w.webContents.executeJavaScript('require("electron").webFrame.' + expr); @@ -110,16 +105,17 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', () => { ifit(shouldRun)('can be dynamically changed', async () => { await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "Beautifulllll asd asd"'); await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()'); - // Wait for spellchecker to load - await delay(500); + await rightClickUntil((contextMenuParams) => contextMenuParams.misspelledWord.length > 0); const callWebFrameFn = (expr: string) => w.webContents.executeJavaScript('require("electron").webFrame.' + expr); w.webContents.session.spellCheckerEnabled = false; + v8Util.runUntilIdle(); expect(w.webContents.session.spellCheckerEnabled).to.be.false(); expect(await callWebFrameFn('isWordMisspelled("testt")')).to.equal(false); w.webContents.session.spellCheckerEnabled = true; + v8Util.runUntilIdle(); expect(w.webContents.session.spellCheckerEnabled).to.be.true(); expect(await callWebFrameFn('isWordMisspelled("testt")')).to.equal(true); }); diff --git a/typings/internal-ambient.d.ts b/typings/internal-ambient.d.ts index e597153e0f..4acb503571 100644 --- a/typings/internal-ambient.d.ts +++ b/typings/internal-ambient.d.ts @@ -43,6 +43,7 @@ declare namespace NodeJS { weaklyTrackValue(value: any): void; clearWeaklyTrackedValues(): void; getWeaklyTrackedValues(): any[]; + runUntilIdle(): void; isSameOrigin(a: string, b: string): boolean; triggerFatalErrorForTesting(): void; }