mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(steps): make expect.toPass and expect.poll step containers (#30389)
Fixes https://github.com/microsoft/playwright/issues/30322
This commit is contained in:
parent
73fce8fb98
commit
3bdbe4284e
@ -187,9 +187,7 @@ export abstract class ChannelOwner<T extends channels.Channel = channels.Channel
|
|||||||
try {
|
try {
|
||||||
logApiCall(logger, `=> ${apiName} started`, isInternal);
|
logApiCall(logger, `=> ${apiName} started`, isInternal);
|
||||||
const apiZone: ApiZone = { apiName, frames, isInternal, reported: false, csi, callCookie, wallTime };
|
const apiZone: ApiZone = { apiName, frames, isInternal, reported: false, csi, callCookie, wallTime };
|
||||||
const result = await zones.run<ApiZone, Promise<R>>('apiZone', apiZone, async () => {
|
const result = await zones.run('apiZone', apiZone, async () => await func(apiZone));
|
||||||
return await func(apiZone);
|
|
||||||
});
|
|
||||||
csi?.onApiCallEnd(callCookie);
|
csi?.onApiCallEnd(callCookie);
|
||||||
logApiCall(logger, `<= ${apiName} succeeded`, isInternal);
|
logApiCall(logger, `<= ${apiName} succeeded`, isInternal);
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@ -21,18 +21,18 @@ export type ZoneType = 'apiZone' | 'expectZone' | 'stepZone';
|
|||||||
class ZoneManager {
|
class ZoneManager {
|
||||||
private readonly _asyncLocalStorage = new AsyncLocalStorage<Zone<unknown>|undefined>();
|
private readonly _asyncLocalStorage = new AsyncLocalStorage<Zone<unknown>|undefined>();
|
||||||
|
|
||||||
run<T, R>(type: ZoneType, data: T, func: (data: T) => R): R {
|
run<T, R>(type: ZoneType, data: T, func: () => R): R {
|
||||||
const previous = this._asyncLocalStorage.getStore();
|
const previous = this._asyncLocalStorage.getStore();
|
||||||
const zone = new Zone(previous, type, data);
|
const zone = new Zone(previous, type, data);
|
||||||
return this._asyncLocalStorage.run(zone, () => func(data));
|
return this._asyncLocalStorage.run(zone, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
zoneData<T>(type: ZoneType): T | null {
|
zoneData<T>(type: ZoneType): T | undefined {
|
||||||
for (let zone = this._asyncLocalStorage.getStore(); zone; zone = zone.previous) {
|
for (let zone = this._asyncLocalStorage.getStore(); zone; zone = zone.previous) {
|
||||||
if (zone.type === type)
|
if (zone.type === type)
|
||||||
return zone.data as T;
|
return zone.data as T;
|
||||||
}
|
}
|
||||||
return null;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
exitZones<R>(func: () => R): R {
|
exitZones<R>(func: () => R): R {
|
||||||
|
|||||||
@ -284,9 +284,12 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const expectZone: ExpectZone | null = matcherName !== 'toPass' ? { title, wallTime } : null;
|
|
||||||
const callback = () => matcher.call(target, ...args);
|
const callback = () => matcher.call(target, ...args);
|
||||||
const result = expectZone ? zones.run<ExpectZone, any>('expectZone', expectZone, callback) : callback();
|
// toPass and poll matchers can contain other steps, expects and API calls,
|
||||||
|
// so they behave like a retriable step.
|
||||||
|
const result = (matcherName === 'toPass' || this._info.isPoll) ?
|
||||||
|
zones.run('stepZone', step, callback) :
|
||||||
|
zones.run<ExpectZone, any>('expectZone', { title, wallTime }, callback);
|
||||||
if (result instanceof Promise)
|
if (result instanceof Promise)
|
||||||
return result.then(finalizer).catch(reportStepError);
|
return result.then(finalizer).catch(reportStepError);
|
||||||
finalizer();
|
finalizer();
|
||||||
|
|||||||
@ -252,7 +252,7 @@ export class TestInfoImpl implements TestInfo {
|
|||||||
// Predefined stages form a fixed hierarchy - use the current one as parent.
|
// Predefined stages form a fixed hierarchy - use the current one as parent.
|
||||||
parentStep = this._findLastStageStep();
|
parentStep = this._findLastStageStep();
|
||||||
} else {
|
} else {
|
||||||
parentStep = zones.zoneData<TestStepInternal>('stepZone') || undefined;
|
parentStep = zones.zoneData<TestStepInternal>('stepZone');
|
||||||
if (!parentStep && data.category !== 'test.step') {
|
if (!parentStep && data.category !== 'test.step') {
|
||||||
// API steps (but not test.step calls) can be nested by time, instead of by stack.
|
// API steps (but not test.step calls) can be nested by time, instead of by stack.
|
||||||
// However, do not nest chains of route.continue by checking the title.
|
// However, do not nest chains of route.continue by checking the title.
|
||||||
|
|||||||
@ -846,7 +846,6 @@ fixture | fixture: context
|
|||||||
|
|
||||||
test('step inside expect.toPass', async ({ runInlineTest }) => {
|
test('step inside expect.toPass', async ({ runInlineTest }) => {
|
||||||
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30322' });
|
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30322' });
|
||||||
test.fixme();
|
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'reporter.ts': stepIndentReporter,
|
'reporter.ts': stepIndentReporter,
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
@ -883,7 +882,7 @@ expect | expect.toPass @ a.test.ts:11
|
|||||||
test.step | step 2, attempt: 0 @ a.test.ts:7
|
test.step | step 2, attempt: 0 @ a.test.ts:7
|
||||||
test.step | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
test.step | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
expect | expect.toBe @ a.test.ts:9
|
expect | expect.toBe @ a.test.ts:9
|
||||||
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
test.step | step 2, attempt: 1 @ a.test.ts:7
|
test.step | step 2, attempt: 1 @ a.test.ts:7
|
||||||
expect | expect.toBe @ a.test.ts:9
|
expect | expect.toBe @ a.test.ts:9
|
||||||
test.step | step 3 @ a.test.ts:12
|
test.step | step 3 @ a.test.ts:12
|
||||||
@ -895,7 +894,6 @@ hook |After Hooks
|
|||||||
|
|
||||||
test('library API call inside expect.toPass', async ({ runInlineTest }) => {
|
test('library API call inside expect.toPass', async ({ runInlineTest }) => {
|
||||||
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30322' });
|
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30322' });
|
||||||
test.fixme();
|
|
||||||
const result = await runInlineTest({
|
const result = await runInlineTest({
|
||||||
'reporter.ts': stepIndentReporter,
|
'reporter.ts': stepIndentReporter,
|
||||||
'playwright.config.ts': `
|
'playwright.config.ts': `
|
||||||
@ -940,4 +938,53 @@ hook |After Hooks
|
|||||||
fixture | fixture: page
|
fixture | fixture: page
|
||||||
fixture | fixture: context
|
fixture | fixture: context
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('library API call inside expect.poll', async ({ runInlineTest }) => {
|
||||||
|
test.info().annotations.push({ type: 'issue', description: 'https://github.com/microsoft/playwright/issues/30322' });
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'reporter.ts': stepIndentReporter,
|
||||||
|
'playwright.config.ts': `
|
||||||
|
module.exports = {
|
||||||
|
reporter: './reporter',
|
||||||
|
};
|
||||||
|
`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
test('pass', async ({page}) => {
|
||||||
|
let counter = 0
|
||||||
|
const a = [];
|
||||||
|
await expect.poll(async () => {
|
||||||
|
await page.goto('about:blank');
|
||||||
|
await test.step('inner step attempt: ' + counter, async () => {
|
||||||
|
counter++;
|
||||||
|
expect(1).toBe(1);
|
||||||
|
});
|
||||||
|
a.push(1);
|
||||||
|
return a;
|
||||||
|
}).toHaveLength(2);
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { reporter: '', workers: 1 });
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(stripAnsi(result.output)).toBe(`
|
||||||
|
hook |Before Hooks
|
||||||
|
fixture | fixture: browser
|
||||||
|
pw:api | browserType.launch
|
||||||
|
fixture | fixture: context
|
||||||
|
pw:api | browser.newContext
|
||||||
|
fixture | fixture: page
|
||||||
|
pw:api | browserContext.newPage
|
||||||
|
expect |expect.poll.toHaveLength @ a.test.ts:14
|
||||||
|
pw:api | page.goto(about:blank) @ a.test.ts:7
|
||||||
|
test.step | inner step attempt: 0 @ a.test.ts:8
|
||||||
|
expect | expect.toBe @ a.test.ts:10
|
||||||
|
pw:api | page.goto(about:blank) @ a.test.ts:7
|
||||||
|
test.step | inner step attempt: 1 @ a.test.ts:8
|
||||||
|
expect | expect.toBe @ a.test.ts:10
|
||||||
|
hook |After Hooks
|
||||||
|
fixture | fixture: page
|
||||||
|
fixture | fixture: context
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user