mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
feat: support clarification message for expect (#11735)
The clarification message is displayed in the HTML report as the name of the step:  It is also shown in terminal output:  Fixes #7816
This commit is contained in:
parent
2b55adaafa
commit
2336692e8a
@ -19,6 +19,12 @@ expect(value).not.toEqual(0);
|
||||
await expect(locator).not.toContainText("some text");
|
||||
```
|
||||
|
||||
You can also specify a custom error message as a second argument to the `expect` function, for example:
|
||||
|
||||
```js
|
||||
expect(value, 'my custom error message').toBe(42);
|
||||
```
|
||||
|
||||
<!-- TOC -->
|
||||
- [`method: LocatorAssertions.toBeChecked`]
|
||||
- [`method: LocatorAssertions.toBeDisabled`]
|
||||
|
||||
@ -619,3 +619,4 @@ export class SigIntWatcher {
|
||||
process.off('SIGINT', this._sigintHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -92,7 +92,15 @@ export const printReceivedStringContainExpectedResult = (
|
||||
|
||||
// #endregion
|
||||
|
||||
export const expect: Expect = expectLibrary as any;
|
||||
export const expect: Expect = new Proxy(expectLibrary as any, {
|
||||
apply: function(target: any, thisArg: any, argumentsList: [actual: unknown, message: string|undefined]) {
|
||||
const message = argumentsList[1];
|
||||
if (message !== undefined && typeof message !== 'string')
|
||||
throw new Error('expect(actual, optionalErrorMessage): optional error message must be a string.');
|
||||
return new Proxy(expectLibrary.call(thisArg, argumentsList[0]), new ExpectMetaInfoProxyHandler(message || ''));
|
||||
}
|
||||
});
|
||||
|
||||
expectLibrary.setState({ expand: false });
|
||||
const customMatchers = {
|
||||
toBeChecked,
|
||||
@ -118,23 +126,56 @@ const customMatchers = {
|
||||
toMatchSnapshot,
|
||||
};
|
||||
|
||||
type ExpectMetaInfo = {
|
||||
message: string;
|
||||
};
|
||||
|
||||
let expectCallMetaInfo: undefined|ExpectMetaInfo = undefined;
|
||||
|
||||
class ExpectMetaInfoProxyHandler {
|
||||
private _message: string;
|
||||
|
||||
constructor(message: string) {
|
||||
this._message = message;
|
||||
}
|
||||
|
||||
get(target: any, prop: any, receiver: any): any {
|
||||
const value = Reflect.get(target, prop, receiver);
|
||||
if (typeof value !== 'function')
|
||||
return new Proxy(value, this);
|
||||
return (...args: any[]) => {
|
||||
try {
|
||||
expectCallMetaInfo = {
|
||||
message: this._message,
|
||||
};
|
||||
const result = value.call(target, ...args);
|
||||
return result;
|
||||
} finally {
|
||||
expectCallMetaInfo = undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function wrap(matcherName: string, matcher: any) {
|
||||
const result = function(this: any, ...args: any[]) {
|
||||
const testInfo = currentTestInfo();
|
||||
if (!testInfo)
|
||||
return matcher.call(this, ...args);
|
||||
|
||||
const INTERNAL_STACK_LENGTH = 3;
|
||||
const INTERNAL_STACK_LENGTH = 4;
|
||||
// at Object.__PWTRAP__[expect.toHaveText] (...)
|
||||
// at __EXTERNAL_MATCHER_TRAP__ (...)
|
||||
// at Object.throwingMatcher [as toHaveText] (...)
|
||||
// at Proxy.<anonymous>
|
||||
// at <test function> (...)
|
||||
const stackLines = new Error().stack!.split('\n').slice(INTERNAL_STACK_LENGTH + 1);
|
||||
const frame = stackLines[0] ? stackUtils.parseLine(stackLines[0]) : undefined;
|
||||
const customMessage = expectCallMetaInfo?.message ?? '';
|
||||
const step = testInfo._addStep({
|
||||
location: frame && frame.file ? { file: path.resolve(process.cwd(), frame.file), line: frame.line || 0, column: frame.column || 0 } : undefined,
|
||||
category: 'expect',
|
||||
title: `expect${this.isNot ? '.not' : ''}.${matcherName}`,
|
||||
title: customMessage || `expect${this.isNot ? '.not' : ''}.${matcherName}`,
|
||||
canHaveChildren: true,
|
||||
forceNoParent: false
|
||||
});
|
||||
@ -145,6 +186,25 @@ function wrap(matcherName: string, matcher: any) {
|
||||
if (!success) {
|
||||
const message = result.message();
|
||||
error = { message, stack: message + '\n' + stackLines.join('\n') };
|
||||
if (customMessage) {
|
||||
const messageLines = message.split('\n');
|
||||
// Jest adds something like the following error to all errors:
|
||||
// expect(received).toBe(expected); // Object.is equality
|
||||
const uselessMatcherLineIndex = messageLines.findIndex((line: string) => /expect.*\(.*received.*\)/.test(line));
|
||||
if (uselessMatcherLineIndex !== -1) {
|
||||
// if there's a newline after the matcher text, then remove it as well.
|
||||
if (uselessMatcherLineIndex + 1 < messageLines.length && messageLines[uselessMatcherLineIndex + 1].trim() === '')
|
||||
messageLines.splice(uselessMatcherLineIndex, 2);
|
||||
else
|
||||
messageLines.splice(uselessMatcherLineIndex, 1);
|
||||
}
|
||||
const newMessage = [
|
||||
customMessage,
|
||||
'',
|
||||
...messageLines,
|
||||
].join('\n');
|
||||
result.message = () => newMessage;
|
||||
}
|
||||
}
|
||||
step.complete(error);
|
||||
return result;
|
||||
|
||||
@ -419,9 +419,9 @@ function monotonicTime(): number {
|
||||
return seconds * 1000 + (nanoseconds / 1000000 | 0);
|
||||
}
|
||||
|
||||
const asciiRegex = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', 'g');
|
||||
const ansiRegex = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', 'g');
|
||||
export function stripAnsiEscapes(str: string): string {
|
||||
return str.replace(asciiRegex, '');
|
||||
return str.replace(ansiRegex, '');
|
||||
}
|
||||
|
||||
// Leaves enough space for the "suffix" to also fit.
|
||||
@ -432,8 +432,8 @@ export function fitToScreen(line: string, width: number, suffix?: string): strin
|
||||
return line;
|
||||
let m;
|
||||
let ansiLen = 0;
|
||||
asciiRegex.lastIndex = 0;
|
||||
while ((m = asciiRegex.exec(line)) !== null) {
|
||||
ansiRegex.lastIndex = 0;
|
||||
while ((m = ansiRegex.exec(line)) !== null) {
|
||||
const visibleLen = m.index - ansiLen;
|
||||
if (visibleLen >= width)
|
||||
break;
|
||||
|
||||
@ -197,11 +197,10 @@ function serializeXML(entry: XMLEntry, tokens: string[], stripANSIControlSequenc
|
||||
|
||||
// See https://en.wikipedia.org/wiki/Valid_characters_in_XML
|
||||
const discouragedXMLCharacters = /[\u0001-\u0008\u000b-\u000c\u000e-\u001f\u007f-\u0084\u0086-\u009f]/g;
|
||||
const ansiControlSequence = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', 'g');
|
||||
|
||||
function escape(text: string, stripANSIControlSequences: boolean, isCharacterData: boolean): string {
|
||||
if (stripANSIControlSequences)
|
||||
text = text.replace(ansiControlSequence, '');
|
||||
text = stripAnsiEscapes(text);
|
||||
const escapeRe = isCharacterData ? /[&<]/g : /[&"<>]/g;
|
||||
text = text.replace(escapeRe, c => ({ '&': '&', '"': '"', '<': '<', '>': '>' }[c]!));
|
||||
if (isCharacterData)
|
||||
|
||||
@ -28,7 +28,7 @@ type MakeMatchers<T, ReturnValue = T> = PlaywrightTest.Matchers<ReturnValue> &
|
||||
ExtraMatchers<T, APIResponse, APIResponseMatchers>
|
||||
|
||||
export declare type Expect = {
|
||||
<T = unknown>(actual: T): MakeMatchers<T>;
|
||||
<T = unknown>(actual: T, message?: string): MakeMatchers<T>;
|
||||
|
||||
// Sourced from node_modules/expect/build/types.d.ts
|
||||
assertions(arg0: number): void;
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should fail', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -286,7 +286,7 @@ test('should work with test wrapper', async ({ runInlineTest }) => {
|
||||
}, { workers: 1, reporter: 'line' });
|
||||
expect(result.passed).toBe(4);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(stripAscii(result.output).split('\n').filter(line => line.startsWith('%%'))).toEqual([
|
||||
expect(stripAnsi(result.output).split('\n').filter(line => line.startsWith('%%'))).toEqual([
|
||||
'%%a.spec',
|
||||
'%%helper',
|
||||
'%%b.spec',
|
||||
@ -335,7 +335,7 @@ test('should work with test helper', async ({ runInlineTest }) => {
|
||||
}, { workers: 1, reporter: 'line' });
|
||||
expect(result.passed).toBe(4);
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(stripAscii(result.output).split('\n').filter(line => line.startsWith('%%'))).toEqual([
|
||||
expect(stripAnsi(result.output).split('\n').filter(line => line.startsWith('%%'))).toEqual([
|
||||
'%%a.spec',
|
||||
'%%helper-a',
|
||||
'%%b.spec',
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
function monotonicTime(): number {
|
||||
const [seconds, nanoseconds] = process.hrtime();
|
||||
@ -46,7 +46,7 @@ test('should work with not defined errors', async ({ runInlineTest }) => {
|
||||
foo();
|
||||
`
|
||||
});
|
||||
expect(stripAscii(result.output)).toContain('foo is not defined');
|
||||
expect(stripAnsi(result.output)).toContain('foo is not defined');
|
||||
expect(result.exitCode).toBe(1);
|
||||
});
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should be able to call expect.extend in config', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -67,6 +67,57 @@ test('should not expand huge arrays', async ({ runInlineTest }) => {
|
||||
expect(result.output.length).toBeLessThan(100000);
|
||||
});
|
||||
|
||||
test('should fail when passed `null` instead of message', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'expect-test.spec.ts': `
|
||||
const { test } = pwt;
|
||||
test('custom expect message', () => {
|
||||
test.expect(1+1, null).toEqual(3);
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(stripAnsi(result.output)).toContain(`optional error message must be a string.`);
|
||||
});
|
||||
|
||||
test('should include custom error message', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'expect-test.spec.ts': `
|
||||
const { test } = pwt;
|
||||
test('custom expect message', () => {
|
||||
test.expect(1+1, 'one plus one is two!').toEqual(3);
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(stripAnsi(result.output)).toContain([
|
||||
` Error: one plus one is two!`,
|
||||
``,
|
||||
` Expected: 3`,
|
||||
` Received: 2`,
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
test('should include custom error message with web-first assertions', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'expect-test.spec.ts': `
|
||||
const { test } = pwt;
|
||||
test('custom expect message', async ({page}) => {
|
||||
await expect(page.locator('x-foo'), 'x-foo must be visible').toBeVisible({timeout: 1});
|
||||
});
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.output).toContain([
|
||||
` Error: x-foo must be visible`,
|
||||
``,
|
||||
` Call log:`,
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
test('should work with default expect prototype functions', async ({ runTSC }) => {
|
||||
const result = await runTSC({
|
||||
'a.spec.ts': `
|
||||
@ -90,6 +141,16 @@ test('should work with default expect matchers', async ({ runTSC }) => {
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should work with expect message', async ({ runTSC }) => {
|
||||
const result = await runTSC({
|
||||
'a.spec.ts': `
|
||||
const { test } = pwt;
|
||||
test.expect(42, 'this is expect message').toBe(42);
|
||||
`
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
|
||||
test('should work with default expect matchers and esModuleInterop=false', async ({ runTSC }) => {
|
||||
const result = await runTSC({
|
||||
'a.spec.ts': `
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, countTimes, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, countTimes, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should handle fixture timeout', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -452,6 +452,6 @@ test('should not report fixture teardown error twice', async ({ runInlineTest })
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.output).toContain('Error: Oh my error');
|
||||
expect(stripAscii(result.output)).toContain(`throw new Error('Oh my error')`);
|
||||
expect(stripAnsi(result.output)).toContain(`throw new Error('Oh my error')`);
|
||||
expect(countTimes(result.output, 'Oh my error')).toBe(2);
|
||||
});
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
import colors from 'colors/safe';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
const files = {
|
||||
'helper.ts': `
|
||||
@ -91,7 +91,7 @@ test('should write detailed failure result to an output folder', async ({ runInl
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
const outputText = stripAscii(result.output);
|
||||
const outputText = stripAnsi(result.output);
|
||||
expect(outputText).toContain('Snapshot comparison failed:');
|
||||
const expectedSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-expected.txt');
|
||||
const actualSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-actual.txt');
|
||||
@ -114,7 +114,7 @@ test("doesn\'t create comparison artifacts in an output folder for passed negate
|
||||
});
|
||||
|
||||
expect(result.exitCode).toBe(0);
|
||||
const outputText = stripAscii(result.output);
|
||||
const outputText = stripAnsi(result.output);
|
||||
const expectedSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-expected.txt');
|
||||
const actualSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-actual.txt');
|
||||
expect(outputText).not.toContain(`Expected: ${expectedSnapshotArtifactPath}`);
|
||||
@ -406,7 +406,7 @@ test('should compare different PNG images', async ({ runInlineTest }, testInfo)
|
||||
`
|
||||
});
|
||||
|
||||
const outputText = stripAscii(result.output);
|
||||
const outputText = stripAnsi(result.output);
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(outputText).toContain('Snapshot comparison failed:');
|
||||
const expectedSnapshotArtifactPath = testInfo.outputPath('test-results', 'a-is-a-test', 'snapshot-expected.png');
|
||||
@ -636,7 +636,7 @@ test('should attach expected/actual/diff with snapshot path', async ({ runInline
|
||||
`
|
||||
});
|
||||
|
||||
const outputText = stripAscii(result.output);
|
||||
const outputText = stripAnsi(result.output);
|
||||
const attachments = outputText.split('\n').filter(l => l.startsWith('## ')).map(l => l.substring(3)).map(l => JSON.parse(l))[0];
|
||||
for (const attachment of attachments)
|
||||
attachment.path = attachment.path.replace(/\\/g, '/').replace(/.*test-results\//, '');
|
||||
@ -675,7 +675,7 @@ test('should attach expected/actual/diff', async ({ runInlineTest }, testInfo) =
|
||||
`
|
||||
});
|
||||
|
||||
const outputText = stripAscii(result.output);
|
||||
const outputText = stripAnsi(result.output);
|
||||
const attachments = outputText.split('\n').filter(l => l.startsWith('## ')).map(l => l.substring(3)).map(l => JSON.parse(l))[0];
|
||||
for (const attachment of attachments)
|
||||
attachment.path = attachment.path.replace(/\\/g, '/').replace(/.*test-results\//, '');
|
||||
@ -714,7 +714,7 @@ test('should attach expected/actual and no diff', async ({ runInlineTest }, test
|
||||
`
|
||||
});
|
||||
|
||||
const outputText = stripAscii(result.output);
|
||||
const outputText = stripAnsi(result.output);
|
||||
expect(outputText).toContain('Sizes differ; expected image 2px X 2px, but got 1px X 1px.');
|
||||
const attachments = outputText.split('\n').filter(l => l.startsWith('## ')).map(l => l.substring(3)).map(l => JSON.parse(l))[0];
|
||||
for (const attachment of attachments)
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('hooks should work with fixtures', async ({ runInlineTest }) => {
|
||||
const { results } = await runInlineTest({
|
||||
@ -546,8 +546,8 @@ test('uncaught error in beforeEach should not be masked by another error', async
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain('Expected: 2');
|
||||
expect(stripAscii(result.output)).toContain('Received: 1');
|
||||
expect(stripAnsi(result.output)).toContain('Expected: 2');
|
||||
expect(stripAnsi(result.output)).toContain('Received: 1');
|
||||
});
|
||||
|
||||
test('should report error from fixture teardown when beforeAll times out', async ({ runInlineTest }) => {
|
||||
@ -569,8 +569,8 @@ test('should report error from fixture teardown when beforeAll times out', async
|
||||
}, { timeout: 1000 });
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain('Timeout of 1000ms exceeded in beforeAll hook.');
|
||||
expect(stripAscii(result.output)).toContain('Error: Oh my!');
|
||||
expect(stripAnsi(result.output)).toContain('Timeout of 1000ms exceeded in beforeAll hook.');
|
||||
expect(stripAnsi(result.output)).toContain('Error: Oh my!');
|
||||
});
|
||||
|
||||
test('should not hang and report results when worker process suddenly exits during afterAll', async ({ runInlineTest }) => {
|
||||
@ -584,6 +584,6 @@ test('should not hang and report results when worker process suddenly exits duri
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(1);
|
||||
expect(result.output).toContain('Worker process exited unexpectedly');
|
||||
expect(stripAscii(result.output)).toContain('[1/1] a.spec.js:6:7 › passed');
|
||||
expect(stripAscii(result.output)).toContain('[1/1] a.spec.js:7:12 › afterAll');
|
||||
expect(stripAnsi(result.output)).toContain('[1/1] a.spec.js:6:7 › passed');
|
||||
expect(stripAnsi(result.output)).toContain('[1/1] a.spec.js:7:12 › afterAll');
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('max-failures should work', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -143,5 +143,5 @@ test('max-failures should properly shutdown', async ({ runInlineTest }) => {
|
||||
}, { workers: 1 });
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain('expect(false).toBeTruthy()');
|
||||
expect(stripAnsi(result.output)).toContain('expect(false).toBeTruthy()');
|
||||
});
|
||||
|
||||
@ -243,7 +243,7 @@ const TSCONFIG = {
|
||||
export { expect } from './stable-test-runner';
|
||||
|
||||
const asciiRegex = new RegExp('[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))', 'g');
|
||||
export function stripAscii(str: string): string {
|
||||
export function stripAnsi(str: string): string {
|
||||
return str.replace(asciiRegex, '');
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should support toHaveCount', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -80,7 +80,7 @@ test('should support toHaveCount', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(result.passed).toBe(5);
|
||||
expect(result.failed).toBe(2);
|
||||
expect(result.exitCode).toBe(1);
|
||||
@ -109,7 +109,7 @@ test('should support toHaveJSProperty', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('- "c"');
|
||||
expect(result.passed).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
@ -165,7 +165,7 @@ test('should support toHaveJSProperty with builtin types', async ({ runInlineTes
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(result.passed).toBe(3);
|
||||
expect(result.failed).toBe(3);
|
||||
expect(result.exitCode).toBe(1);
|
||||
@ -196,7 +196,7 @@ test('should support toHaveClass', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('expect(locator).toHaveClass');
|
||||
expect(output).toContain('Expected string: \"foo bar baz\"');
|
||||
expect(result.passed).toBe(1);
|
||||
@ -222,7 +222,7 @@ test('should support toHaveClass w/ array', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('expect(received).toHaveClass(expected)');
|
||||
expect(output).toContain('- /[a-z]az/,');
|
||||
expect(result.passed).toBe(1);
|
||||
@ -246,7 +246,7 @@ test('should support toHaveTitle', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('expect(page).toHaveTitle');
|
||||
expect(output).toContain('Expected string: \"Hello\"');
|
||||
expect(result.passed).toBe(1);
|
||||
@ -270,7 +270,7 @@ test('should support toHaveURL', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('expect(page).toHaveURL');
|
||||
expect(output).toContain('Expected string: \"wrong\"');
|
||||
expect(result.passed).toBe(1);
|
||||
@ -304,7 +304,7 @@ test('should support toHaveURL with baseURL from webServer', async ({ runInlineT
|
||||
};
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('expect(page).toHaveURL');
|
||||
expect(output).toContain(`Expected string: \"http://localhost:${port}/kek\"`);
|
||||
expect(result.passed).toBe(1);
|
||||
@ -327,7 +327,7 @@ test('should respect expect.timeout', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('expect(received).toHaveURL(expected)');
|
||||
expect(output).toContain('expect.toHaveURL with timeout 1000ms');
|
||||
expect(result.failed).toBe(1);
|
||||
@ -345,7 +345,7 @@ test('should log scale the time', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
const tokens = output.split('unexpected value');
|
||||
// Log scale: 0, 100, 250, 500, 1000, 1000, should be less than 8.
|
||||
expect(tokens.length).toBeGreaterThan(1);
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should support toHaveText w/ regex', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -37,7 +37,7 @@ test('should support toHaveText w/ regex', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Error: expect(received).toHaveText(expected)');
|
||||
expect(output).toContain('Expected pattern: /Text 2/');
|
||||
expect(output).toContain('Received string: "Text content"');
|
||||
@ -68,7 +68,7 @@ test('should support toContainText w/ regex', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Error: expect(received).toContainText(expected)');
|
||||
expect(output).toContain('Expected pattern: /ex2/');
|
||||
expect(output).toContain('Received string: "Text content"');
|
||||
@ -107,7 +107,7 @@ test('should support toHaveText w/ text', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Error: expect(received).toHaveText(expected)');
|
||||
expect(output).toContain('Expected string: "Text"');
|
||||
expect(output).toContain('Received string: "Text content"');
|
||||
@ -135,7 +135,7 @@ test('should support toHaveText w/ not', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Error: expect(received).not.toHaveText(expected)');
|
||||
expect(output).toContain('Expected string: not "Text content"');
|
||||
expect(output).toContain('Received string: "Text content');
|
||||
@ -208,7 +208,7 @@ test('should support toHaveText w/ array', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Error: expect(received).toHaveText(expected) // deep equality');
|
||||
expect(output).toContain('await expect(locator).toHaveText');
|
||||
expect(output).toContain('- "Extra"');
|
||||
@ -237,7 +237,7 @@ test('should support toContainText w/ array', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Error: expect(received).toContainText(expected)');
|
||||
expect(output).toContain('await expect(locator).toContainText');
|
||||
expect(output).toContain('- "Text 2"');
|
||||
@ -403,8 +403,8 @@ test('should print expected/received before timeout', async ({ runInlineTest })
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.output).toContain('Timeout of 2000ms exceeded.');
|
||||
expect(stripAscii(result.output)).toContain('Expected string: "Text 2"');
|
||||
expect(stripAscii(result.output)).toContain('Received string: "Text content"');
|
||||
expect(stripAnsi(result.output)).toContain('Expected string: "Text 2"');
|
||||
expect(stripAnsi(result.output)).toContain('Received string: "Text content"');
|
||||
});
|
||||
|
||||
test('should print nice error for toHaveText', async ({ runInlineTest }) => {
|
||||
@ -420,7 +420,7 @@ test('should print nice error for toHaveText', async ({ runInlineTest }) => {
|
||||
}, { workers: 1, timeout: 2000 });
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.exitCode).toBe(1);
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Pending operations:');
|
||||
expect(output).toContain('Error: expect(received).toHaveText(expected)');
|
||||
expect(output).toContain('Expected string: "Text"');
|
||||
@ -447,8 +447,8 @@ test('should print expected/received on Ctrl+C', async ({ runInlineTest }) => {
|
||||
expect(result.exitCode).toBe(130);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.skipped).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain('Expected string: "Text 2"');
|
||||
expect(stripAscii(result.output)).toContain('Received string: "Text content"');
|
||||
expect(stripAnsi(result.output)).toContain('Expected string: "Text 2"');
|
||||
expect(stripAnsi(result.output)).toContain('Received string: "Text content"');
|
||||
});
|
||||
|
||||
test('should support not.toHaveText when selector does not match', async ({ runInlineTest }) => {
|
||||
@ -465,7 +465,7 @@ test('should support not.toHaveText when selector does not match', async ({ runI
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.failed).toBe(1);
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Expected string: not "hello"');
|
||||
expect(output).toContain('Received string: ""');
|
||||
expect(output).toContain('waiting for selector "span"');
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should support toBeChecked', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -46,7 +46,7 @@ test('should support toBeChecked', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('Error: expect(received).toBeChecked()');
|
||||
expect(output).toContain('expect(locator).toBeChecked');
|
||||
expect(result.passed).toBe(3);
|
||||
@ -90,7 +90,7 @@ test('should support toBeChecked w/ not', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { workers: 1 });
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(result.passed).toBe(2);
|
||||
expect(result.failed).toBe(3);
|
||||
expect(result.exitCode).toBe(1);
|
||||
@ -172,7 +172,7 @@ test('should support toBeEditable, toBeEnabled, toBeDisabled, toBeEmpty', async
|
||||
expect(result.passed).toBe(8);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.exitCode).toBe(1);
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(output).toContain('expect(locator).toBeEnabled({ timeout: 500 }');
|
||||
});
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { spawnSync } from 'child_process';
|
||||
@ -306,7 +306,7 @@ test('should report error and pending operations on timeout', async ({ runInline
|
||||
expect(result.output).toContain('- page.click at a.test.ts:9:16');
|
||||
expect(result.output).toContain('- page.textContent at a.test.ts:10:16');
|
||||
expect(result.output).toContain('waiting for selector');
|
||||
expect(stripAscii(result.output)).toContain(`10 | page.textContent('text=More missing'),`);
|
||||
expect(stripAnsi(result.output)).toContain(`10 | page.textContent('text=More missing'),`);
|
||||
});
|
||||
|
||||
test('should report error on timeout with shared page', async ({ runInlineTest }, testInfo) => {
|
||||
@ -330,7 +330,7 @@ test('should report error on timeout with shared page', async ({ runInlineTest }
|
||||
expect(result.passed).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.output).toContain('waiting for selector "text=Missing"');
|
||||
expect(stripAscii(result.output)).toContain(`14 | await page.click('text=Missing');`);
|
||||
expect(stripAnsi(result.output)).toContain(`14 | await page.click('text=Missing');`);
|
||||
});
|
||||
|
||||
test('should report error and pending operations from beforeAll timeout', async ({ runInlineTest }, testInfo) => {
|
||||
@ -357,7 +357,7 @@ test('should report error and pending operations from beforeAll timeout', async
|
||||
expect(result.output).toContain('- page.click at a.test.ts:10:16');
|
||||
expect(result.output).toContain('- page.textContent at a.test.ts:11:16');
|
||||
expect(result.output).toContain('waiting for selector');
|
||||
expect(stripAscii(result.output)).toContain(`11 | page.textContent('text=More missing'),`);
|
||||
expect(stripAnsi(result.output)).toContain(`11 | page.textContent('text=More missing'),`);
|
||||
});
|
||||
|
||||
test('should not report waitForEventInfo as pending', async ({ runInlineTest }, testInfo) => {
|
||||
@ -417,7 +417,7 @@ test('should report click error on sigint', async ({ runInlineTest }) => {
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.failed).toBe(0);
|
||||
expect(result.skipped).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain(`8 | const promise = page.click('text=Missing');`);
|
||||
expect(stripAnsi(result.output)).toContain(`8 | const promise = page.click('text=Missing');`);
|
||||
});
|
||||
|
||||
test('should work with video: retain-on-failure', async ({ runInlineTest }, testInfo) => {
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
import { ZipFileSystem } from '../../packages/playwright-core/lib/utils/vfs';
|
||||
import fs from 'fs';
|
||||
|
||||
@ -108,8 +108,8 @@ test('should not throw with trace and timeouts', async ({ runInlineTest }, testI
|
||||
}, { workers: 2 });
|
||||
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(stripAscii(result.output)).not.toContain('tracing.stopChunk:');
|
||||
expect(stripAscii(result.output)).not.toContain('tracing.stop:');
|
||||
expect(stripAnsi(result.output)).not.toContain('tracing.stopChunk:');
|
||||
expect(stripAnsi(result.output)).not.toContain('tracing.stop:');
|
||||
});
|
||||
|
||||
test('should save sources when requested', async ({ runInlineTest }, testInfo) => {
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('render text attachment', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -30,7 +30,7 @@ test('render text attachment', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'line' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
expect(text).toContain(' attachment #1: attachment (text/plain) ---------------------------------------------------------');
|
||||
expect(text).toContain(' Hello world');
|
||||
expect(text).toContain(' ------------------------------------------------------------------------------------------------');
|
||||
@ -51,7 +51,7 @@ test('render screenshot attachment', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'line' });
|
||||
const text = stripAscii(result.output).replace(/\\/g, '/');
|
||||
const text = stripAnsi(result.output).replace(/\\/g, '/');
|
||||
expect(text).toContain(' attachment #1: screenshot (image/png) ----------------------------------------------------------');
|
||||
expect(text).toContain(' test-results/a-one/some/path.png');
|
||||
expect(text).toContain(' ------------------------------------------------------------------------------------------------');
|
||||
@ -72,7 +72,7 @@ test('render trace attachment', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'line' });
|
||||
const text = stripAscii(result.output).replace(/\\/g, '/');
|
||||
const text = stripAnsi(result.output).replace(/\\/g, '/');
|
||||
expect(text).toContain(' attachment #1: trace (application/zip) ---------------------------------------------------------');
|
||||
expect(text).toContain(' test-results/a-one/trace.zip');
|
||||
expect(text).toContain('npx playwright show-trace test-results/a-one/trace.zip');
|
||||
@ -95,7 +95,7 @@ test(`testInfo.attach errors`, async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'line', workers: 1 });
|
||||
const text = stripAscii(result.output).replace(/\\/g, '/');
|
||||
const text = stripAnsi(result.output).replace(/\\/g, '/');
|
||||
expect(text).toMatch(/Error: ENOENT: no such file or directory, copyfile '.*foo.txt.*'/);
|
||||
expect(text).toContain(`Exactly one of "path" and "body" must be specified`);
|
||||
expect(result.passed).toBe(0);
|
||||
@ -112,7 +112,7 @@ test(`testInfo.attach errors with empty path`, async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'line', workers: 1 });
|
||||
expect(stripAscii(result.output)).toMatch(/Error: ENOENT: no such file or directory, copyfile ''/);
|
||||
expect(stripAnsi(result.output)).toMatch(/Error: ENOENT: no such file or directory, copyfile ''/);
|
||||
expect(result.exitCode).toBe(1);
|
||||
});
|
||||
|
||||
@ -129,7 +129,7 @@ test(`testInfo.attach error in fixture`, async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'line', workers: 1 });
|
||||
const text = stripAscii(result.output).replace(/\\/g, '/');
|
||||
const text = stripAnsi(result.output).replace(/\\/g, '/');
|
||||
expect(text).toMatch(/Error: ENOENT: no such file or directory, copyfile '.*foo.txt.*'/);
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(0);
|
||||
@ -154,7 +154,7 @@ test(`testInfo.attach success in fixture`, async ({ runInlineTest }) => {
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain('attachment #1: name (text/plain)');
|
||||
expect(stripAnsi(result.output)).toContain('attachment #1: name (text/plain)');
|
||||
});
|
||||
|
||||
test(`testInfo.attach allow empty string body`, async ({ runInlineTest }) => {
|
||||
@ -169,7 +169,7 @@ test(`testInfo.attach allow empty string body`, async ({ runInlineTest }) => {
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output)).toMatch(/^.*attachment #1: name \(text\/plain\).*\n.*\n.*------/gm);
|
||||
expect(stripAnsi(result.output)).toMatch(/^.*attachment #1: name \(text\/plain\).*\n.*\n.*------/gm);
|
||||
});
|
||||
|
||||
test(`testInfo.attach allow empty buffer body`, async ({ runInlineTest }) => {
|
||||
@ -184,5 +184,5 @@ test(`testInfo.attach allow empty buffer body`, async ({ runInlineTest }) => {
|
||||
});
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output)).toMatch(/^.*attachment #1: name \(text\/plain\).*\n.*\n.*------/gm);
|
||||
expect(stripAnsi(result.output)).toMatch(/^.*attachment #1: name \(text\/plain\).*\n.*\n.*------/gm);
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
import * as path from 'path';
|
||||
|
||||
test('handle long test names', async ({ runInlineTest }) => {
|
||||
@ -27,7 +27,7 @@ test('handle long test names', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
});
|
||||
expect(stripAscii(result.output)).toContain('expect(1).toBe');
|
||||
expect(stripAnsi(result.output)).toContain('expect(1).toBe');
|
||||
expect(result.exitCode).toBe(1);
|
||||
});
|
||||
|
||||
@ -138,15 +138,15 @@ test('should print slow tests', async ({ runInlineTest }) => {
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(8);
|
||||
expect(stripAscii(result.output)).toContain(`Slow test file: [foo] › dir${path.sep}a.test.js (`);
|
||||
expect(stripAscii(result.output)).toContain(`Slow test file: [bar] › dir${path.sep}a.test.js (`);
|
||||
expect(stripAscii(result.output)).toContain(`Slow test file: [baz] › dir${path.sep}a.test.js (`);
|
||||
expect(stripAscii(result.output)).toContain(`Slow test file: [qux] › dir${path.sep}a.test.js (`);
|
||||
expect(stripAscii(result.output)).toContain(`Consider splitting slow test files to speed up parallel execution`);
|
||||
expect(stripAscii(result.output)).not.toContain(`Slow test file: [foo] › dir${path.sep}b.test.js (`);
|
||||
expect(stripAscii(result.output)).not.toContain(`Slow test file: [bar] › dir${path.sep}b.test.js (`);
|
||||
expect(stripAscii(result.output)).not.toContain(`Slow test file: [baz] › dir${path.sep}b.test.js (`);
|
||||
expect(stripAscii(result.output)).not.toContain(`Slow test file: [qux] › dir${path.sep}b.test.js (`);
|
||||
expect(stripAnsi(result.output)).toContain(`Slow test file: [foo] › dir${path.sep}a.test.js (`);
|
||||
expect(stripAnsi(result.output)).toContain(`Slow test file: [bar] › dir${path.sep}a.test.js (`);
|
||||
expect(stripAnsi(result.output)).toContain(`Slow test file: [baz] › dir${path.sep}a.test.js (`);
|
||||
expect(stripAnsi(result.output)).toContain(`Slow test file: [qux] › dir${path.sep}a.test.js (`);
|
||||
expect(stripAnsi(result.output)).toContain(`Consider splitting slow test files to speed up parallel execution`);
|
||||
expect(stripAnsi(result.output)).not.toContain(`Slow test file: [foo] › dir${path.sep}b.test.js (`);
|
||||
expect(stripAnsi(result.output)).not.toContain(`Slow test file: [bar] › dir${path.sep}b.test.js (`);
|
||||
expect(stripAnsi(result.output)).not.toContain(`Slow test file: [baz] › dir${path.sep}b.test.js (`);
|
||||
expect(stripAnsi(result.output)).not.toContain(`Slow test file: [qux] › dir${path.sep}b.test.js (`);
|
||||
});
|
||||
|
||||
test('should not print slow tests', async ({ runInlineTest }) => {
|
||||
@ -172,7 +172,7 @@ test('should not print slow tests', async ({ runInlineTest }) => {
|
||||
});
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(4);
|
||||
expect(stripAscii(result.output)).not.toContain('Slow test');
|
||||
expect(stripAnsi(result.output)).not.toContain('Slow test');
|
||||
});
|
||||
|
||||
test('should print flaky failures', async ({ runInlineTest }) => {
|
||||
@ -186,7 +186,7 @@ test('should print flaky failures', async ({ runInlineTest }) => {
|
||||
}, { retries: '1', reporter: 'list' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.flaky).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain('expect(testInfo.retry).toBe(1)');
|
||||
expect(stripAnsi(result.output)).toContain('expect(testInfo.retry).toBe(1)');
|
||||
});
|
||||
|
||||
test('should print flaky timeouts', async ({ runInlineTest }) => {
|
||||
@ -201,7 +201,7 @@ test('should print flaky timeouts', async ({ runInlineTest }) => {
|
||||
}, { retries: '1', reporter: 'list', timeout: '1000' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.flaky).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain('Timeout of 1000ms exceeded.');
|
||||
expect(stripAnsi(result.output)).toContain('Timeout of 1000ms exceeded.');
|
||||
});
|
||||
|
||||
test('should print stack-less errors', async ({ runInlineTest }) => {
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import colors from 'colors/safe';
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('render expected', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -52,7 +52,7 @@ test('render unexpected after retry', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { retries: 3 });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
expect(text).toContain('×××F');
|
||||
expect(result.output).toContain(colors.red('F'));
|
||||
expect(result.exitCode).toBe(1);
|
||||
@ -67,7 +67,7 @@ test('render flaky', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { retries: 3 });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
expect(text).toContain('×××±');
|
||||
expect(result.output).toContain(colors.yellow('±'));
|
||||
expect(text).toContain('1 flaky');
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
import path from 'path';
|
||||
|
||||
function relativeFilePath(file: string): string {
|
||||
@ -32,7 +32,7 @@ test('print GitHub annotations for success', async ({ runInlineTest }) => {
|
||||
});
|
||||
`
|
||||
}, { reporter: 'github' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
expect(text).not.toContain('::error');
|
||||
expect(text).toContain('::notice title=🎭 Playwright Run Summary:: 1 passed');
|
||||
expect(result.exitCode).toBe(0);
|
||||
@ -47,7 +47,7 @@ test('print GitHub annotations for failed tests', async ({ runInlineTest }, test
|
||||
});
|
||||
`
|
||||
}, { retries: 3, reporter: 'github' }, { GITHUB_WORKSPACE: process.cwd() });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
const testPath = relativeFilePath(testInfo.outputPath('a.test.js'));
|
||||
expect(text).toContain(`::error file=${testPath},title=a.test.js:6:7 › example,line=7,col=23:: 1) a.test.js:6:7 › example =======================================================================%0A%0A Retry #1`);
|
||||
expect(text).toContain(`::error file=${testPath},title=a.test.js:6:7 › example,line=7,col=23:: 1) a.test.js:6:7 › example =======================================================================%0A%0A Retry #2`);
|
||||
@ -69,7 +69,7 @@ test('print GitHub annotations for slow tests', async ({ runInlineTest }) => {
|
||||
});
|
||||
`
|
||||
}, { retries: 3, reporter: 'github' }, { GITHUB_WORKSPACE: '' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
expect(text).toContain('::warning title=Slow Test,file=a.test.js::a.test.js took');
|
||||
expect(text).toContain('::notice title=🎭 Playwright Run Summary:: 1 passed');
|
||||
expect(result.exitCode).toBe(0);
|
||||
@ -88,7 +88,7 @@ test('print GitHub annotations for global error', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'github' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
expect(text).toContain('::error ::%0AError: Oh my!%0A%0A');
|
||||
expect(result.exitCode).toBe(1);
|
||||
});
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should support spec.ok', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -215,6 +215,6 @@ test('should add dot in addition to file json', async ({ runInlineTest }, testIn
|
||||
`,
|
||||
}, { reporter: '' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(stripAscii(result.output)).toContain('·');
|
||||
expect(stripAnsi(result.output)).toContain('·');
|
||||
expect(fs.existsSync(testInfo.outputPath('a.json'))).toBeTruthy();
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('render unexpected after retry', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -25,7 +25,7 @@ test('render unexpected after retry', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { retries: 3, reporter: 'line' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
expect(text).toContain('[1/1] a.test.js:6:7 › one');
|
||||
expect(text).toContain('[2/1] (retries) a.test.js:6:7 › one (retry #1)');
|
||||
expect(text).toContain('[3/1] (retries) a.test.js:6:7 › one (retry #2)');
|
||||
@ -48,7 +48,7 @@ test('render flaky', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { retries: 3, reporter: 'line' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
expect(text).toContain('1 flaky');
|
||||
expect(result.exitCode).toBe(0);
|
||||
});
|
||||
@ -64,5 +64,5 @@ test('should print flaky failures', async ({ runInlineTest }) => {
|
||||
}, { retries: '1', reporter: 'line' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.flaky).toBe(1);
|
||||
expect(stripAscii(result.output)).toContain('expect(testInfo.retry).toBe(1)');
|
||||
expect(stripAnsi(result.output)).toContain('expect(testInfo.retry).toBe(1)');
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('render each test with project name', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -36,7 +36,7 @@ test('render each test with project name', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'list' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
const positiveStatusMarkPrefix = process.platform === 'win32' ? 'ok' : '✓ ';
|
||||
const negativateStatusMarkPrefix = process.platform === 'win32' ? 'x ' : '✘ ';
|
||||
expect(text).toContain(`${negativateStatusMarkPrefix} [foo] › a.test.ts:6:7 › fails`);
|
||||
@ -64,7 +64,7 @@ test('render steps', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'list' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
const lines = text.split('\n').filter(l => l.startsWith('0 :'));
|
||||
lines.pop(); // Remove last item that contains [v] and time in ms.
|
||||
expect(lines).toEqual([
|
||||
@ -92,7 +92,7 @@ test('render retries', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'list', retries: '1' });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
const lines = text.split('\n').filter(l => l.startsWith('0 :') || l.startsWith('1 :')).map(l => l.replace(/[\dm]+s/, 'XXms'));
|
||||
const positiveStatusMarkPrefix = process.platform === 'win32' ? 'ok' : '✓ ';
|
||||
const negativateStatusMarkPrefix = process.platform === 'win32' ? 'x ' : '✘ ';
|
||||
@ -122,7 +122,7 @@ test('should truncate long test names', async ({ runInlineTest }) => {
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'list', retries: 0 }, { PWTEST_TTY_WIDTH: 50, PWTEST_SKIP_TEST_OUTPUT: undefined });
|
||||
const text = stripAscii(result.output);
|
||||
const text = stripAnsi(result.output);
|
||||
const positiveStatusMarkPrefix = process.platform === 'win32' ? 'ok' : '✓ ';
|
||||
const negativateStatusMarkPrefix = process.platform === 'win32' ? 'x ' : '✘ ';
|
||||
expect(text).toContain(`${negativateStatusMarkPrefix} [foo] › a.test.ts:6:7 › fails very`);
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should retry failures', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -72,7 +72,7 @@ test('should retry timeout', async ({ runInlineTest }) => {
|
||||
expect(exitCode).toBe(1);
|
||||
expect(passed).toBe(0);
|
||||
expect(failed).toBe(1);
|
||||
expect(stripAscii(output).split('\n')[2]).toBe('××T');
|
||||
expect(stripAnsi(output).split('\n')[2]).toBe('××T');
|
||||
});
|
||||
|
||||
test('should fail on unexpected pass with retries', async ({ runInlineTest }) => {
|
||||
@ -103,7 +103,7 @@ test('should retry unexpected pass', async ({ runInlineTest }) => {
|
||||
expect(exitCode).toBe(1);
|
||||
expect(passed).toBe(0);
|
||||
expect(failed).toBe(1);
|
||||
expect(stripAscii(output).split('\n')[2]).toBe('××F');
|
||||
expect(stripAnsi(output).split('\n')[2]).toBe('××F');
|
||||
});
|
||||
|
||||
test('should not retry expected failure', async ({ runInlineTest }) => {
|
||||
@ -123,7 +123,7 @@ test('should not retry expected failure', async ({ runInlineTest }) => {
|
||||
expect(exitCode).toBe(0);
|
||||
expect(passed).toBe(2);
|
||||
expect(failed).toBe(0);
|
||||
expect(stripAscii(output).split('\n')[2]).toBe('··');
|
||||
expect(stripAnsi(output).split('\n')[2]).toBe('··');
|
||||
});
|
||||
|
||||
test('should retry unhandled rejection', async ({ runInlineTest }) => {
|
||||
@ -141,7 +141,7 @@ test('should retry unhandled rejection', async ({ runInlineTest }) => {
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output).split('\n')[2]).toBe('××F');
|
||||
expect(stripAnsi(result.output).split('\n')[2]).toBe('××F');
|
||||
expect(result.output).toContain('Unhandled rejection');
|
||||
});
|
||||
|
||||
@ -162,7 +162,7 @@ test('should retry beforeAll failure', async ({ runInlineTest }) => {
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.skipped).toBe(1);
|
||||
expect(stripAscii(result.output).split('\n')[2]).toBe('×°×°F°');
|
||||
expect(stripAnsi(result.output).split('\n')[2]).toBe('×°×°F°');
|
||||
expect(result.output).toContain('BeforeAll is bugged!');
|
||||
});
|
||||
|
||||
@ -184,6 +184,6 @@ test('should retry worker fixture setup failure', async ({ runInlineTest }) => {
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(0);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output).split('\n')[2]).toBe('××F');
|
||||
expect(stripAnsi(result.output).split('\n')[2]).toBe('××F');
|
||||
expect(result.output).toContain('worker setup is bugged!');
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import path from 'path';
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('it should not allow multiple tests with the same name per suite', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -194,7 +194,7 @@ test('should not stall when workers are available', async ({ runInlineTest }) =>
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.passed).toBe(2);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(stripAscii(result.output).split('\n').filter(line => line.startsWith('%%'))).toEqual([
|
||||
expect(stripAnsi(result.output).split('\n').filter(line => line.startsWith('%%'))).toEqual([
|
||||
'%%fails-1-started',
|
||||
'%%passes-2-started',
|
||||
'%%fails-1-done',
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('should work directly', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -84,7 +84,7 @@ test('should throw outside test', async ({ runInlineTest }) => {
|
||||
test('test 1', async ({title}) => {});
|
||||
`,
|
||||
});
|
||||
const output = stripAscii(result.output);
|
||||
const output = stripAnsi(result.output);
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(output).toContain('test.info() can only be called while test is running');
|
||||
});
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { test, expect, stripAscii } from './playwright-test-fixtures';
|
||||
import { test, expect, stripAnsi } from './playwright-test-fixtures';
|
||||
|
||||
test('test modifiers should work', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
@ -331,5 +331,5 @@ test('modifier timeout should be reported', async ({ runInlineTest }) => {
|
||||
expect(result.exitCode).toBe(1);
|
||||
expect(result.failed).toBe(1);
|
||||
expect(result.output).toContain('Error: Timeout of 2000ms exceeded while running skip modifier');
|
||||
expect(stripAscii(result.output)).toContain('6 | test.skip(async () => new Promise(() => {}));');
|
||||
expect(stripAnsi(result.output)).toContain('6 | test.skip(async () => new Promise(() => {}));');
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user