chore: make input actions not use rerunnable task (#19638)
This commit is contained in:
parent
d5881b8d48
commit
cce29215f9
|
|
@ -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<InjectedScript>, 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<R>(
|
||||
progress: Progress,
|
||||
selector: string,
|
||||
strict: boolean | undefined,
|
||||
action: (handle: dom.ElementHandle<Element>) => Promise<R | 'error:notconnected'>): Promise<R> {
|
||||
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<Element>;
|
||||
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<Element>;
|
||||
result.dispose();
|
||||
try {
|
||||
const result = await action(element);
|
||||
if (result === 'error:notconnected') {
|
||||
|
|
|
|||
|
|
@ -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`);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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(`
|
||||
<button style="display: none; position: absolute; top: 0px; left: 0px; width: 100%;">
|
||||
Reset
|
||||
</button>
|
||||
<button>
|
||||
Reset
|
||||
</button>
|
||||
<button style="display: none; position: absolute; top: 0px; left: 0px; width: 100%;">Reset</button>
|
||||
<button>Reset</button>
|
||||
`);
|
||||
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: <button>Reset</button>');
|
||||
});
|
||||
|
||||
it('should resolve promise when node is added in shadow dom', async ({ page, server }) => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue