Skip to content

Commit 89cdf3d

Browse files
authored
feat: env.PLAYWRIGHT_FORCE_TTY to control reporters' tty (#30834)
Previously, terminal reporters consulted `process.stdout.isTTY`. Now it is possible to control the tty behavior: - `PLAYWRIGHT_FORCE_TTY=0` or `PLAYWRIGHT_FORCE_TTY=false` to disable TTY; - `PLAYWRIGHT_FORCE_TTY=1` or `PLAYWRIGHT_FORCE_TTY=true` to enable TTY, defaults to 100 columns when real columns are unavailable; - `PLAYWRIGHT_FORCE_TTY=<number>` to force enable TTY and set the columns. Fixes #29422.
1 parent 3370f37 commit 89cdf3d

File tree

4 files changed

+43
-31
lines changed

4 files changed

+43
-31
lines changed

packages/playwright/src/reporters/base.ts

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,41 @@ type TestSummary = {
4545
fatalErrors: TestError[];
4646
};
4747

48-
export const isTTY = !!process.env.PWTEST_TTY_WIDTH || process.stdout.isTTY;
49-
export const ttyWidth = process.env.PWTEST_TTY_WIDTH ? parseInt(process.env.PWTEST_TTY_WIDTH, 10) : process.stdout.columns || 0;
50-
let useColors = isTTY;
51-
if (process.env.DEBUG_COLORS === '0'
52-
|| process.env.DEBUG_COLORS === 'false'
53-
|| process.env.FORCE_COLOR === '0'
54-
|| process.env.FORCE_COLOR === 'false')
55-
useColors = false;
56-
else if (process.env.DEBUG_COLORS || process.env.FORCE_COLOR)
57-
useColors = true;
58-
59-
export const colors = useColors ? realColors : {
60-
bold: (t: string) => t,
61-
cyan: (t: string) => t,
62-
dim: (t: string) => t,
63-
gray: (t: string) => t,
64-
green: (t: string) => t,
65-
red: (t: string) => t,
66-
yellow: (t: string) => t,
67-
enabled: false,
68-
};
48+
export const { isTTY, ttyWidth, colors } = (() => {
49+
let isTTY = !!process.stdout.isTTY;
50+
let ttyWidth = process.stdout.columns || 0;
51+
if (process.env.PLAYWRIGHT_FORCE_TTY === 'false' || process.env.PLAYWRIGHT_FORCE_TTY === '0') {
52+
isTTY = false;
53+
ttyWidth = 0;
54+
} else if (process.env.PLAYWRIGHT_FORCE_TTY === 'true' || process.env.PLAYWRIGHT_FORCE_TTY === '1') {
55+
isTTY = true;
56+
ttyWidth = process.stdout.columns || 100;
57+
} else if (process.env.PLAYWRIGHT_FORCE_TTY) {
58+
isTTY = true;
59+
ttyWidth = +process.env.PLAYWRIGHT_FORCE_TTY;
60+
if (isNaN(ttyWidth))
61+
ttyWidth = 100;
62+
}
63+
64+
let useColors = isTTY;
65+
if (process.env.DEBUG_COLORS === '0' || process.env.DEBUG_COLORS === 'false' ||
66+
process.env.FORCE_COLOR === '0' || process.env.FORCE_COLOR === 'false')
67+
useColors = false;
68+
else if (process.env.DEBUG_COLORS || process.env.FORCE_COLOR)
69+
useColors = true;
70+
71+
const colors = useColors ? realColors : {
72+
bold: (t: string) => t,
73+
cyan: (t: string) => t,
74+
dim: (t: string) => t,
75+
gray: (t: string) => t,
76+
green: (t: string) => t,
77+
red: (t: string) => t,
78+
yellow: (t: string) => t,
79+
enabled: false,
80+
};
81+
return { isTTY, ttyWidth, colors };
82+
})();
6983

7084
export class BaseReporter implements ReporterV2 {
7185
config!: FullConfig;

packages/playwright/src/reporters/list.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,8 +229,6 @@ class ListReporter extends BaseReporter {
229229
// Go down if needed.
230230
if (row !== this._lastRow)
231231
process.stdout.write(`\u001B[${this._lastRow - row}E`);
232-
if (process.env.PWTEST_TTY_WIDTH)
233-
process.stdout.write('\n'); // For testing.
234232
}
235233

236234
private _testPrefix(index: string, statusMark: string) {

tests/playwright-test/reporter-blob.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ test('merge into list report by default', async ({ runInlineTest, mergeReports }
441441
const reportFiles = await fs.promises.readdir(reportDir);
442442
reportFiles.sort();
443443
expect(reportFiles).toEqual(['report-1.zip', 'report-2.zip', 'report-3.zip']);
444-
const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PWTEST_TTY_WIDTH: '80' }, { additionalArgs: ['--reporter', 'list'] });
444+
const { exitCode, output } = await mergeReports(reportDir, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PLAYWRIGHT_FORCE_TTY: '80' }, { additionalArgs: ['--reporter', 'list'] });
445445
expect(exitCode).toBe(0);
446446

447447
const text = stripAnsi(output);

tests/playwright-test/reporter-list.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { test, expect } from './playwright-test-fixtures';
17+
import { test, expect, stripAnsi } from './playwright-test-fixtures';
1818

1919
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === 'win32' && process.env.TERM_PROGRAM !== 'vscode' && !process.env.WT_SESSION;
2020
const POSITIVE_STATUS_MARK = DOES_NOT_SUPPORT_UTF8_IN_TERMINAL ? 'ok' : '✓ ';
@@ -70,7 +70,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
7070
});
7171
});
7272
`,
73-
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PWTEST_TTY_WIDTH: '80' });
73+
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PW_TEST_DEBUG_REPORTERS_PRINT_STEPS: '1', PLAYWRIGHT_FORCE_TTY: '80' });
7474
const text = result.output;
7575
const lines = text.split('\n').filter(l => l.match(/^\d :/)).map(l => l.replace(/[.\d]+m?s/, 'Xms'));
7676
lines.pop(); // Remove last item that contains [v] and time in ms.
@@ -105,7 +105,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
105105
await test.step('inner 2.2', async () => {});
106106
});
107107
});`,
108-
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PWTEST_TTY_WIDTH: '80' });
108+
}, { reporter: 'list' }, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_FORCE_TTY: '80' });
109109
const text = result.output;
110110
const lines = text.split('\n').filter(l => l.match(/^\d :/)).map(l => l.replace(/[.\d]+m?s/, 'Xms'));
111111
lines.pop(); // Remove last item that contains [v] and time in ms.
@@ -135,7 +135,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
135135
console.log('a'.repeat(80) + 'b'.repeat(20));
136136
});
137137
`,
138-
}, { reporter: 'list' }, { PWTEST_TTY_WIDTH: TTY_WIDTH + '' });
138+
}, { reporter: 'list' }, { PLAYWRIGHT_FORCE_TTY: TTY_WIDTH + '' });
139139

140140
const renderedText = simpleAnsiRenderer(result.rawOutput, TTY_WIDTH);
141141
if (process.platform === 'win32')
@@ -154,7 +154,7 @@ for (const useIntermediateMergeReport of [false, true] as const) {
154154
expect(testInfo.retry).toBe(1);
155155
});
156156
`,
157-
}, { reporter: 'list', retries: '1' }, { PW_TEST_DEBUG_REPORTERS: '1', PWTEST_TTY_WIDTH: '80' });
157+
}, { reporter: 'list', retries: '1' }, { PW_TEST_DEBUG_REPORTERS: '1', PLAYWRIGHT_FORCE_TTY: '80' });
158158
const text = result.output;
159159
const lines = text.split('\n').filter(l => l.startsWith('0 :') || l.startsWith('1 :')).map(l => l.replace(/\d+(\.\d+)?m?s/, 'XXms'));
160160

@@ -185,10 +185,10 @@ for (const useIntermediateMergeReport of [false, true] as const) {
185185
test.skip('skipped very long name', async () => {
186186
});
187187
`,
188-
}, { reporter: 'list', retries: 0 }, { PWTEST_TTY_WIDTH: '50' });
188+
}, { reporter: 'list', retries: 0 }, { PLAYWRIGHT_FORCE_TTY: '50' });
189189
expect(result.exitCode).toBe(1);
190190

191-
const lines = result.output.split('\n').slice(3, 11);
191+
const lines = result.rawOutput.split('\n').map(line => line.split('\x1B[22m\x1B[1E')).flat().map(line => stripAnsi(line)).filter(line => line.trim()).slice(1, 9);
192192
expect(lines.every(line => line.length <= 50)).toBe(true);
193193

194194
expect(lines[0]).toBe(` 1 …a.test.ts:3:15 › failure in very long name`);

0 commit comments

Comments
 (0)