chore: remove expectZone (#34312)
This commit is contained in:
parent
08af3a2f06
commit
be6caed8df
|
@ -156,8 +156,3 @@ export function compressCallLog(log: string[]): string[] {
|
||||||
}
|
}
|
||||||
return lines;
|
return lines;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ExpectZone = {
|
|
||||||
title: string;
|
|
||||||
stepId: string;
|
|
||||||
};
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
import { AsyncLocalStorage } from 'async_hooks';
|
import { AsyncLocalStorage } from 'async_hooks';
|
||||||
|
|
||||||
export type ZoneType = 'apiZone' | 'expectZone' | 'stepZone';
|
export type ZoneType = 'apiZone' | 'stepZone';
|
||||||
|
|
||||||
class ZoneManager {
|
class ZoneManager {
|
||||||
private readonly _asyncLocalStorage = new AsyncLocalStorage<Zone | undefined>();
|
private readonly _asyncLocalStorage = new AsyncLocalStorage<Zone | undefined>();
|
||||||
|
|
|
@ -19,7 +19,6 @@ import * as path from 'path';
|
||||||
import type { APIRequestContext, BrowserContext, Browser, BrowserContextOptions, LaunchOptions, Page, Tracing, Video } from 'playwright-core';
|
import type { APIRequestContext, BrowserContext, Browser, BrowserContextOptions, LaunchOptions, Page, Tracing, Video } from 'playwright-core';
|
||||||
import * as playwrightLibrary from 'playwright-core';
|
import * as playwrightLibrary from 'playwright-core';
|
||||||
import { createGuid, debugMode, addInternalStackPrefix, isString, asLocator, jsonStringifyForceASCII, zones } from 'playwright-core/lib/utils';
|
import { createGuid, debugMode, addInternalStackPrefix, isString, asLocator, jsonStringifyForceASCII, zones } from 'playwright-core/lib/utils';
|
||||||
import type { ExpectZone } from 'playwright-core/lib/utils';
|
|
||||||
import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, VideoMode } from '../types/test';
|
import type { Fixtures, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, ScreenshotMode, TestInfo, TestType, VideoMode } from '../types/test';
|
||||||
import type { TestInfoImpl, TestStepInternal } from './worker/testInfo';
|
import type { TestInfoImpl, TestStepInternal } from './worker/testInfo';
|
||||||
import { rootTestType } from './common/testType';
|
import { rootTestType } from './common/testType';
|
||||||
|
@ -264,12 +263,12 @@ const playwrightFixtures: Fixtures<TestFixtures, WorkerFixtures> = ({
|
||||||
// Some special calls do not get into steps.
|
// Some special calls do not get into steps.
|
||||||
if (!testInfo || data.apiName.includes('setTestIdAttribute') || data.apiName === 'tracing.groupEnd')
|
if (!testInfo || data.apiName.includes('setTestIdAttribute') || data.apiName === 'tracing.groupEnd')
|
||||||
return;
|
return;
|
||||||
const expectZone = zones.zoneData<ExpectZone>('expectZone');
|
const zone = zones.zoneData<TestStepInternal>('stepZone');
|
||||||
if (expectZone) {
|
if (zone && zone.category === 'expect') {
|
||||||
// Display the internal locator._expect call under the name of the enclosing expect call,
|
// Display the internal locator._expect call under the name of the enclosing expect call,
|
||||||
// and connect it to the existing expect step.
|
// and connect it to the existing expect step.
|
||||||
data.apiName = expectZone.title;
|
data.apiName = zone.title;
|
||||||
data.stepId = expectZone.stepId;
|
data.stepId = zone.stepId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// In the general case, create a step for each api call and connect them through the stepId.
|
// In the general case, create a step for each api call and connect them through the stepId.
|
||||||
|
|
|
@ -19,7 +19,6 @@ import {
|
||||||
createGuid,
|
createGuid,
|
||||||
isString,
|
isString,
|
||||||
pollAgainstDeadline } from 'playwright-core/lib/utils';
|
pollAgainstDeadline } from 'playwright-core/lib/utils';
|
||||||
import type { ExpectZone } from 'playwright-core/lib/utils';
|
|
||||||
import {
|
import {
|
||||||
toBeAttached,
|
toBeAttached,
|
||||||
toBeChecked,
|
toBeChecked,
|
||||||
|
@ -315,9 +314,10 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
|
||||||
// out all the frames that belong to the test runner from caught runtime errors.
|
// out all the frames that belong to the test runner from caught runtime errors.
|
||||||
const stackFrames = filteredStackTrace(captureRawStack());
|
const stackFrames = filteredStackTrace(captureRawStack());
|
||||||
|
|
||||||
// Enclose toPass in a step to maintain async stacks, toPass matcher is always async.
|
// toPass and poll matchers can contain other steps, expects and API calls,
|
||||||
|
// so they behave like a retriable step.
|
||||||
const stepInfo = {
|
const stepInfo = {
|
||||||
category: 'expect',
|
category: (matcherName === 'toPass' || this._info.poll) ? 'step' : 'expect',
|
||||||
title: trimLongString(title, 1024),
|
title: trimLongString(title, 1024),
|
||||||
params: args[0] ? { expected: args[0] } : undefined,
|
params: args[0] ? { expected: args[0] } : undefined,
|
||||||
infectParentStepsWithError: this._info.isSoft,
|
infectParentStepsWithError: this._info.isSoft,
|
||||||
|
@ -345,11 +345,7 @@ class ExpectMetaInfoProxyHandler implements ProxyHandler<any> {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const callback = () => matcher.call(target, ...args);
|
const callback = () => matcher.call(target, ...args);
|
||||||
// toPass and poll matchers can contain other steps, expects and API calls,
|
const result = zones.run('stepZone', step, callback);
|
||||||
// so they behave like a retriable step.
|
|
||||||
const result = (matcherName === 'toPass' || this._info.poll) ?
|
|
||||||
zones.run('stepZone', step, callback) :
|
|
||||||
zones.run<ExpectZone, any>('expectZone', { title, stepId: step.stepId }, callback);
|
|
||||||
if (result instanceof Promise)
|
if (result instanceof Promise)
|
||||||
return result.then(finalizer).catch(reportStepError);
|
return result.then(finalizer).catch(reportStepError);
|
||||||
finalizer();
|
finalizer();
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { captureRawStack, monotonicTime, zones, sanitizeForFilePath, stringifyStackFrames } from 'playwright-core/lib/utils';
|
import { captureRawStack, monotonicTime, zones, sanitizeForFilePath, stringifyStackFrames } from 'playwright-core/lib/utils';
|
||||||
import type { ExpectZone } from 'playwright-core/lib/utils';
|
|
||||||
import type { TestInfo, TestStatus, FullProject } from '../../types/test';
|
import type { TestInfo, TestStatus, FullProject } from '../../types/test';
|
||||||
import type { AttachmentPayload, StepBeginPayload, StepEndPayload, TestInfoErrorImpl, WorkerInitParams } from '../common/ipc';
|
import type { AttachmentPayload, StepBeginPayload, StepEndPayload, TestInfoErrorImpl, WorkerInitParams } from '../common/ipc';
|
||||||
import type { TestCase } from '../common/test';
|
import type { TestCase } from '../common/test';
|
||||||
|
@ -35,7 +34,7 @@ export interface TestStepInternal {
|
||||||
attachmentIndices: number[];
|
attachmentIndices: number[];
|
||||||
stepId: string;
|
stepId: string;
|
||||||
title: string;
|
title: string;
|
||||||
category: 'hook' | 'fixture' | 'test.step' | 'test.step.skip' | 'expect' | 'attach' | string;
|
category: string;
|
||||||
location?: Location;
|
location?: Location;
|
||||||
boxedStack?: StackFrame[];
|
boxedStack?: StackFrame[];
|
||||||
steps: TestStepInternal[];
|
steps: TestStepInternal[];
|
||||||
|
@ -195,7 +194,7 @@ export class TestInfoImpl implements TestInfo {
|
||||||
this._attachmentsPush = this.attachments.push.bind(this.attachments);
|
this._attachmentsPush = this.attachments.push.bind(this.attachments);
|
||||||
this.attachments.push = (...attachments: TestInfo['attachments']) => {
|
this.attachments.push = (...attachments: TestInfo['attachments']) => {
|
||||||
for (const a of attachments)
|
for (const a of attachments)
|
||||||
this._attach(a, this._expectStepId() ?? this._parentStep()?.stepId);
|
this._attach(a, this._parentStep()?.stepId);
|
||||||
return this.attachments.length;
|
return this.attachments.length;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -245,10 +244,6 @@ export class TestInfoImpl implements TestInfo {
|
||||||
?? this._findLastStageStep(this._steps); // If no parent step on stack, assume the current stage as parent.
|
?? this._findLastStageStep(this._steps); // If no parent step on stack, assume the current stage as parent.
|
||||||
}
|
}
|
||||||
|
|
||||||
private _expectStepId() {
|
|
||||||
return zones.zoneData<ExpectZone>('expectZone')?.stepId;
|
|
||||||
}
|
|
||||||
|
|
||||||
_addStep(data: Omit<TestStepInternal, 'complete' | 'stepId' | 'steps' | 'attachmentIndices'>, parentStep?: TestStepInternal): TestStepInternal {
|
_addStep(data: Omit<TestStepInternal, 'complete' | 'stepId' | 'steps' | 'attachmentIndices'>, parentStep?: TestStepInternal): TestStepInternal {
|
||||||
const stepId = `${data.category}@${++this._lastStepId}`;
|
const stepId = `${data.category}@${++this._lastStepId}`;
|
||||||
|
|
||||||
|
|
|
@ -616,7 +616,7 @@ test('should not propagate errors from within toPass', async ({ runInlineTest })
|
||||||
expect(result.exitCode).toBe(0);
|
expect(result.exitCode).toBe(0);
|
||||||
expect(result.output).toBe(`
|
expect(result.output).toBe(`
|
||||||
hook |Before Hooks
|
hook |Before Hooks
|
||||||
expect |expect.toPass @ a.test.ts:7
|
step |expect.toPass @ a.test.ts:7
|
||||||
expect | expect.toBe @ a.test.ts:6
|
expect | expect.toBe @ a.test.ts:6
|
||||||
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
expect | expect.toBe @ a.test.ts:6
|
expect | expect.toBe @ a.test.ts:6
|
||||||
|
@ -643,8 +643,8 @@ test('should show final toPass error', async ({ runInlineTest }) => {
|
||||||
expect(result.exitCode).toBe(1);
|
expect(result.exitCode).toBe(1);
|
||||||
expect(stripAnsi(result.output)).toBe(`
|
expect(stripAnsi(result.output)).toBe(`
|
||||||
hook |Before Hooks
|
hook |Before Hooks
|
||||||
expect |expect.toPass @ a.test.ts:6
|
step |expect.toPass @ a.test.ts:6
|
||||||
expect |↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
step |↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
expect | expect.toBe @ a.test.ts:5
|
expect | expect.toBe @ a.test.ts:5
|
||||||
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
hook |After Hooks
|
hook |After Hooks
|
||||||
|
@ -909,7 +909,7 @@ test('step inside expect.toPass', async ({ runInlineTest }) => {
|
||||||
expect(stripAnsi(result.output)).toBe(`
|
expect(stripAnsi(result.output)).toBe(`
|
||||||
hook |Before Hooks
|
hook |Before Hooks
|
||||||
test.step |step 1 @ a.test.ts:4
|
test.step |step 1 @ a.test.ts:4
|
||||||
expect | expect.toPass @ a.test.ts:11
|
step | 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
|
||||||
|
@ -956,7 +956,7 @@ fixture | fixture: context
|
||||||
pw:api | browser.newContext
|
pw:api | browser.newContext
|
||||||
fixture | fixture: page
|
fixture | fixture: page
|
||||||
pw:api | browserContext.newPage
|
pw:api | browserContext.newPage
|
||||||
expect |expect.toPass @ a.test.ts:11
|
step |expect.toPass @ a.test.ts:11
|
||||||
pw:api | page.goto(about:blank) @ a.test.ts:6
|
pw:api | page.goto(about:blank) @ a.test.ts:6
|
||||||
test.step | inner step attempt: 0 @ a.test.ts:7
|
test.step | inner step 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
|
||||||
|
@ -1007,7 +1007,7 @@ fixture | fixture: context
|
||||||
pw:api | browser.newContext
|
pw:api | browser.newContext
|
||||||
fixture | fixture: page
|
fixture | fixture: page
|
||||||
pw:api | browserContext.newPage
|
pw:api | browserContext.newPage
|
||||||
expect |expect.poll.toHaveLength @ a.test.ts:14
|
step |expect.poll.toHaveLength @ a.test.ts:14
|
||||||
pw:api | page.goto(about:blank) @ a.test.ts:7
|
pw:api | page.goto(about:blank) @ a.test.ts:7
|
||||||
test.step | inner step attempt: 0 @ a.test.ts:8
|
test.step | inner step attempt: 0 @ a.test.ts:8
|
||||||
expect | expect.toBe @ a.test.ts:10
|
expect | expect.toBe @ a.test.ts:10
|
||||||
|
@ -1059,7 +1059,7 @@ pw:api | browser.newContext
|
||||||
fixture | fixture: page
|
fixture | fixture: page
|
||||||
pw:api | browserContext.newPage
|
pw:api | browserContext.newPage
|
||||||
pw:api |page.setContent @ a.test.ts:4
|
pw:api |page.setContent @ a.test.ts:4
|
||||||
expect |expect.poll.toBe @ a.test.ts:13
|
step |expect.poll.toBe @ a.test.ts:13
|
||||||
expect | expect.toHaveText @ a.test.ts:7
|
expect | expect.toHaveText @ a.test.ts:7
|
||||||
test.step | iteration 1 @ a.test.ts:9
|
test.step | iteration 1 @ a.test.ts:9
|
||||||
expect | expect.toBeVisible @ a.test.ts:10
|
expect | expect.toBeVisible @ a.test.ts:10
|
||||||
|
@ -1565,3 +1565,66 @@ expect |expect.toBe @ a.test.ts:10
|
||||||
hook |After Hooks
|
hook |After Hooks
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('show api calls inside expects', async ({ runInlineTest }) => {
|
||||||
|
const result = await runInlineTest({
|
||||||
|
'reporter.ts': stepIndentReporter,
|
||||||
|
'playwright.config.ts': `module.exports = { reporter: './reporter' };`,
|
||||||
|
'a.test.ts': `
|
||||||
|
import { test, expect as baseExpect } from '@playwright/test';
|
||||||
|
|
||||||
|
const expect = baseExpect.extend({
|
||||||
|
async toBeInvisible(locator: Locator) {
|
||||||
|
try {
|
||||||
|
await expect.poll(() => locator.isVisible()).toBe(false);
|
||||||
|
return { name: 'toBeInvisible', pass: true, message: '' };
|
||||||
|
} catch (e) {
|
||||||
|
return { name: 'toBeInvisible', pass: false, message: () => 'Expected to be invisible, got visible!' };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
test('test', async ({ page }) => {
|
||||||
|
await page.setContent('<div>hello</div>');
|
||||||
|
const promise = expect(page.locator('div')).toBeInvisible();
|
||||||
|
await page.waitForTimeout(1100);
|
||||||
|
await page.setContent('<div style="display:none">hello</div>');
|
||||||
|
await promise;
|
||||||
|
});
|
||||||
|
`
|
||||||
|
}, { reporter: '' });
|
||||||
|
|
||||||
|
expect(result.exitCode).toBe(0);
|
||||||
|
expect(result.report.stats.expected).toBe(1);
|
||||||
|
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
|
||||||
|
pw:api |page.setContent @ a.test.ts:16
|
||||||
|
expect |expect.toBeInvisible @ a.test.ts:17
|
||||||
|
step | expect.poll.toBe @ a.test.ts:7
|
||||||
|
pw:api | locator.isVisible(div) @ a.test.ts:7
|
||||||
|
expect | expect.toBe @ a.test.ts:7
|
||||||
|
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
|
pw:api | locator.isVisible(div) @ a.test.ts:7
|
||||||
|
expect | expect.toBe @ a.test.ts:7
|
||||||
|
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
|
pw:api | locator.isVisible(div) @ a.test.ts:7
|
||||||
|
expect | expect.toBe @ a.test.ts:7
|
||||||
|
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
|
pw:api | locator.isVisible(div) @ a.test.ts:7
|
||||||
|
expect | expect.toBe @ a.test.ts:7
|
||||||
|
expect | ↪ error: Error: expect(received).toBe(expected) // Object.is equality
|
||||||
|
pw:api | locator.isVisible(div) @ a.test.ts:7
|
||||||
|
expect | expect.toBe @ a.test.ts:7
|
||||||
|
pw:api |page.waitForTimeout @ a.test.ts:18
|
||||||
|
pw:api |page.setContent @ a.test.ts:19
|
||||||
|
hook |After Hooks
|
||||||
|
fixture | fixture: page
|
||||||
|
fixture | fixture: context
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue