feat(codegen): support --save-trace option (#8267)
This commit is contained in:
parent
93c0da6c07
commit
8d81890e47
|
|
@ -270,6 +270,7 @@ type Options = {
|
||||||
loadStorage?: string;
|
loadStorage?: string;
|
||||||
proxyServer?: string;
|
proxyServer?: string;
|
||||||
saveStorage?: string;
|
saveStorage?: string;
|
||||||
|
saveTrace?: string;
|
||||||
timeout: string;
|
timeout: string;
|
||||||
timezone?: string;
|
timezone?: string;
|
||||||
viewportSize?: string;
|
viewportSize?: string;
|
||||||
|
|
@ -386,6 +387,8 @@ async function launchContext(options: Options, headless: boolean, executablePath
|
||||||
if (closingBrowser)
|
if (closingBrowser)
|
||||||
return;
|
return;
|
||||||
closingBrowser = true;
|
closingBrowser = true;
|
||||||
|
if (options.saveTrace)
|
||||||
|
await context.tracing.stop({ path: options.saveTrace });
|
||||||
if (options.saveStorage)
|
if (options.saveStorage)
|
||||||
await context.storageState({ path: options.saveStorage }).catch(e => null);
|
await context.storageState({ path: options.saveStorage }).catch(e => null);
|
||||||
await browser.close();
|
await browser.close();
|
||||||
|
|
@ -406,6 +409,9 @@ async function launchContext(options: Options, headless: boolean, executablePath
|
||||||
context.setDefaultNavigationTimeout(parseInt(options.timeout, 10));
|
context.setDefaultNavigationTimeout(parseInt(options.timeout, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.saveTrace)
|
||||||
|
await context.tracing.start({ screenshots: true, snapshots: true });
|
||||||
|
|
||||||
// Omit options that we add automatically for presentation purpose.
|
// Omit options that we add automatically for presentation purpose.
|
||||||
delete launchOptions.headless;
|
delete launchOptions.headless;
|
||||||
delete launchOptions.executablePath;
|
delete launchOptions.executablePath;
|
||||||
|
|
@ -548,6 +554,7 @@ function commandWithOpenOptions(command: string, description: string, options: a
|
||||||
.option('--lang <language>', 'specify language / locale, for example "en-GB"')
|
.option('--lang <language>', 'specify language / locale, for example "en-GB"')
|
||||||
.option('--proxy-server <proxy>', 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"')
|
.option('--proxy-server <proxy>', 'specify proxy server, for example "http://myproxy:3128" or "socks5://myproxy:8080"')
|
||||||
.option('--save-storage <filename>', 'save context storage state at the end, for later use with --load-storage')
|
.option('--save-storage <filename>', 'save context storage state at the end, for later use with --load-storage')
|
||||||
|
.option('--save-trace <filename>', 'record a trace for the session and save it to a file')
|
||||||
.option('--timezone <time zone>', 'time zone to emulate, for example "Europe/Rome"')
|
.option('--timezone <time zone>', 'time zone to emulate, for example "Europe/Rome"')
|
||||||
.option('--timeout <timeout>', 'timeout for Playwright actions in milliseconds', '10000')
|
.option('--timeout <timeout>', 'timeout for Playwright actions in milliseconds', '10000')
|
||||||
.option('--user-agent <ua string>', 'specify user agent string')
|
.option('--user-agent <ua string>', 'specify user agent string')
|
||||||
|
|
|
||||||
|
|
@ -29,10 +29,10 @@ import { PythonLanguageGenerator } from './recorder/python';
|
||||||
import * as recorderSource from '../../generated/recorderSource';
|
import * as recorderSource from '../../generated/recorderSource';
|
||||||
import * as consoleApiSource from '../../generated/consoleApiSource';
|
import * as consoleApiSource from '../../generated/consoleApiSource';
|
||||||
import { RecorderApp } from './recorder/recorderApp';
|
import { RecorderApp } from './recorder/recorderApp';
|
||||||
import { CallMetadata, InstrumentationListener, internalCallMetadata, SdkObject } from '../instrumentation';
|
import { CallMetadata, InstrumentationListener, SdkObject } from '../instrumentation';
|
||||||
import { Point } from '../../common/types';
|
import { Point } from '../../common/types';
|
||||||
import { CallLog, CallLogStatus, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
|
import { CallLog, CallLogStatus, EventData, Mode, Source, UIState } from './recorder/recorderTypes';
|
||||||
import { isUnderTest } from '../../utils/utils';
|
import { createGuid, isUnderTest, monotonicTime } from '../../utils/utils';
|
||||||
import { metadataToCallLog } from './recorder/recorderUtils';
|
import { metadataToCallLog } from './recorder/recorderUtils';
|
||||||
import { Debugger } from './debugger';
|
import { Debugger } from './debugger';
|
||||||
|
|
||||||
|
|
@ -308,36 +308,64 @@ export class RecorderSupplement implements InstrumentationListener {
|
||||||
...describeFrame(frame),
|
...describeFrame(frame),
|
||||||
action
|
action
|
||||||
};
|
};
|
||||||
this._generator.willPerformAction(actionInContext);
|
|
||||||
const noCallMetadata = internalCallMetadata();
|
const perform = async (action: string, params: any, cb: (callMetadata: CallMetadata) => Promise<any>) => {
|
||||||
try {
|
const callMetadata: CallMetadata = {
|
||||||
const kActionTimeout = 5000;
|
id: `call@${createGuid()}`,
|
||||||
if (action.name === 'click') {
|
apiName: 'frame.' + action,
|
||||||
const { options } = toClickOptions(action);
|
objectId: frame.guid,
|
||||||
await frame.click(noCallMetadata, action.selector, { ...options, timeout: kActionTimeout });
|
pageId: frame._page.guid,
|
||||||
|
frameId: frame.guid,
|
||||||
|
startTime: monotonicTime(),
|
||||||
|
endTime: 0,
|
||||||
|
type: 'Frame',
|
||||||
|
method: action,
|
||||||
|
params,
|
||||||
|
log: [],
|
||||||
|
snapshots: [],
|
||||||
|
};
|
||||||
|
this._generator.willPerformAction(actionInContext);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await frame.instrumentation.onBeforeCall(frame, callMetadata);
|
||||||
|
await cb(callMetadata);
|
||||||
|
} catch (e) {
|
||||||
|
callMetadata.endTime = monotonicTime();
|
||||||
|
await frame.instrumentation.onAfterCall(frame, callMetadata);
|
||||||
|
this._generator.performedActionFailed(actionInContext);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (action.name === 'press') {
|
|
||||||
const modifiers = toModifiers(action.modifiers);
|
callMetadata.endTime = monotonicTime();
|
||||||
const shortcut = [...modifiers, action.key].join('+');
|
await frame.instrumentation.onAfterCall(frame, callMetadata);
|
||||||
await frame.press(noCallMetadata, action.selector, shortcut, { timeout: kActionTimeout });
|
|
||||||
}
|
const timer = setTimeout(() => {
|
||||||
if (action.name === 'check')
|
// Commit the action after 5 seconds so that no further signals are added to it.
|
||||||
await frame.check(noCallMetadata, action.selector, { timeout: kActionTimeout });
|
actionInContext.committed = true;
|
||||||
if (action.name === 'uncheck')
|
this._timers.delete(timer);
|
||||||
await frame.uncheck(noCallMetadata, action.selector, { timeout: kActionTimeout });
|
}, 5000);
|
||||||
if (action.name === 'select')
|
this._generator.didPerformAction(actionInContext);
|
||||||
await frame.selectOption(noCallMetadata, action.selector, [], action.options.map(value => ({ value })), { timeout: kActionTimeout });
|
this._timers.add(timer);
|
||||||
} catch (e) {
|
};
|
||||||
this._generator.performedActionFailed(actionInContext);
|
|
||||||
return;
|
const kActionTimeout = 5000;
|
||||||
|
if (action.name === 'click') {
|
||||||
|
const { options } = toClickOptions(action);
|
||||||
|
await perform('click', { selector: action.selector }, callMetadata => frame.click(callMetadata, action.selector, { ...options, timeout: kActionTimeout }));
|
||||||
|
}
|
||||||
|
if (action.name === 'press') {
|
||||||
|
const modifiers = toModifiers(action.modifiers);
|
||||||
|
const shortcut = [...modifiers, action.key].join('+');
|
||||||
|
await perform('press', { selector: action.selector, key: shortcut }, callMetadata => frame.press(callMetadata, action.selector, shortcut, { timeout: kActionTimeout }));
|
||||||
|
}
|
||||||
|
if (action.name === 'check')
|
||||||
|
await perform('check', { selector: action.selector }, callMetadata => frame.check(callMetadata, action.selector, { timeout: kActionTimeout }));
|
||||||
|
if (action.name === 'uncheck')
|
||||||
|
await perform('uncheck', { selector: action.selector }, callMetadata => frame.uncheck(callMetadata, action.selector, { timeout: kActionTimeout }));
|
||||||
|
if (action.name === 'select') {
|
||||||
|
const values = action.options.map(value => ({ value }));
|
||||||
|
await perform('selectOption', { selector: action.selector, values }, callMetadata => frame.selectOption(callMetadata, action.selector, [], values, { timeout: kActionTimeout }));
|
||||||
}
|
}
|
||||||
const timer = setTimeout(() => {
|
|
||||||
// Commit the action after 5 seconds so that no further signals are added to it.
|
|
||||||
actionInContext.committed = true;
|
|
||||||
this._timers.delete(timer);
|
|
||||||
}, 5000);
|
|
||||||
this._generator.didPerformAction(actionInContext);
|
|
||||||
this._timers.add(timer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _recordAction(frame: Frame, action: actions.Action) {
|
private async _recordAction(frame: Frame, action: actions.Action) {
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
import { test, expect } from './inspectorTest';
|
import { test, expect } from './inspectorTest';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
|
import fs from 'fs';
|
||||||
|
|
||||||
test.describe('cli codegen', () => {
|
test.describe('cli codegen', () => {
|
||||||
test.skip(({ mode }) => mode !== 'default');
|
test.skip(({ mode }) => mode !== 'default');
|
||||||
|
|
@ -637,4 +638,11 @@ test.describe('cli codegen', () => {
|
||||||
|
|
||||||
expect(sources.get('JavaScript').text).toContain(`page.waitForNavigation(/*{ url: '${server.EMPTY_PAGE}' }*/)`);
|
expect(sources.get('JavaScript').text).toContain(`page.waitForNavigation(/*{ url: '${server.EMPTY_PAGE}' }*/)`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should --save-trace', async ({ runCLI }, testInfo) => {
|
||||||
|
const traceFileName = testInfo.outputPath('trace.zip');
|
||||||
|
const cli = runCLI([`--save-trace=${traceFileName}`]);
|
||||||
|
await cli.exited;
|
||||||
|
expect(fs.existsSync(traceFileName)).toBeTruthy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue