Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/test/expect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ function wrap(matcherName: string, matcher: any) {
return matcher.call(this, ...args);

const INTERNAL_STACK_LENGTH = 3;
// at Object.__PWTRAP__[expect.toHaveText] (...)
// at __EXTERNAL_MATCHER_TRAP__ (...)
// at Object.throwingMatcher [as toHaveText] (...)
// at <test function> (...)
const stackLines = new Error().stack!.split('\n').slice(INTERNAL_STACK_LENGTH + 1);
const step = testInfo._addStep({
category: 'expect',
Expand Down Expand Up @@ -107,6 +111,7 @@ function wrap(matcherName: string, matcher: any) {
reportStepError(e);
}
};
result.displayName = '__PWTRAP__[expect.' + matcherName + ']';
return result;
}

Expand Down
47 changes: 22 additions & 25 deletions src/utils/stackTrace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ export function rewriteErrorMessage<E extends Error>(e: E, newMessage: string):
const ROOT_DIR = path.resolve(__dirname, '..', '..');
const CLIENT_LIB = path.join(ROOT_DIR, 'lib', 'client');
const CLIENT_SRC = path.join(ROOT_DIR, 'src', 'client');
const TEST_LIB = path.join(ROOT_DIR, 'lib', 'test');
const TEST_SRC = path.join(ROOT_DIR, 'src', 'test');

export type ParsedStackTrace = {
allFrames: StackFrame[];
Expand Down Expand Up @@ -65,14 +63,7 @@ export function captureStackTrace(): ParsedStackTrace {
const fileName = path.resolve(process.cwd(), frame.file);
if (isTesting && fileName.includes(path.join('playwright', 'tests', 'config', 'coverage.js')))
return null;
const inClient =
// Allow fixtures in the reported stacks.
(!fileName.includes('test/index') && !fileName.includes('test\\index')) && (
frame.file.includes(path.join('node_modules', 'expect'))
|| fileName.startsWith(CLIENT_LIB)
|| fileName.startsWith(CLIENT_SRC)
|| fileName.startsWith(TEST_LIB)
|| fileName.startsWith(TEST_SRC));
const inClient = fileName.startsWith(CLIENT_LIB) || fileName.startsWith(CLIENT_SRC);
Comment thread
dgozman marked this conversation as resolved.
const parsed: ParsedFrame = {
frame: {
file: fileName,
Expand All @@ -87,23 +78,29 @@ export function captureStackTrace(): ParsedStackTrace {
}).filter(Boolean) as ParsedFrame[];

let apiName = '';
// Deepest transition between non-client code calling into client code
// is the api entry.
const allFrames = parsedFrames;
for (let i = 0; i < parsedFrames.length - 1; i++) {
if (parsedFrames[i].inClient && !parsedFrames[i + 1].inClient) {
const frame = parsedFrames[i].frame;
const text = parsedFrames[i].frameText;
// expect matchers have the following stack structure:
// at __EXTERNAL_MATCHER_TRAP__ (.../index.js:342:30)
// at Object.throwingMatcher [as toBeChecked] (.../index.js:343:15)
const aliasIndex = text.indexOf('[as ');
if (aliasIndex !== -1)
apiName = 'expect.' + text.substring(aliasIndex + 4, text.indexOf(']'));
else

// expect matchers have the following stack structure:
// at Object.__PWTRAP__[expect.toHaveText] (...)
// at __EXTERNAL_MATCHER_TRAP__ (...)
// at Object.throwingMatcher [as toHaveText] (...)
const TRAP = '__PWTRAP__[';
const expectIndex = parsedFrames.findIndex(f => f.frameText.includes(TRAP));
if (expectIndex !== -1) {
const text = parsedFrames[expectIndex].frameText;
const aliasIndex = text.indexOf(TRAP);
apiName = text.substring(aliasIndex + TRAP.length, text.indexOf(']'));
parsedFrames = parsedFrames.slice(expectIndex + 3);
} else {
// Deepest transition between non-client code calling into client code
// is the api entry.
for (let i = 0; i < parsedFrames.length - 1; i++) {
if (parsedFrames[i].inClient && !parsedFrames[i + 1].inClient) {
const frame = parsedFrames[i].frame;
apiName = frame.function ? frame.function[0].toLowerCase() + frame.function.slice(1) : '';
parsedFrames = parsedFrames.slice(i + 1);
break;
parsedFrames = parsedFrames.slice(i + 1);
break;
}
}
}

Expand Down
19 changes: 19 additions & 0 deletions tests/inspector/pause.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,25 @@ it.describe('pause', () => {
await scriptPromise;
});

it('should show expect.toHaveText', async ({ page, recorderPageGetter }) => {
await page.setContent('<button>Submit</button>');
const scriptPromise = (async () => {
await page.pause();
await expect(page.locator('button')).toHaveText('Submit');
await page.pause(); // 2
})();
const recorderPage = await recorderPageGetter();
await recorderPage.click('[title="Resume"]');
await recorderPage.waitForSelector('.source-line-paused:has-text("page.pause(); // 2")');
expect(await sanitizeLog(recorderPage)).toEqual([
'page.pause- XXms',
'expect.toHaveText(button)- XXms',
'page.pause',
]);
await recorderPage.click('[title="Resume"]');
await scriptPromise;
});

it('should highlight waitForEvent', async ({ page, recorderPageGetter }) => {
await page.setContent('<button onclick="console.log(1)">Submit</button>');
const scriptPromise = (async () => {
Expand Down
2 changes: 2 additions & 0 deletions tests/trace-viewer/trace-viewer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ test.beforeAll(async function recordTrace({ browser, browserName, browserType, s
const page = await context.newPage();
await page.goto('data:text/html,<html>Hello world</html>');
await page.setContent('<button>Click</button>');
await expect(page.locator('button')).toHaveText('Click');
await page.evaluate(({ a }) => {
console.log('Info');
console.warn('Warning');
Expand Down Expand Up @@ -158,6 +159,7 @@ test('should open simple trace viewer', async ({ showTraceViewer }) => {
await expect(traceViewer.actionTitles).toHaveText([
/page.gotodata:text\/html,<html>Hello world<\/html>— [\d.ms]+/,
/page.setContent— [\d.ms]+/,
/expect.toHaveTextbutton— [\d.ms]+/,
/page.evaluate— [\d.ms]+/,
/page.click"Click"— [\d.ms]+/,
/page.waitForEvent— [\d.ms]+/,
Expand Down