mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
fix(list): avoid overwriting stdio logs from tests when writing status (#36219)
This commit is contained in:
parent
a02722a2f6
commit
6caf3442fc
@ -102,7 +102,7 @@ List report supports the following configuration options and environment variabl
|
||||
| Environment Variable Name | Reporter Config Option| Description | Default
|
||||
|---|---|---|---|
|
||||
| `PLAYWRIGHT_LIST_PRINT_STEPS` | `printSteps` | Whether to print each step on its own line. | `false`
|
||||
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. If a number is specified, it will also be used as the terminal width. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. Supports `true`, `1`, `false`, `0`, `[WIDTH]`, and `[WIDTH]x[HEIGHT]`. `[WIDTH]` and `[WIDTH]x[HEIGHT]` specifies the TTY dimensions. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
| `FORCE_COLOR` | | Whether to produce colored output. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
|
||||
|
||||
@ -140,7 +140,7 @@ Line report supports the following configuration options and environment variabl
|
||||
|
||||
| Environment Variable Name | Reporter Config Option| Description | Default
|
||||
|---|---|---|---|
|
||||
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. If a number is specified, it will also be used as the terminal width. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. Supports `true`, `1`, `false`, `0`, `[WIDTH]`, and `[WIDTH]x[HEIGHT]`. `[WIDTH]` and `[WIDTH]x[HEIGHT]` specifies the TTY dimensions. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
| `FORCE_COLOR` | | Whether to produce colored output. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
|
||||
|
||||
@ -182,7 +182,7 @@ Dot report supports the following configuration options and environment variable
|
||||
|
||||
| Environment Variable Name | Reporter Config Option| Description | Default
|
||||
|---|---|---|---|
|
||||
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. If a number is specified, it will also be used as the terminal width. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
| `PLAYWRIGHT_FORCE_TTY` | | Whether to produce output suitable for a live terminal. Supports `true`, `1`, `false`, `0`, `[WIDTH]`, and `[WIDTH]x[HEIGHT]`. `[WIDTH]` and `[WIDTH]x[HEIGHT]` specifies the TTY dimensions. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
| `FORCE_COLOR` | | Whether to produce colored output. | `true` when terminal is in TTY mode, `false` otherwise.
|
||||
|
||||
### HTML reporter
|
||||
|
||||
@ -58,23 +58,39 @@ export type Screen = {
|
||||
colors: Colors;
|
||||
isTTY: boolean;
|
||||
ttyWidth: number;
|
||||
ttyHeight: number;
|
||||
};
|
||||
|
||||
const DEFAULT_TTY_WIDTH = 100;
|
||||
const DEFAULT_TTY_HEIGHT = 40;
|
||||
|
||||
// Output goes to terminal.
|
||||
export const terminalScreen: Screen = (() => {
|
||||
let isTTY = !!process.stdout.isTTY;
|
||||
let ttyWidth = process.stdout.columns || 0;
|
||||
let ttyHeight = process.stdout.rows || 0;
|
||||
if (process.env.PLAYWRIGHT_FORCE_TTY === 'false' || process.env.PLAYWRIGHT_FORCE_TTY === '0') {
|
||||
isTTY = false;
|
||||
ttyWidth = 0;
|
||||
ttyHeight = 0;
|
||||
} else if (process.env.PLAYWRIGHT_FORCE_TTY === 'true' || process.env.PLAYWRIGHT_FORCE_TTY === '1') {
|
||||
isTTY = true;
|
||||
ttyWidth = process.stdout.columns || 100;
|
||||
ttyWidth = process.stdout.columns || DEFAULT_TTY_WIDTH;
|
||||
ttyHeight = process.stdout.rows || DEFAULT_TTY_HEIGHT;
|
||||
} else if (process.env.PLAYWRIGHT_FORCE_TTY) {
|
||||
isTTY = true;
|
||||
ttyWidth = +process.env.PLAYWRIGHT_FORCE_TTY;
|
||||
const sizeMatch = process.env.PLAYWRIGHT_FORCE_TTY.match(/^(\d+)x(\d+)$/);
|
||||
if (sizeMatch) {
|
||||
ttyWidth = +sizeMatch[1];
|
||||
ttyHeight = +sizeMatch[2];
|
||||
} else {
|
||||
ttyWidth = +process.env.PLAYWRIGHT_FORCE_TTY;
|
||||
ttyHeight = DEFAULT_TTY_HEIGHT;
|
||||
}
|
||||
if (isNaN(ttyWidth))
|
||||
ttyWidth = 100;
|
||||
ttyWidth = DEFAULT_TTY_WIDTH;
|
||||
if (isNaN(ttyHeight))
|
||||
ttyHeight = DEFAULT_TTY_HEIGHT;
|
||||
}
|
||||
|
||||
let useColors = isTTY;
|
||||
@ -89,6 +105,7 @@ export const terminalScreen: Screen = (() => {
|
||||
resolveFiles: 'cwd',
|
||||
isTTY,
|
||||
ttyWidth,
|
||||
ttyHeight,
|
||||
colors
|
||||
};
|
||||
})();
|
||||
@ -98,6 +115,7 @@ export const nonTerminalScreen: Screen = {
|
||||
colors: terminalScreen.colors,
|
||||
isTTY: false,
|
||||
ttyWidth: 0,
|
||||
ttyHeight: 0,
|
||||
resolveFiles: 'rootDir',
|
||||
};
|
||||
|
||||
@ -106,6 +124,7 @@ export const internalScreen: Screen = {
|
||||
colors: realColors,
|
||||
isTTY: false,
|
||||
ttyWidth: 0,
|
||||
ttyHeight: 0,
|
||||
resolveFiles: 'rootDir',
|
||||
};
|
||||
|
||||
|
||||
@ -102,7 +102,7 @@ class ListReporter extends TerminalReporter {
|
||||
const line = test.title + this.screen.colors.dim(stepSuffix(step));
|
||||
this._appendLine(line, prefix);
|
||||
} else {
|
||||
this._updateLine(this._testRows.get(test)!, this.screen.colors.dim(this.formatTestTitle(test, step)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
this._updateOrAppendLine(this._testRows, test, this.screen.colors.dim(this.formatTestTitle(test, step)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
}
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ class ListReporter extends TerminalReporter {
|
||||
const testIndex = this._resultIndex.get(result) || '';
|
||||
if (!this._printSteps) {
|
||||
if (this.screen.isTTY)
|
||||
this._updateLine(this._testRows.get(test)!, this.screen.colors.dim(this.formatTestTitle(test, step.parent)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
this._updateOrAppendLine(this._testRows, test, this.screen.colors.dim(this.formatTestTitle(test, step.parent)) + this._retrySuffix(result), this._testPrefix(testIndex, ''));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ class ListReporter extends TerminalReporter {
|
||||
text = title;
|
||||
text += this.screen.colors.dim(` (${milliseconds(step.duration)})`);
|
||||
|
||||
this._updateOrAppendLine(this._stepRows.get(step)!, text, prefix);
|
||||
this._updateOrAppendLine(this._stepRows, step, text, prefix);
|
||||
}
|
||||
|
||||
private _maybeWriteNewLine() {
|
||||
@ -196,14 +196,17 @@ class ListReporter extends TerminalReporter {
|
||||
text += this._retrySuffix(result) + this.screen.colors.dim(` (${milliseconds(result.duration)})`);
|
||||
}
|
||||
|
||||
this._updateOrAppendLine(this._testRows.get(test)!, text, prefix);
|
||||
this._updateOrAppendLine(this._testRows, test, text, prefix);
|
||||
}
|
||||
|
||||
private _updateOrAppendLine(row: number, text: string, prefix: string) {
|
||||
if (this.screen.isTTY) {
|
||||
private _updateOrAppendLine<T>(entityRowNumbers: Map<T, number>, entity: T, text: string, prefix: string) {
|
||||
const row = entityRowNumbers.get(entity);
|
||||
// Only update the line if we assume that the line is still on the screen
|
||||
if (row !== undefined && this.screen.isTTY && this._lastRow - row < this.screen.ttyHeight) {
|
||||
this._updateLine(row, text, prefix);
|
||||
} else {
|
||||
this._maybeWriteNewLine();
|
||||
entityRowNumbers.set(entity, this._lastRow);
|
||||
this._appendLine(text, prefix);
|
||||
}
|
||||
}
|
||||
|
||||
@ -303,6 +303,116 @@ for (const useIntermediateMergeReport of [false, true] as const) {
|
||||
for (let i = 0; i < expected.length; ++i)
|
||||
expect(lines[firstIndex + i]).toContain(expected[i]);
|
||||
});
|
||||
|
||||
test('should update test status row only when TTY has not scrolled', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('A', async ({}) => {
|
||||
for (let i = 0; i < 20; ++i) {
|
||||
console.log('line ' + i);
|
||||
}
|
||||
});
|
||||
|
||||
test('B', async ({}) => {
|
||||
// Go past end of the screen
|
||||
for (let i = 20; i < 60; ++i) {
|
||||
console.log('line ' + i);
|
||||
}
|
||||
|
||||
// Should create new line
|
||||
await test.step('First step', async () => {
|
||||
console.log('step 1');
|
||||
});
|
||||
|
||||
for (let i = 60; i < 80; ++i) {
|
||||
console.log('line ' + i);
|
||||
}
|
||||
|
||||
// Should update the new (not original) line
|
||||
await test.step('Second step', async () => {
|
||||
console.log('step 2');
|
||||
});
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_FORCE_TTY: '80' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(2);
|
||||
const expected = [
|
||||
'#0 : 1 a.test.ts:3:15 › A',
|
||||
];
|
||||
for (let i = 0; i < 20; ++i)
|
||||
expected.push(`line ${i}`);
|
||||
// Update to initial test status row
|
||||
expected.push(`#0 : ${POSITIVE_STATUS_MARK} 1 a.test.ts:3:15 › A`);
|
||||
expected.push(`#21 : 2 a.test.ts:9:15 › B`);
|
||||
for (let i = 20; i < 60; ++i)
|
||||
expected.push(`line ${i}`);
|
||||
expected.push(`#62 : 2 a.test.ts:9:15 › B › First step`);
|
||||
expected.push(`step 1`);
|
||||
expected.push(`#62 : 2 a.test.ts:9:15 › B`);
|
||||
for (let i = 60; i < 80; ++i)
|
||||
expected.push(`line ${i}`);
|
||||
expected.push(`#62 : 2 a.test.ts:9:15 › B › Second step`);
|
||||
expected.push(`step 2`);
|
||||
expected.push(`#62 : 2 a.test.ts:9:15 › B`);
|
||||
expected.push(`#62 : ${POSITIVE_STATUS_MARK} 2 a.test.ts:9:15 › B`);
|
||||
const lines = result.output.split('\n');
|
||||
const firstIndex = lines.indexOf(expected[0]);
|
||||
expect(firstIndex, 'first line should be there').not.toBe(-1);
|
||||
for (let i = 0; i < expected.length; ++i)
|
||||
expect(lines[firstIndex + i]).toContain(expected[i]);
|
||||
});
|
||||
|
||||
test('should update test status row only within configured TTY height', async ({ runInlineTest }) => {
|
||||
const result = await runInlineTest({
|
||||
'a.test.ts': `
|
||||
import { test, expect } from '@playwright/test';
|
||||
test('A', async ({}) => {
|
||||
// No scroll
|
||||
for (let i = 0; i < 60; ++i) {
|
||||
console.log('line ' + i);
|
||||
}
|
||||
|
||||
// Update original line
|
||||
await test.step('First step', async () => {
|
||||
console.log('step 1');
|
||||
});
|
||||
|
||||
for (let i = 60; i < 120; ++i) {
|
||||
console.log('line ' + i);
|
||||
}
|
||||
|
||||
// Should create new line
|
||||
await test.step('Second step', async () => {
|
||||
console.log('step 2');
|
||||
});
|
||||
});
|
||||
`,
|
||||
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_FORCE_TTY: '80x80' });
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.passed).toBe(1);
|
||||
const expected = [
|
||||
'#0 : 1 a.test.ts:3:15 › A',
|
||||
];
|
||||
for (let i = 0; i < 60; ++i)
|
||||
expected.push(`line ${i}`);
|
||||
// Update to initial test status row
|
||||
expected.push(`#0 : 1 a.test.ts:3:15 › A › First step`);
|
||||
expected.push(`step 1`);
|
||||
expected.push(`#0 : 1 a.test.ts:3:15 › A`);
|
||||
for (let i = 60; i < 120; ++i)
|
||||
expected.push(`line ${i}`);
|
||||
expected.push(`#122 : 1 a.test.ts:3:15 › A › Second step`);
|
||||
expected.push(`step 2`);
|
||||
expected.push(`#122 : 1 a.test.ts:3:15 › A`);
|
||||
expected.push(`#122 : ${POSITIVE_STATUS_MARK} 1 a.test.ts:3:15 › A`);
|
||||
const lines = result.output.split('\n');
|
||||
const firstIndex = lines.indexOf(expected[0]);
|
||||
expect(firstIndex, 'first line should be there').not.toBe(-1);
|
||||
for (let i = 0; i < expected.length; ++i)
|
||||
expect(lines[firstIndex + i]).toContain(expected[i]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user