playwright-bdd
Version:
BDD Testing with Playwright runner
72 lines (62 loc) • 2.42 kB
text/typescript
import fs from 'node:fs';
import path from 'node:path';
import { TestInfoError } from '@playwright/test';
import { stripAnsiEscapes } from '../utils/stripAnsiEscapes';
import { parseStackFrame } from '../playwright/stackTrace';
const filesCache = new Map<string, string>();
/**
* Get code snippet by the error stack.
* See: https://github.com/microsoft/playwright/blob/release-1.49/packages/playwright/src/reporters/internalReporter.ts#L115
*
* Some downside here is that it's done in a test worker, not in the reporter process.
*/
export function getCodeSnippet(error: TestInfoError) {
const location = getErrorLocation(error);
if (!location?.file || !location.line) return;
const source = getFileContent(location.file);
const lines = source.split('\n');
const startLine = Math.max(0, location.line - 3);
const snippetLines = lines.slice(startLine, location.line + 4);
return trimEmptyLines(snippetLines).join('\n');
}
/**
* Extracts location of the first user's file in error stack trace.
* See: https://github.com/microsoft/playwright/blob/release-1.49/packages/playwright/src/reporters/base.ts#L466
*/
// eslint-disable-next-line visual/complexity
function getErrorLocation(error: TestInfoError) {
const lines = stripAnsiEscapes(error.stack || '').split('\n');
let firstStackLine = lines.findIndex((line) => line.startsWith(' at '));
if (firstStackLine === -1) firstStackLine = lines.length;
const stackLines = lines.slice(firstStackLine);
for (const line of stackLines) {
const frame = parseStackFrame(line, path.sep, false);
if (!frame || !frame.file || belongsToNodeModules(frame.file)) continue;
return { file: frame.file, column: frame.column || 0, line: frame.line || 0 };
}
}
function getFileContent(file: string) {
let content = filesCache.get(file);
if (content === undefined) {
content = readFileSyncSafe(file);
filesCache.set(file, content);
}
return content;
}
function readFileSyncSafe(file: string) {
try {
return fs.readFileSync(file, 'utf8');
} catch {
return '';
}
}
function belongsToNodeModules(file: string) {
return file.includes(`${path.sep}node_modules${path.sep}`);
}
function trimEmptyLines(lines: string[]) {
let start = 0;
let end = lines.length - 1;
while (start <= end && !lines[start].trim()) start++;
while (end >= start && !lines[end].trim()) end--;
return lines.slice(start, end + 1);
}