From cce29215f927beb182f92516eda787c0cd61b8b8 Mon Sep 17 00:00:00 2001 From: Dmitry Gozman Date: Sat, 24 Dec 2022 10:40:36 -0800 Subject: [PATCH] chore: make input actions not use rerunnable task (#19638) --- packages/playwright-core/src/server/frames.ts | 48 +++++++++++++++---- tests/page/page-fill.spec.ts | 2 +- tests/page/page-wait-for-selector-1.spec.ts | 10 ++-- 3 files changed, 44 insertions(+), 16 deletions(-) diff --git a/packages/playwright-core/src/server/frames.ts b/packages/playwright-core/src/server/frames.ts index 2e41a5384bb06..4c82ec56bad21 100644 --- a/packages/playwright-core/src/server/frames.ts +++ b/packages/playwright-core/src/server/frames.ts @@ -1134,19 +1134,51 @@ export class Frame extends SdkObject { return false; } + private async _resolveInjectedForSelector(progress: Progress, selector: string, strict: boolean | undefined): Promise<{ injected: js.JSHandle, info: SelectorInfo } | undefined> { + const selectorInFrame = await this.resolveFrameForSelectorNoWait(selector, { strict }); + if (!selectorInFrame) + return; + progress.throwIfAborted(); + + // Be careful, |this| can be different from |selectorInFrame.frame|. + const context = await selectorInFrame.frame._context(selectorInFrame.info.world); + const injected = await context.injectedScript(); + progress.throwIfAborted(); + return { injected, info: selectorInFrame.info }; + } + private async _retryWithProgressIfNotConnected( progress: Progress, selector: string, strict: boolean | undefined, action: (handle: dom.ElementHandle) => Promise): Promise { - return this.retryWithProgress(progress, selector, { strict }, async (selectorInFrame, continuePolling) => { - // We did not pass omitAttached, so selectorInFrame is not null. - const { frame, info } = selectorInFrame!; - // Be careful, |this| can be different from |frame|. - const task = dom.waitForSelectorTask(info, 'attached'); - progress.log(`waiting for ${this._asLocator(selector)}`); - const handle = await frame._scheduleRerunnableHandleTask(progress, info.world, task); - const element = handle.asElement() as dom.ElementHandle; + progress.log(`waiting for ${this._asLocator(selector)}`); + return this.retryWithProgressAndTimeouts(progress, [0, 20, 50, 100, 100, 500], async continuePolling => { + const resolved = await this._resolveInjectedForSelector(progress, selector, strict); + if (!resolved) + return continuePolling; + const result = await resolved.injected.evaluateHandle((injected, { info }) => { + const elements = injected.querySelectorAll(info.parsed, document); + const element = elements[0] as Element | undefined; + let log = ''; + if (elements.length > 1) { + if (info.strict) + throw injected.strictModeViolationError(info.parsed, elements); + log = ` locator resolved to ${elements.length} elements. Proceeding with the first one: ${injected.previewNode(elements[0])}`; + } else if (element) { + log = ` locator resolved to ${injected.previewNode(element)}`; + } + return { log, success: !!element, element }; + }, { info: resolved.info }); + const { log, success } = await result.evaluate(r => ({ log: r.log, success: r.success })); + if (log) + progress.log(log); + if (!success) { + result.dispose(); + return continuePolling; + } + const element = await result.evaluateHandle(r => r.element) as dom.ElementHandle; + result.dispose(); try { const result = await action(element); if (result === 'error:notconnected') { diff --git a/tests/page/page-fill.spec.ts b/tests/page/page-fill.spec.ts index 3dc8ae74c2cba..17161937d3fc2 100644 --- a/tests/page/page-fill.spec.ts +++ b/tests/page/page-fill.spec.ts @@ -40,7 +40,7 @@ it('should throw on unsupported inputs', async ({ page, server }) => { await page.$eval('input', (input, type) => input.setAttribute('type', type), type); let error = null; await page.fill('input', '').catch(e => error = e); - expect(error.message).toContain(`input of type "${type}" cannot be filled`); + expect(error.message).toContain(`Input of type "${type}" cannot be filled`); } }); diff --git a/tests/page/page-wait-for-selector-1.spec.ts b/tests/page/page-wait-for-selector-1.spec.ts index 59f2d963c9e8b..906fbd2c35200 100644 --- a/tests/page/page-wait-for-selector-1.spec.ts +++ b/tests/page/page-wait-for-selector-1.spec.ts @@ -172,17 +172,13 @@ it('should report logs while waiting for hidden', async ({ page, server }) => { it('should report logs when the selector resolves to multiple elements', async ({ page, server }) => { await page.goto(server.EMPTY_PAGE); await page.setContent(` - - + + `); const error = await page.click('text=Reset', { timeout: 1000 }).catch(e => e); - expect(error.toString()).toContain('locator resolved to 2 elements. Proceeding with the first one.'); + expect(error.toString()).toContain('locator resolved to 2 elements. Proceeding with the first one: '); }); it('should resolve promise when node is added in shadow dom', async ({ page, server }) => {