mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: allow opening empty trace viewer (#5080)
This commit is contained in:
parent
16249ccbda
commit
4b5c876bbf
@ -140,11 +140,10 @@ program
|
|||||||
|
|
||||||
if (process.env.PWTRACE) {
|
if (process.env.PWTRACE) {
|
||||||
program
|
program
|
||||||
.command('show-trace <trace>')
|
.command('show-trace [trace]')
|
||||||
.description('Show trace viewer')
|
.description('Show trace viewer')
|
||||||
.option('--resources <dir>', 'Directory with the shared trace artifacts')
|
|
||||||
.action(function(trace, command) {
|
.action(function(trace, command) {
|
||||||
showTraceViewer(command.resources, trace);
|
showTraceViewer(trace);
|
||||||
}).on('--help', function() {
|
}).on('--help', function() {
|
||||||
console.log('');
|
console.log('');
|
||||||
console.log('Examples:');
|
console.log('Examples:');
|
||||||
|
@ -26,27 +26,66 @@ import { VideoTileGenerator } from './videoTileGenerator';
|
|||||||
|
|
||||||
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
|
const fsReadFileAsync = util.promisify(fs.readFile.bind(fs));
|
||||||
|
|
||||||
class TraceViewer {
|
type TraceViewerDocument = {
|
||||||
private _traceStorageDir: string;
|
resourcesDir: string;
|
||||||
private _traceModel: TraceModel;
|
model: TraceModel;
|
||||||
private _snapshotRouter: SnapshotRouter;
|
snapshotRouter: SnapshotRouter;
|
||||||
private _screenshotGenerator: ScreenshotGenerator;
|
screenshotGenerator: ScreenshotGenerator;
|
||||||
private _videoTileGenerator: VideoTileGenerator;
|
videoTileGenerator: VideoTileGenerator;
|
||||||
|
};
|
||||||
|
|
||||||
constructor(traceStorageDir: string) {
|
const emptyModel: TraceModel = {
|
||||||
this._traceStorageDir = traceStorageDir;
|
contexts: [
|
||||||
this._snapshotRouter = new SnapshotRouter(traceStorageDir);
|
{
|
||||||
this._traceModel = {
|
startTime: 0,
|
||||||
contexts: [],
|
endTime: 1,
|
||||||
};
|
created: {
|
||||||
this._screenshotGenerator = new ScreenshotGenerator(traceStorageDir, this._traceModel);
|
timestamp: Date.now(),
|
||||||
this._videoTileGenerator = new VideoTileGenerator(this._traceModel);
|
type: 'context-created',
|
||||||
|
browserName: 'none',
|
||||||
|
contextId: '<empty>',
|
||||||
|
deviceScaleFactor: 1,
|
||||||
|
isMobile: false,
|
||||||
|
viewportSize: { width: 800, height: 600 },
|
||||||
|
},
|
||||||
|
destroyed: {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
type: 'context-destroyed',
|
||||||
|
contextId: '<empty>',
|
||||||
|
},
|
||||||
|
name: '<empty>',
|
||||||
|
filePath: '',
|
||||||
|
pages: [],
|
||||||
|
resourcesByUrl: new Map()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
class TraceViewer {
|
||||||
|
private _document: TraceViewerDocument | undefined;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async load(filePath: string) {
|
async load(traceDir: string) {
|
||||||
const traceContent = await fsReadFileAsync(filePath, 'utf8');
|
const resourcesDir = path.join(traceDir, 'resources');
|
||||||
const events = traceContent.split('\n').map(line => line.trim()).filter(line => !!line).map(line => JSON.parse(line)) as TraceEvent[];
|
const model = { contexts: [] };
|
||||||
readTraceFile(events, this._traceModel, filePath);
|
this._document = {
|
||||||
|
model,
|
||||||
|
resourcesDir,
|
||||||
|
snapshotRouter: new SnapshotRouter(resourcesDir),
|
||||||
|
screenshotGenerator: new ScreenshotGenerator(resourcesDir, model),
|
||||||
|
videoTileGenerator: new VideoTileGenerator(model)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const name of fs.readdirSync(traceDir)) {
|
||||||
|
if (!name.endsWith('.trace'))
|
||||||
|
continue;
|
||||||
|
const filePath = path.join(traceDir, name);
|
||||||
|
const traceContent = await fsReadFileAsync(filePath, 'utf8');
|
||||||
|
const events = traceContent.split('\n').map(line => line.trim()).filter(line => !!line).map(line => JSON.parse(line)) as TraceEvent[];
|
||||||
|
readTraceFile(events, model, filePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async show() {
|
async show() {
|
||||||
@ -57,6 +96,8 @@ class TraceViewer {
|
|||||||
return fs.readFileSync(path).toString();
|
return fs.readFileSync(path).toString();
|
||||||
});
|
});
|
||||||
await uiPage.exposeBinding('renderSnapshot', async (_, action: ActionTraceEvent) => {
|
await uiPage.exposeBinding('renderSnapshot', async (_, action: ActionTraceEvent) => {
|
||||||
|
if (!this._document)
|
||||||
|
return;
|
||||||
try {
|
try {
|
||||||
if (!action.snapshot) {
|
if (!action.snapshot) {
|
||||||
const snapshotFrame = uiPage.frames()[1];
|
const snapshotFrame = uiPage.frames()[1];
|
||||||
@ -64,10 +105,10 @@ class TraceViewer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const snapshot = await fsReadFileAsync(path.join(this._traceStorageDir, action.snapshot!.sha1), 'utf8');
|
const snapshot = await fsReadFileAsync(path.join(this._document.resourcesDir, action.snapshot!.sha1), 'utf8');
|
||||||
const snapshotObject = JSON.parse(snapshot) as PageSnapshot;
|
const snapshotObject = JSON.parse(snapshot) as PageSnapshot;
|
||||||
const contextEntry = this._traceModel.contexts.find(entry => entry.created.contextId === action.contextId)!;
|
const contextEntry = this._document.model.contexts.find(entry => entry.created.contextId === action.contextId)!;
|
||||||
this._snapshotRouter.selectSnapshot(snapshotObject, contextEntry);
|
this._document.snapshotRouter.selectSnapshot(snapshotObject, contextEntry);
|
||||||
|
|
||||||
// TODO: fix Playwright bug where frame.name is lost (empty).
|
// TODO: fix Playwright bug where frame.name is lost (empty).
|
||||||
const snapshotFrame = uiPage.frames()[1];
|
const snapshotFrame = uiPage.frames()[1];
|
||||||
@ -88,21 +129,21 @@ class TraceViewer {
|
|||||||
console.log(e); // eslint-disable-line no-console
|
console.log(e); // eslint-disable-line no-console
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
await uiPage.exposeBinding('getTraceModel', () => this._traceModel);
|
await uiPage.exposeBinding('getTraceModel', () => this._document ? this._document.model : emptyModel);
|
||||||
await uiPage.exposeBinding('getVideoMetaInfo', async (_, videoId: string) => {
|
await uiPage.exposeBinding('getVideoMetaInfo', async (_, videoId: string) => {
|
||||||
return this._videoTileGenerator.render(videoId);
|
return this._document ? this._document.videoTileGenerator.render(videoId) : null;
|
||||||
});
|
});
|
||||||
await uiPage.route('**/*', (route, request) => {
|
await uiPage.route('**/*', (route, request) => {
|
||||||
if (request.frame().parentFrame()) {
|
if (request.frame().parentFrame() && this._document) {
|
||||||
this._snapshotRouter.route(route);
|
this._document.snapshotRouter.route(route);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const url = new URL(request.url());
|
const url = new URL(request.url());
|
||||||
try {
|
try {
|
||||||
if (request.url().includes('action-preview')) {
|
if (this._document && request.url().includes('action-preview')) {
|
||||||
const fullPath = url.pathname.substring('/action-preview/'.length);
|
const fullPath = url.pathname.substring('/action-preview/'.length);
|
||||||
const actionId = fullPath.substring(0, fullPath.indexOf('.png'));
|
const actionId = fullPath.substring(0, fullPath.indexOf('.png'));
|
||||||
this._screenshotGenerator.generateScreenshot(actionId).then(body => {
|
this._document.screenshotGenerator.generateScreenshot(actionId).then(body => {
|
||||||
if (body)
|
if (body)
|
||||||
route.fulfill({ contentType: 'image/png', body });
|
route.fulfill({ contentType: 'image/png', body });
|
||||||
else
|
else
|
||||||
@ -111,9 +152,9 @@ class TraceViewer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let filePath: string;
|
let filePath: string;
|
||||||
if (request.url().includes('video-tile')) {
|
if (this._document && request.url().includes('video-tile')) {
|
||||||
const fullPath = url.pathname.substring('/video-tile/'.length);
|
const fullPath = url.pathname.substring('/video-tile/'.length);
|
||||||
filePath = this._videoTileGenerator.tilePath(fullPath);
|
filePath = this._document.videoTileGenerator.tilePath(fullPath);
|
||||||
} else {
|
} else {
|
||||||
filePath = path.join(__dirname, 'web', url.pathname.substring(1));
|
filePath = path.join(__dirname, 'web', url.pathname.substring(1));
|
||||||
}
|
}
|
||||||
@ -133,37 +174,13 @@ class TraceViewer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function showTraceViewer(traceStorageDir: string | undefined, tracePath: string) {
|
export async function showTraceViewer(traceDir: string) {
|
||||||
if (!fs.existsSync(tracePath))
|
const traceViewer = new TraceViewer();
|
||||||
throw new Error(`${tracePath} does not exist`);
|
if (traceDir)
|
||||||
|
await traceViewer.load(traceDir);
|
||||||
const files: string[] = fs.statSync(tracePath).isFile() ? [tracePath] : collectFiles(tracePath);
|
|
||||||
|
|
||||||
if (!traceStorageDir) {
|
|
||||||
traceStorageDir = fs.statSync(tracePath).isFile() ? path.dirname(tracePath) : tracePath;
|
|
||||||
|
|
||||||
if (fs.existsSync(traceStorageDir + '/trace-resources'))
|
|
||||||
traceStorageDir = traceStorageDir + '/trace-resources';
|
|
||||||
}
|
|
||||||
|
|
||||||
const traceViewer = new TraceViewer(traceStorageDir);
|
|
||||||
for (const filePath of files)
|
|
||||||
await traceViewer.load(filePath);
|
|
||||||
await traceViewer.show();
|
await traceViewer.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
function collectFiles(dir: string): string[] {
|
|
||||||
const files = [];
|
|
||||||
for (const name of fs.readdirSync(dir)) {
|
|
||||||
const fullName = path.join(dir, name);
|
|
||||||
if (fs.lstatSync(fullName).isDirectory())
|
|
||||||
files.push(...collectFiles(fullName));
|
|
||||||
else if (fullName.endsWith('.trace'))
|
|
||||||
files.push(fullName);
|
|
||||||
}
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
|
|
||||||
const extensionToMime: { [key: string]: string } = {
|
const extensionToMime: { [key: string]: string } = {
|
||||||
'css': 'text/css',
|
'css': 'text/css',
|
||||||
'html': 'text/html',
|
'html': 'text/html',
|
||||||
|
@ -80,7 +80,7 @@ const SnapshotTab: React.FunctionComponent<{
|
|||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (actionEntry)
|
if (actionEntry)
|
||||||
window.renderSnapshot(actionEntry.action);
|
(window as any).renderSnapshot(actionEntry.action);
|
||||||
}, [actionEntry]);
|
}, [actionEntry]);
|
||||||
|
|
||||||
const scale = Math.min(measure.width / snapshotSize.width, measure.height / snapshotSize.height);
|
const scale = Math.min(measure.width / snapshotSize.width, measure.height / snapshotSize.height);
|
||||||
|
@ -9,6 +9,7 @@ module.exports = {
|
|||||||
resolve: {
|
resolve: {
|
||||||
extensions: ['.ts', '.js', '.tsx', '.jsx']
|
extensions: ['.ts', '.js', '.tsx', '.jsx']
|
||||||
},
|
},
|
||||||
|
devtool: 'source-map',
|
||||||
output: {
|
output: {
|
||||||
globalObject: 'self',
|
globalObject: 'self',
|
||||||
filename: '[name].bundle.js',
|
filename: '[name].bundle.js',
|
||||||
|
@ -45,8 +45,8 @@ export class Browser extends ChannelOwner<channels.BrowserChannel, channels.Brow
|
|||||||
|
|
||||||
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
async newContext(options: BrowserContextOptions = {}): Promise<BrowserContext> {
|
||||||
return this._wrapApiCall('browser.newContext', async () => {
|
return this._wrapApiCall('browser.newContext', async () => {
|
||||||
if (this._isRemote && options._tracePath)
|
if (this._isRemote && options._traceDir)
|
||||||
throw new Error(`"_tracePath" is not supported in connected browser`);
|
throw new Error(`"_traceDir" is not supported in connected browser`);
|
||||||
const contextOptions = await prepareBrowserContextOptions(options);
|
const contextOptions = await prepareBrowserContextOptions(options);
|
||||||
const context = BrowserContext.from((await this._channel.newContext(contextOptions)).context);
|
const context = BrowserContext.from((await this._channel.newContext(contextOptions)).context);
|
||||||
context._options = contextOptions;
|
context._options = contextOptions;
|
||||||
|
@ -297,8 +297,7 @@ export type BrowserTypeLaunchPersistentContextParams = {
|
|||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'light' | 'dark' | 'no-preference',
|
colorScheme?: 'light' | 'dark' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_traceResourcesPath?: string,
|
_traceDir?: string,
|
||||||
_tracePath?: string,
|
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
dir: string,
|
dir: string,
|
||||||
size?: {
|
size?: {
|
||||||
@ -360,8 +359,7 @@ export type BrowserTypeLaunchPersistentContextOptions = {
|
|||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'light' | 'dark' | 'no-preference',
|
colorScheme?: 'light' | 'dark' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_traceResourcesPath?: string,
|
_traceDir?: string,
|
||||||
_tracePath?: string,
|
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
dir: string,
|
dir: string,
|
||||||
size?: {
|
size?: {
|
||||||
@ -424,8 +422,7 @@ export type BrowserNewContextParams = {
|
|||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_traceResourcesPath?: string,
|
_traceDir?: string,
|
||||||
_tracePath?: string,
|
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
dir: string,
|
dir: string,
|
||||||
size?: {
|
size?: {
|
||||||
@ -477,8 +474,7 @@ export type BrowserNewContextOptions = {
|
|||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_traceResourcesPath?: string,
|
_traceDir?: string,
|
||||||
_tracePath?: string,
|
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
dir: string,
|
dir: string,
|
||||||
size?: {
|
size?: {
|
||||||
@ -2772,8 +2768,7 @@ export type AndroidDeviceLaunchBrowserParams = {
|
|||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_traceResourcesPath?: string,
|
_traceDir?: string,
|
||||||
_tracePath?: string,
|
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
dir: string,
|
dir: string,
|
||||||
size?: {
|
size?: {
|
||||||
@ -2817,8 +2812,7 @@ export type AndroidDeviceLaunchBrowserOptions = {
|
|||||||
hasTouch?: boolean,
|
hasTouch?: boolean,
|
||||||
colorScheme?: 'dark' | 'light' | 'no-preference',
|
colorScheme?: 'dark' | 'light' | 'no-preference',
|
||||||
acceptDownloads?: boolean,
|
acceptDownloads?: boolean,
|
||||||
_traceResourcesPath?: string,
|
_traceDir?: string,
|
||||||
_tracePath?: string,
|
|
||||||
recordVideo?: {
|
recordVideo?: {
|
||||||
dir: string,
|
dir: string,
|
||||||
size?: {
|
size?: {
|
||||||
|
@ -372,8 +372,7 @@ BrowserType:
|
|||||||
- dark
|
- dark
|
||||||
- no-preference
|
- no-preference
|
||||||
acceptDownloads: boolean?
|
acceptDownloads: boolean?
|
||||||
_traceResourcesPath: string?
|
_traceDir: string?
|
||||||
_tracePath: string?
|
|
||||||
recordVideo:
|
recordVideo:
|
||||||
type: object?
|
type: object?
|
||||||
properties:
|
properties:
|
||||||
@ -445,8 +444,7 @@ Browser:
|
|||||||
- light
|
- light
|
||||||
- no-preference
|
- no-preference
|
||||||
acceptDownloads: boolean?
|
acceptDownloads: boolean?
|
||||||
_traceResourcesPath: string?
|
_traceDir: string?
|
||||||
_tracePath: string?
|
|
||||||
recordVideo:
|
recordVideo:
|
||||||
type: object?
|
type: object?
|
||||||
properties:
|
properties:
|
||||||
@ -2336,8 +2334,7 @@ AndroidDevice:
|
|||||||
- light
|
- light
|
||||||
- no-preference
|
- no-preference
|
||||||
acceptDownloads: boolean?
|
acceptDownloads: boolean?
|
||||||
_traceResourcesPath: string?
|
_traceDir: string?
|
||||||
_tracePath: string?
|
|
||||||
recordVideo:
|
recordVideo:
|
||||||
type: object?
|
type: object?
|
||||||
properties:
|
properties:
|
||||||
|
@ -211,8 +211,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||||||
hasTouch: tOptional(tBoolean),
|
hasTouch: tOptional(tBoolean),
|
||||||
colorScheme: tOptional(tEnum(['light', 'dark', 'no-preference'])),
|
colorScheme: tOptional(tEnum(['light', 'dark', 'no-preference'])),
|
||||||
acceptDownloads: tOptional(tBoolean),
|
acceptDownloads: tOptional(tBoolean),
|
||||||
_traceResourcesPath: tOptional(tString),
|
_traceDir: tOptional(tString),
|
||||||
_tracePath: tOptional(tString),
|
|
||||||
recordVideo: tOptional(tObject({
|
recordVideo: tOptional(tObject({
|
||||||
dir: tString,
|
dir: tString,
|
||||||
size: tOptional(tObject({
|
size: tOptional(tObject({
|
||||||
@ -255,8 +254,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||||||
hasTouch: tOptional(tBoolean),
|
hasTouch: tOptional(tBoolean),
|
||||||
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
||||||
acceptDownloads: tOptional(tBoolean),
|
acceptDownloads: tOptional(tBoolean),
|
||||||
_traceResourcesPath: tOptional(tString),
|
_traceDir: tOptional(tString),
|
||||||
_tracePath: tOptional(tString),
|
|
||||||
recordVideo: tOptional(tObject({
|
recordVideo: tOptional(tObject({
|
||||||
dir: tString,
|
dir: tString,
|
||||||
size: tOptional(tObject({
|
size: tOptional(tObject({
|
||||||
@ -1040,8 +1038,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme {
|
|||||||
hasTouch: tOptional(tBoolean),
|
hasTouch: tOptional(tBoolean),
|
||||||
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
colorScheme: tOptional(tEnum(['dark', 'light', 'no-preference'])),
|
||||||
acceptDownloads: tOptional(tBoolean),
|
acceptDownloads: tOptional(tBoolean),
|
||||||
_traceResourcesPath: tOptional(tString),
|
_traceDir: tOptional(tString),
|
||||||
_tracePath: tOptional(tString),
|
|
||||||
recordVideo: tOptional(tObject({
|
recordVideo: tOptional(tObject({
|
||||||
dir: tString,
|
dir: tString,
|
||||||
size: tOptional(tObject({
|
size: tOptional(tObject({
|
||||||
|
@ -248,8 +248,7 @@ export type BrowserContextOptions = {
|
|||||||
path: string
|
path: string
|
||||||
},
|
},
|
||||||
proxy?: ProxySettings,
|
proxy?: ProxySettings,
|
||||||
_tracePath?: string,
|
_traceDir?: string,
|
||||||
_traceResourcesPath?: string,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EnvArray = { name: string, value: string }[];
|
export type EnvArray = { name: string, value: string }[];
|
||||||
|
@ -43,17 +43,11 @@ class Tracer implements ContextListener {
|
|||||||
private _contextTracers = new Map<BrowserContext, ContextTracer>();
|
private _contextTracers = new Map<BrowserContext, ContextTracer>();
|
||||||
|
|
||||||
async onContextCreated(context: BrowserContext): Promise<void> {
|
async onContextCreated(context: BrowserContext): Promise<void> {
|
||||||
let traceStorageDir: string;
|
const traceDir = envTrace || context._options._traceDir;
|
||||||
let tracePath: string;
|
if (!traceDir)
|
||||||
if (context._options._tracePath) {
|
|
||||||
traceStorageDir = context._options._traceResourcesPath || path.join(path.dirname(context._options._tracePath), 'trace-resources');
|
|
||||||
tracePath = context._options._tracePath;
|
|
||||||
} else if (envTrace) {
|
|
||||||
traceStorageDir = envTrace;
|
|
||||||
tracePath = path.join(envTrace, createGuid() + '.trace');
|
|
||||||
} else {
|
|
||||||
return;
|
return;
|
||||||
}
|
const traceStorageDir = path.join(traceDir, 'resources');
|
||||||
|
const tracePath = path.join(traceDir, createGuid() + '.trace');
|
||||||
const contextTracer = new ContextTracer(context, traceStorageDir, tracePath);
|
const contextTracer = new ContextTracer(context, traceStorageDir, tracePath);
|
||||||
this._contextTracers.set(context, contextTracer);
|
this._contextTracers.set(context, contextTracer);
|
||||||
}
|
}
|
||||||
|
@ -20,14 +20,13 @@ import * as path from 'path';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
|
||||||
it('should record trace', async ({browser, testInfo, server}) => {
|
it('should record trace', async ({browser, testInfo, server}) => {
|
||||||
const artifactsPath = testInfo.outputPath('');
|
const traceDir = testInfo.outputPath('trace');
|
||||||
const tracePath = path.join(artifactsPath, 'playwright.trace');
|
const context = await browser.newContext({ _traceDir: traceDir } as any);
|
||||||
const context = await browser.newContext({ _tracePath: tracePath } as any);
|
|
||||||
const page = await context.newPage();
|
const page = await context.newPage();
|
||||||
const url = server.PREFIX + '/snapshot/snapshot-with-css.html';
|
const url = server.PREFIX + '/snapshot/snapshot-with-css.html';
|
||||||
await page.goto(url);
|
await page.goto(url);
|
||||||
await context.close();
|
await context.close();
|
||||||
|
const tracePath = path.join(traceDir, fs.readdirSync(traceDir).find(n => n.endsWith('.trace')));
|
||||||
const traceFileContent = await fs.promises.readFile(tracePath, 'utf8');
|
const traceFileContent = await fs.promises.readFile(tracePath, 'utf8');
|
||||||
const traceEvents = traceFileContent.split('\n').filter(line => !!line).map(line => JSON.parse(line)) as trace.TraceEvent[];
|
const traceEvents = traceFileContent.split('\n').filter(line => !!line).map(line => JSON.parse(line)) as trace.TraceEvent[];
|
||||||
|
|
||||||
@ -47,5 +46,5 @@ it('should record trace', async ({browser, testInfo, server}) => {
|
|||||||
expect(gotoEvent.value).toBe(url);
|
expect(gotoEvent.value).toBe(url);
|
||||||
|
|
||||||
expect(gotoEvent.snapshot).toBeTruthy();
|
expect(gotoEvent.snapshot).toBeTruthy();
|
||||||
expect(fs.existsSync(path.join(artifactsPath, 'trace-resources', gotoEvent.snapshot!.sha1))).toBe(true);
|
expect(fs.existsSync(path.join(traceDir, 'resources', gotoEvent.snapshot!.sha1))).toBe(true);
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user