fix(toBeHidden): return true to missing elements (#9205)
This commit is contained in:
parent
55ddc553a5
commit
f78302e8dd
|
|
@ -70,7 +70,7 @@ export type NavigationEvent = {
|
|||
};
|
||||
|
||||
export type SchedulableTask<T> = (injectedScript: js.JSHandle<InjectedScript>) => Promise<js.JSHandle<InjectedScriptPoll<T>>>;
|
||||
export type DomTaskBody<T, R> = (progress: InjectedScriptProgress, element: Element, data: T, elements: Element[], continuePolling: any) => R;
|
||||
export type DomTaskBody<T, R, E> = (progress: InjectedScriptProgress, element: E, data: T, elements: Element[], continuePolling: any) => R;
|
||||
|
||||
export class FrameManager {
|
||||
private _page: Page;
|
||||
|
|
@ -1165,9 +1165,18 @@ export class Frame extends SdkObject {
|
|||
const controller = new ProgressController(metadata, this);
|
||||
const querySelectorAll = options.expression === 'to.have.count' || options.expression.endsWith('.array');
|
||||
const mainWorld = options.expression === 'to.have.property';
|
||||
const omitAttached = (!options.isNot && options.expression === 'to.be.hidden') || (options.isNot && options.expression === 'to.be.visible');
|
||||
|
||||
return await this._scheduleRerunnableTaskWithController(controller, selector, (progress, element, options, elements, continuePolling) => {
|
||||
return progress.injectedScript.expect(progress, element, options, elements, continuePolling);
|
||||
}, options, { strict: true, querySelectorAll, mainWorld, logScale: true, ...options }).catch(e => {
|
||||
// We don't have an element and we don't need an element => pass.
|
||||
if (!element && options.omitAttached)
|
||||
return { pass: !options.isNot };
|
||||
// We don't have an element and we DO need an element => fail.
|
||||
if (!element)
|
||||
return { pass: !!options.isNot };
|
||||
// We have an element.
|
||||
return progress.injectedScript.expect(progress, element!, options, elements, continuePolling);
|
||||
}, { omitAttached, ...options }, { strict: true, querySelectorAll, mainWorld, omitAttached, logScale: true, ...options }).catch(e => {
|
||||
if (js.isJavaScriptErrorInEvaluate(e))
|
||||
throw e;
|
||||
return { received: controller.lastIntermediateResult(), pass: !!options.isNot, log: metadata.log };
|
||||
|
|
@ -1231,17 +1240,17 @@ export class Frame extends SdkObject {
|
|||
this._parentFrame = null;
|
||||
}
|
||||
|
||||
private async _scheduleRerunnableTask<T, R>(metadata: CallMetadata, selector: string, body: DomTaskBody<T, R>, taskData: T, options: types.TimeoutOptions & types.StrictOptions & { mainWorld?: boolean } = {}): Promise<R> {
|
||||
private async _scheduleRerunnableTask<T, R>(metadata: CallMetadata, selector: string, body: DomTaskBody<T, R, Element>, taskData: T, options: types.TimeoutOptions & types.StrictOptions & { mainWorld?: boolean } = {}): Promise<R> {
|
||||
const controller = new ProgressController(metadata, this);
|
||||
return this._scheduleRerunnableTaskWithController(controller, selector, body, taskData, options);
|
||||
return this._scheduleRerunnableTaskWithController(controller, selector, body as DomTaskBody<T, R, Element | undefined>, taskData, options);
|
||||
}
|
||||
|
||||
private async _scheduleRerunnableTaskWithController<T, R>(
|
||||
controller: ProgressController,
|
||||
selector: string,
|
||||
body: DomTaskBody<T, R>,
|
||||
body: DomTaskBody<T, R, Element | undefined>,
|
||||
taskData: T,
|
||||
options: types.TimeoutOptions & types.StrictOptions & { mainWorld?: boolean, querySelectorAll?: boolean, logScale?: boolean } = {}): Promise<R> {
|
||||
options: types.TimeoutOptions & types.StrictOptions & { mainWorld?: boolean, querySelectorAll?: boolean, logScale?: boolean, omitAttached?: boolean } = {}): Promise<R> {
|
||||
|
||||
const info = this._page.parseSelector(selector, options);
|
||||
const callbackText = body.toString();
|
||||
|
|
@ -1250,8 +1259,8 @@ export class Frame extends SdkObject {
|
|||
return controller.run(async progress => {
|
||||
progress.log(`waiting for selector "${selector}"`);
|
||||
const rerunnableTask = new RerunnableTask(data, progress, injectedScript => {
|
||||
return injectedScript.evaluateHandle((injected, { info, taskData, callbackText, querySelectorAll, logScale }) => {
|
||||
const callback = injected.eval(callbackText) as DomTaskBody<T, R>;
|
||||
return injectedScript.evaluateHandle((injected, { info, taskData, callbackText, querySelectorAll, logScale, omitAttached }) => {
|
||||
const callback = injected.eval(callbackText) as DomTaskBody<T, R, Element | undefined>;
|
||||
const poller = logScale ? injected.pollLogScale.bind(injected) : injected.pollRaf.bind(injected);
|
||||
return poller((progress, continuePolling) => {
|
||||
if (querySelectorAll) {
|
||||
|
|
@ -1261,12 +1270,13 @@ export class Frame extends SdkObject {
|
|||
}
|
||||
|
||||
const element = injected.querySelector(info.parsed, document, info.strict);
|
||||
if (!element)
|
||||
if (!element && !omitAttached)
|
||||
return continuePolling;
|
||||
progress.logRepeating(` selector resolved to ${injected.previewNode(element)}`);
|
||||
if (element)
|
||||
progress.logRepeating(` selector resolved to ${injected.previewNode(element)}`);
|
||||
return callback(progress, element, taskData as T, [], continuePolling);
|
||||
});
|
||||
}, { info, taskData, callbackText, querySelectorAll: options.querySelectorAll, logScale: options.logScale });
|
||||
}, { info, taskData, callbackText, querySelectorAll: options.querySelectorAll, logScale: options.logScale, omitAttached: options.omitAttached });
|
||||
}, true);
|
||||
|
||||
if (this._detached)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import {
|
|||
printReceivedStringContainExpectedResult,
|
||||
printReceivedStringContainExpectedSubstring
|
||||
} from 'expect/build/print';
|
||||
import colors from 'colors/safe';
|
||||
import { ExpectedTextValue } from '../../protocol/channels';
|
||||
import { isRegExp, isString } from '../../utils/utils';
|
||||
import { currentTestInfo } from '../globals';
|
||||
|
|
@ -122,6 +123,6 @@ export function callLogText(log: string[] | undefined): string {
|
|||
return `
|
||||
|
||||
Call log:
|
||||
${(log || []).join('\n')}
|
||||
- ${colors.dim((log || []).join('\n - '))}
|
||||
`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -137,6 +137,12 @@ test('should support toBeVisible, toBeHidden', async ({ runInlineTest }) => {
|
|||
await expect(locator).toBeHidden();
|
||||
});
|
||||
|
||||
test('was hidden', async ({ page }) => {
|
||||
await page.setContent('<div</div>');
|
||||
const locator = page.locator('button');
|
||||
await expect(locator).toBeHidden();
|
||||
});
|
||||
|
||||
test('not hidden', async ({ page }) => {
|
||||
await page.setContent('<input></input>');
|
||||
const locator = page.locator('input');
|
||||
|
|
@ -144,10 +150,96 @@ test('should support toBeVisible, toBeHidden', async ({ runInlineTest }) => {
|
|||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
expect(result.passed).toBe(5);
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should support toBeVisible, toBeHidden wait', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
|
||||
test('visible', async ({ page }) => {
|
||||
await page.setContent('<div></div>');
|
||||
const locator = page.locator('span');
|
||||
setTimeout(() => {
|
||||
page.$eval('div', div => div.innerHTML = '<span>Hello</span>').catch(() => {});
|
||||
}, 0);
|
||||
await expect(locator).toBeVisible();
|
||||
});
|
||||
|
||||
test('not hidden', async ({ page }) => {
|
||||
await page.setContent('<div></div>');
|
||||
const locator = page.locator('span');
|
||||
setTimeout(() => {
|
||||
page.$eval('div', div => div.innerHTML = '<span>Hello</span>').catch(() => {});
|
||||
}, 0);
|
||||
await expect(locator).not.toBeHidden();
|
||||
});
|
||||
|
||||
test('not visible', async ({ page }) => {
|
||||
await page.setContent('<div><span>Hello</span></div>');
|
||||
const locator = page.locator('span');
|
||||
setTimeout(() => {
|
||||
page.$eval('span', span => span.textContent = '').catch(() => {});
|
||||
}, 0);
|
||||
await expect(locator).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('hidden', async ({ page }) => {
|
||||
await page.setContent('<div><span>Hello</span></div>');
|
||||
const locator = page.locator('span');
|
||||
setTimeout(() => {
|
||||
page.$eval('span', span => span.textContent = '').catch(() => {});
|
||||
}, 0);
|
||||
await expect(locator).toBeHidden();
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
expect(result.passed).toBe(4);
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should support toBeVisible, toBeHidden fail', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
const { test } = pwt;
|
||||
|
||||
test('visible', async ({ page }) => {
|
||||
await page.setContent('<button style="display: none"></button>');
|
||||
const locator = page.locator('button');
|
||||
await expect(locator).toBeVisible({ timeout: 500 });
|
||||
});
|
||||
|
||||
test('not visible', async ({ page }) => {
|
||||
await page.setContent('<input></input>');
|
||||
const locator = page.locator('input');
|
||||
await expect(locator).not.toBeVisible({ timeout: 500 });
|
||||
});
|
||||
|
||||
test('hidden', async ({ page }) => {
|
||||
await page.setContent('<input></input>');
|
||||
const locator = page.locator('input');
|
||||
await expect(locator).toBeHidden({ timeout: 500 });
|
||||
});
|
||||
|
||||
test('not hidden', async ({ page }) => {
|
||||
await page.setContent('<button style="display: none"></button>');
|
||||
const locator = page.locator('button');
|
||||
await expect(locator).not.toBeHidden({ timeout: 500 });
|
||||
});
|
||||
|
||||
test('not hidden 2', async ({ page }) => {
|
||||
await page.setContent('<div></div>');
|
||||
const locator = page.locator('button');
|
||||
await expect(locator).not.toBeHidden({ timeout: 500 });
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
expect(result.failed).toBe(5);
|
||||
expect(result.exitCode).toBe(1);
|
||||
});
|
||||
|
||||
test('should support toBeFocused', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
|
|
|
|||
Loading…
Reference in a new issue