mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore(cli): add recording mode (#2579)
This commit is contained in:
parent
fd9b1031fa
commit
1c7a8952b9
@ -23,6 +23,8 @@ import { Playwright } from '../server/playwright';
|
|||||||
import { BrowserType, LaunchOptions } from '../server/browserType';
|
import { BrowserType, LaunchOptions } from '../server/browserType';
|
||||||
import { DeviceDescriptors } from '../deviceDescriptors';
|
import { DeviceDescriptors } from '../deviceDescriptors';
|
||||||
import { BrowserContextOptions } from '../browserContext';
|
import { BrowserContextOptions } from '../browserContext';
|
||||||
|
import { setRecorderMode } from '../debug/debugController';
|
||||||
|
import { helper } from '../helper';
|
||||||
|
|
||||||
const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']);
|
const playwright = new Playwright(__dirname, require('../../browsers.json')['browsers']);
|
||||||
|
|
||||||
@ -45,6 +47,19 @@ program
|
|||||||
console.log(' $ -b webkit open https://example.com');
|
console.log(' $ -b webkit open https://example.com');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
program
|
||||||
|
.command('record [url]')
|
||||||
|
.description('open page in browser specified via -b, --browser and start recording')
|
||||||
|
.action(function(url, command) {
|
||||||
|
record(command.parent, url);
|
||||||
|
}).on('--help', function() {
|
||||||
|
console.log('');
|
||||||
|
console.log('Examples:');
|
||||||
|
console.log('');
|
||||||
|
console.log(' $ record');
|
||||||
|
console.log(' $ -b webkit record https://example.com');
|
||||||
|
});
|
||||||
|
|
||||||
const browsers = [
|
const browsers = [
|
||||||
{ initial: 'cr', name: 'Chromium', type: 'chromium' },
|
{ initial: 'cr', name: 'Chromium', type: 'chromium' },
|
||||||
{ initial: 'ff', name: 'Firefox', type: 'firefox' },
|
{ initial: 'ff', name: 'Firefox', type: 'firefox' },
|
||||||
@ -88,6 +103,12 @@ async function open(options: Options, url: string | undefined) {
|
|||||||
return { browser, page };
|
return { browser, page };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function record(options: Options, url: string | undefined) {
|
||||||
|
helper.setDebugMode();
|
||||||
|
setRecorderMode();
|
||||||
|
return await open(options, url);
|
||||||
|
}
|
||||||
|
|
||||||
function lookupBrowserType(name: string): BrowserType {
|
function lookupBrowserType(name: string): BrowserType {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case 'chromium': return playwright.chromium!;
|
case 'chromium': return playwright.chromium!;
|
||||||
|
@ -20,15 +20,18 @@ import * as frames from '../frames';
|
|||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { RecorderController } from './recorderController';
|
import { RecorderController } from './recorderController';
|
||||||
|
|
||||||
export class DebugController {
|
let isRecorderMode = false;
|
||||||
private _context: BrowserContextBase;
|
|
||||||
|
|
||||||
|
export function setRecorderMode(): void {
|
||||||
|
isRecorderMode = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DebugController {
|
||||||
constructor(context: BrowserContextBase) {
|
constructor(context: BrowserContextBase) {
|
||||||
this._context = context;
|
|
||||||
const installInFrame = async (frame: frames.Frame) => {
|
const installInFrame = async (frame: frames.Frame) => {
|
||||||
try {
|
try {
|
||||||
const mainContext = await frame._mainContext();
|
const mainContext = await frame._mainContext();
|
||||||
await mainContext.debugScript();
|
await mainContext.createDebugScript({ console: true, record: isRecorderMode });
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -25,8 +25,10 @@ export default class DebugScript {
|
|||||||
constructor() {
|
constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize(injectedScript: InjectedScript) {
|
initialize(injectedScript: InjectedScript, options: { console?: boolean, record?: boolean }) {
|
||||||
this.consoleAPI = new ConsoleAPI(injectedScript);
|
if (options.console)
|
||||||
this.recorder = new Recorder(injectedScript);
|
this.consoleAPI = new ConsoleAPI(injectedScript);
|
||||||
|
if (options.record)
|
||||||
|
this.recorder = new Recorder(injectedScript);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,14 @@ import { parseSelector } from '../../common/selectorParser';
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
recordPlaywrightAction: (action: actions.Action) => void;
|
performPlaywrightAction: (action: actions.Action) => Promise<void>;
|
||||||
|
recordPlaywrightAction: (action: actions.Action) => Promise<void>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Recorder {
|
export class Recorder {
|
||||||
private _injectedScript: InjectedScript;
|
private _injectedScript: InjectedScript;
|
||||||
|
private _performingAction = false;
|
||||||
|
|
||||||
constructor(injectedScript: InjectedScript) {
|
constructor(injectedScript: InjectedScript) {
|
||||||
this._injectedScript = injectedScript;
|
this._injectedScript = injectedScript;
|
||||||
@ -35,13 +37,14 @@ export class Recorder {
|
|||||||
document.addEventListener('keydown', event => this._onKeyDown(event), true);
|
document.addEventListener('keydown', event => this._onKeyDown(event), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onClick(event: MouseEvent) {
|
private async _onClick(event: MouseEvent) {
|
||||||
const selector = this._buildSelector(event.target as Element);
|
|
||||||
if ((event.target as Element).nodeName === 'SELECT')
|
if ((event.target as Element).nodeName === 'SELECT')
|
||||||
return;
|
return;
|
||||||
window.recordPlaywrightAction({
|
|
||||||
|
// Perform action consumes this event and asks Playwright to perform it.
|
||||||
|
this._performAction(event, {
|
||||||
name: 'click',
|
name: 'click',
|
||||||
selector,
|
selector: this._buildSelector(event.target as Element),
|
||||||
signals: [],
|
signals: [],
|
||||||
button: buttonForEvent(event),
|
button: buttonForEvent(event),
|
||||||
modifiers: modifiersForEvent(event),
|
modifiers: modifiersForEvent(event),
|
||||||
@ -49,17 +52,20 @@ export class Recorder {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onInput(event: Event) {
|
private async _onInput(event: Event) {
|
||||||
const selector = this._buildSelector(event.target as Element);
|
const selector = this._buildSelector(event.target as Element);
|
||||||
if ((event.target as Element).nodeName === 'INPUT') {
|
if ((event.target as Element).nodeName === 'INPUT') {
|
||||||
const inputElement = event.target as HTMLInputElement;
|
const inputElement = event.target as HTMLInputElement;
|
||||||
if ((inputElement.type || '').toLowerCase() === 'checkbox') {
|
if ((inputElement.type || '').toLowerCase() === 'checkbox') {
|
||||||
window.recordPlaywrightAction({
|
// Perform action consumes this event and asks Playwright to perform it.
|
||||||
|
this._performAction(event, {
|
||||||
name: inputElement.checked ? 'check' : 'uncheck',
|
name: inputElement.checked ? 'check' : 'uncheck',
|
||||||
selector,
|
selector,
|
||||||
signals: [],
|
signals: [],
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
// Non-navigating actions are simply recorded by Playwright.
|
||||||
window.recordPlaywrightAction({
|
window.recordPlaywrightAction({
|
||||||
name: 'fill',
|
name: 'fill',
|
||||||
selector,
|
selector,
|
||||||
@ -70,6 +76,7 @@ export class Recorder {
|
|||||||
}
|
}
|
||||||
if ((event.target as Element).nodeName === 'SELECT') {
|
if ((event.target as Element).nodeName === 'SELECT') {
|
||||||
const selectElement = event.target as HTMLSelectElement;
|
const selectElement = event.target as HTMLSelectElement;
|
||||||
|
// TODO: move this to this._performAction
|
||||||
window.recordPlaywrightAction({
|
window.recordPlaywrightAction({
|
||||||
name: 'select',
|
name: 'select',
|
||||||
selector,
|
selector,
|
||||||
@ -79,19 +86,29 @@ export class Recorder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onKeyDown(event: KeyboardEvent) {
|
private async _onKeyDown(event: KeyboardEvent) {
|
||||||
if (event.key !== 'Tab' && event.key !== 'Enter' && event.key !== 'Escape')
|
if (event.key !== 'Tab' && event.key !== 'Enter' && event.key !== 'Escape')
|
||||||
return;
|
return;
|
||||||
const selector = this._buildSelector(event.target as Element);
|
this._performAction(event, {
|
||||||
window.recordPlaywrightAction({
|
|
||||||
name: 'press',
|
name: 'press',
|
||||||
selector,
|
selector: this._buildSelector(event.target as Element),
|
||||||
signals: [],
|
signals: [],
|
||||||
key: event.key,
|
key: event.key,
|
||||||
modifiers: modifiersForEvent(event),
|
modifiers: modifiersForEvent(event),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _performAction(event: Event, action: actions.Action) {
|
||||||
|
// If Playwright is performing action for us, bail.
|
||||||
|
if (this._performingAction)
|
||||||
|
return;
|
||||||
|
// Consume as the first thing.
|
||||||
|
consumeEvent(event);
|
||||||
|
this._performingAction = true;
|
||||||
|
await window.performPlaywrightAction(action);
|
||||||
|
this._performingAction = false;
|
||||||
|
}
|
||||||
|
|
||||||
private _buildSelector(targetElement: Element): string {
|
private _buildSelector(targetElement: Element): string {
|
||||||
const path: string[] = [];
|
const path: string[] = [];
|
||||||
const root = document.documentElement;
|
const root = document.documentElement;
|
||||||
@ -175,3 +192,9 @@ function buttonForEvent(event: MouseEvent): 'left' | 'middle' | 'right' {
|
|||||||
function escapeForRegex(text: string): string {
|
function escapeForRegex(text: string): string {
|
||||||
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function consumeEvent(e: Event) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ export type ActionName =
|
|||||||
export type ActionBase = {
|
export type ActionBase = {
|
||||||
signals: Signal[],
|
signals: Signal[],
|
||||||
frameUrl?: string,
|
frameUrl?: string,
|
||||||
|
committed?: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ClickAction = ActionBase & {
|
export type ClickAction = ActionBase & {
|
||||||
@ -74,6 +75,7 @@ export type Action = ClickAction | CheckAction | UncheckAction | FillAction | Na
|
|||||||
export type NavigationSignal = {
|
export type NavigationSignal = {
|
||||||
name: 'navigation',
|
name: 'navigation',
|
||||||
url: string,
|
url: string,
|
||||||
|
type: 'assert' | 'await',
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Signal = NavigationSignal;
|
export type Signal = NavigationSignal;
|
||||||
|
@ -19,33 +19,99 @@ import * as frames from '../frames';
|
|||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import { Events } from '../events';
|
import { Events } from '../events';
|
||||||
import { TerminalOutput } from './terminalOutput';
|
import { TerminalOutput } from './terminalOutput';
|
||||||
|
import * as dom from '../dom';
|
||||||
|
|
||||||
export class RecorderController {
|
export class RecorderController {
|
||||||
private _page: Page;
|
private _page: Page;
|
||||||
private _output = new TerminalOutput();
|
private _output = new TerminalOutput();
|
||||||
|
private _performingAction = false;
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
this._page = page;
|
this._page = page;
|
||||||
|
|
||||||
this._page.exposeBinding('recordPlaywrightAction', (source, action: actions.Action) => {
|
// Input actions that potentially lead to navigation are intercepted on the page and are
|
||||||
if (source.frame !== this._page.mainFrame())
|
// performed by the Playwright.
|
||||||
action.frameUrl = source.frame.url();
|
this._page.exposeBinding('performPlaywrightAction',
|
||||||
this._output.addAction(action);
|
(source, action: actions.Action) => this._performAction(source.frame, action));
|
||||||
});
|
// Other non-essential actions are simply being recorded.
|
||||||
|
this._page.exposeBinding('recordPlaywrightAction',
|
||||||
|
(source, action: actions.Action) => this._recordAction(source.frame, action));
|
||||||
|
|
||||||
this._page.on(Events.Page.FrameNavigated, (frame: frames.Frame) => {
|
this._page.on(Events.Page.FrameNavigated, (frame: frames.Frame) => this._onFrameNavigated(frame));
|
||||||
if (frame.parentFrame())
|
}
|
||||||
return;
|
|
||||||
const action = this._output.lastAction();
|
private async _performAction(frame: frames.Frame, action: actions.Action) {
|
||||||
if (action) {
|
if (frame !== this._page.mainFrame())
|
||||||
this._output.signal({ name: 'navigation', url: frame.url() });
|
action.frameUrl = frame.url();
|
||||||
} else {
|
this._performingAction = true;
|
||||||
this._output.addAction({
|
this._output.addAction(action);
|
||||||
name: 'navigate',
|
if (action.name === 'click') {
|
||||||
url: this._page.url(),
|
const { options } = toClickOptions(action);
|
||||||
signals: [],
|
await frame.click(action.selector, options);
|
||||||
});
|
}
|
||||||
}
|
if (action.name === 'press') {
|
||||||
});
|
const modifiers = toModifiers(action.modifiers);
|
||||||
|
const shortcut = [...modifiers, action.key].join('+');
|
||||||
|
await frame.press(action.selector, shortcut);
|
||||||
|
}
|
||||||
|
if (action.name === 'check')
|
||||||
|
await frame.check(action.selector);
|
||||||
|
if (action.name === 'uncheck')
|
||||||
|
await frame.uncheck(action.selector);
|
||||||
|
this._performingAction = false;
|
||||||
|
setTimeout(() => action.committed = true, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _recordAction(frame: frames.Frame, action: actions.Action) {
|
||||||
|
if (frame !== this._page.mainFrame())
|
||||||
|
action.frameUrl = frame.url();
|
||||||
|
this._output.addAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _onFrameNavigated(frame: frames.Frame) {
|
||||||
|
if (frame.parentFrame())
|
||||||
|
return;
|
||||||
|
const action = this._output.lastAction();
|
||||||
|
// We only augment actions that have not been committed.
|
||||||
|
if (action && !action.committed) {
|
||||||
|
// If we hit a navigation while action is executed, we assert it. Otherwise, we await it.
|
||||||
|
this._output.signal({ name: 'navigation', url: frame.url(), type: this._performingAction ? 'assert' : 'await' });
|
||||||
|
} else {
|
||||||
|
// If navigation happens out of the blue, we just log it.
|
||||||
|
this._output.addAction({
|
||||||
|
name: 'navigate',
|
||||||
|
url: this._page.url(),
|
||||||
|
signals: [],
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function toClickOptions(action: actions.ClickAction): { method: 'click' | 'dblclick', options: dom.ClickOptions } {
|
||||||
|
let method: 'click' | 'dblclick' = 'click';
|
||||||
|
if (action.clickCount === 2)
|
||||||
|
method = 'dblclick';
|
||||||
|
const modifiers = toModifiers(action.modifiers);
|
||||||
|
const options: dom.ClickOptions = {};
|
||||||
|
if (action.button !== 'left')
|
||||||
|
options.button = action.button;
|
||||||
|
if (modifiers.length)
|
||||||
|
options.modifiers = modifiers;
|
||||||
|
if (action.clickCount > 2)
|
||||||
|
options.clickCount = action.clickCount;
|
||||||
|
return { method, options };
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toModifiers(modifiers: number): ('Alt' | 'Control' | 'Meta' | 'Shift')[] {
|
||||||
|
const result: ('Alt' | 'Control' | 'Meta' | 'Shift')[] = [];
|
||||||
|
if (modifiers & 1)
|
||||||
|
result.push('Alt');
|
||||||
|
if (modifiers & 2)
|
||||||
|
result.push('Control');
|
||||||
|
if (modifiers & 4)
|
||||||
|
result.push('Meta');
|
||||||
|
if (modifiers & 8)
|
||||||
|
result.push('Shift');
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
import * as dom from '../dom';
|
import * as dom from '../dom';
|
||||||
import { Formatter, formatColors } from '../utils/formatter';
|
import { Formatter, formatColors } from '../utils/formatter';
|
||||||
import { Action, NavigationSignal, actionTitle } from './recorderActions';
|
import { Action, NavigationSignal, actionTitle } from './recorderActions';
|
||||||
|
import { toModifiers } from './recorderController';
|
||||||
|
|
||||||
|
const { cst, cmt, fnc, kwd, prp, str } = formatColors;
|
||||||
|
|
||||||
export class TerminalOutput {
|
export class TerminalOutput {
|
||||||
private _lastAction: Action | undefined;
|
private _lastAction: Action | undefined;
|
||||||
@ -24,9 +27,9 @@ export class TerminalOutput {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const formatter = new Formatter();
|
const formatter = new Formatter();
|
||||||
const { cst, fnc, kwd, str } = formatColors;
|
|
||||||
|
|
||||||
formatter.add(`
|
formatter.add(`
|
||||||
|
${kwd('const')} ${cst('assert')} = ${fnc('require')}(${str('assert')});
|
||||||
${kwd('const')} { ${cst('chromium')}, ${cst('firefox')}, ${cst('webkit')} } = ${fnc('require')}(${str('playwright')});
|
${kwd('const')} { ${cst('chromium')}, ${cst('firefox')}, ${cst('webkit')} } = ${fnc('require')}(${str('playwright')});
|
||||||
|
|
||||||
(${kwd('async')}() => {
|
(${kwd('async')}() => {
|
||||||
@ -38,6 +41,7 @@ export class TerminalOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addAction(action: Action) {
|
addAction(action: Action) {
|
||||||
|
// We augment last action based on the type.
|
||||||
let eraseLastAction = false;
|
let eraseLastAction = false;
|
||||||
if (this._lastAction && action.name === 'fill' && this._lastAction.name === 'fill') {
|
if (this._lastAction && action.name === 'fill' && this._lastAction.name === 'fill') {
|
||||||
if (action.selector === this._lastAction.selector)
|
if (action.selector === this._lastAction.selector)
|
||||||
@ -57,9 +61,11 @@ export class TerminalOutput {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_printAction(action: Action, eraseLastAction: boolean) {
|
_printAction(action: Action, eraseLastAction: boolean) {
|
||||||
|
// We erase terminating `})();` at all times.
|
||||||
let eraseLines = 1;
|
let eraseLines = 1;
|
||||||
if (eraseLastAction && this._lastActionText)
|
if (eraseLastAction && this._lastActionText)
|
||||||
eraseLines += this._lastActionText.split('\n').length;
|
eraseLines += this._lastActionText.split('\n').length;
|
||||||
|
// And we erase the last action too if augmenting.
|
||||||
for (let i = 0; i < eraseLines; ++i)
|
for (let i = 0; i < eraseLines; ++i)
|
||||||
process.stdout.write('\u001B[F\u001B[2K');
|
process.stdout.write('\u001B[F\u001B[2K');
|
||||||
|
|
||||||
@ -82,23 +88,24 @@ export class TerminalOutput {
|
|||||||
|
|
||||||
private _generateAction(action: Action): string {
|
private _generateAction(action: Action): string {
|
||||||
const formatter = new Formatter(2);
|
const formatter = new Formatter(2);
|
||||||
const { cst, cmt, fnc, kwd, prp, str } = formatColors;
|
|
||||||
formatter.newLine();
|
formatter.newLine();
|
||||||
formatter.add(cmt(actionTitle(action)));
|
formatter.add(cmt(actionTitle(action)));
|
||||||
let navigationSignal: NavigationSignal | undefined;
|
let navigationSignal: NavigationSignal | undefined;
|
||||||
if (action.name !== 'navigate' && action.signals && action.signals.length)
|
if (action.name !== 'navigate' && action.signals && action.signals.length)
|
||||||
navigationSignal = action.signals[action.signals.length - 1];
|
navigationSignal = action.signals[action.signals.length - 1];
|
||||||
|
|
||||||
if (navigationSignal) {
|
const waitForNavigation = navigationSignal && navigationSignal.type === 'await';
|
||||||
|
const assertNavigation = navigationSignal && navigationSignal.type === 'assert';
|
||||||
|
if (waitForNavigation) {
|
||||||
formatter.add(`${kwd('await')} ${cst('Promise')}.${fnc('all')}([
|
formatter.add(`${kwd('await')} ${cst('Promise')}.${fnc('all')}([
|
||||||
${cst('page')}.${fnc('waitForNavigation')}({ ${prp('url')}: ${str(navigationSignal.url)} }),`);
|
${cst('page')}.${fnc('waitForNavigation')}({ ${prp('url')}: ${str(navigationSignal!.url)} }),`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const subject = action.frameUrl ?
|
const subject = action.frameUrl ?
|
||||||
`${cst('page')}.${fnc('frame')}(${formatObject({ url: action.frameUrl })})` : cst('page');
|
`${cst('page')}.${fnc('frame')}(${formatObject({ url: action.frameUrl })})` : cst('page');
|
||||||
|
|
||||||
const prefix = navigationSignal ? '' : kwd('await') + ' ';
|
const prefix = waitForNavigation ? '' : kwd('await') + ' ';
|
||||||
const suffix = navigationSignal ? '' : ';';
|
const suffix = waitForNavigation ? '' : ';';
|
||||||
switch (action.name) {
|
switch (action.name) {
|
||||||
case 'click': {
|
case 'click': {
|
||||||
let method = 'click';
|
let method = 'click';
|
||||||
@ -138,8 +145,10 @@ export class TerminalOutput {
|
|||||||
formatter.add(`${prefix}${subject}.${fnc('select')}(${str(action.selector)}, ${formatObject(action.options.length > 1 ? action.options : action.options[0])})${suffix}`);
|
formatter.add(`${prefix}${subject}.${fnc('select')}(${str(action.selector)}, ${formatObject(action.options.length > 1 ? action.options : action.options[0])})${suffix}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (navigationSignal)
|
if (waitForNavigation)
|
||||||
formatter.add(`]);`);
|
formatter.add(`]);`);
|
||||||
|
else if (assertNavigation)
|
||||||
|
formatter.add(` ${cst('assert')}.${fnc('equal')}(${cst('page')}.${fnc('url')}(), ${str(navigationSignal!.url)});`);
|
||||||
return formatter.format();
|
return formatter.format();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +161,6 @@ function formatOptions(value: any): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function formatObject(value: any): string {
|
function formatObject(value: any): string {
|
||||||
const { prp, str } = formatColors;
|
|
||||||
if (typeof value === 'string')
|
if (typeof value === 'string')
|
||||||
return str(value);
|
return str(value);
|
||||||
if (Array.isArray(value))
|
if (Array.isArray(value))
|
||||||
@ -169,15 +177,3 @@ function formatObject(value: any): string {
|
|||||||
return String(value);
|
return String(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toModifiers(modifiers: number): ('Alt' | 'Control' | 'Meta' | 'Shift')[] {
|
|
||||||
const result: ('Alt' | 'Control' | 'Meta' | 'Shift')[] = [];
|
|
||||||
if (modifiers & 1)
|
|
||||||
result.push('Alt');
|
|
||||||
if (modifiers & 2)
|
|
||||||
result.push('Control');
|
|
||||||
if (modifiers & 4)
|
|
||||||
result.push('Meta');
|
|
||||||
if (modifiers & 8)
|
|
||||||
result.push('Shift');
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
@ -94,7 +94,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||||||
return this._injectedScriptPromise;
|
return this._injectedScriptPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
debugScript(): Promise<js.JSHandle<DebugScript> | undefined> {
|
createDebugScript(options: { record?: boolean, console?: boolean }): Promise<js.JSHandle<DebugScript> | undefined> {
|
||||||
if (!helper.isDebugMode())
|
if (!helper.isDebugMode())
|
||||||
return Promise.resolve(undefined);
|
return Promise.resolve(undefined);
|
||||||
|
|
||||||
@ -102,7 +102,7 @@ export class FrameExecutionContext extends js.ExecutionContext {
|
|||||||
const source = `new (${debugScriptSource.source})()`;
|
const source = `new (${debugScriptSource.source})()`;
|
||||||
this._debugScriptPromise = this._delegate.rawEvaluate(source).then(objectId => new js.JSHandle(this, 'object', objectId)).then(async debugScript => {
|
this._debugScriptPromise = this._delegate.rawEvaluate(source).then(objectId => new js.JSHandle(this, 'object', objectId)).then(async debugScript => {
|
||||||
const injectedScript = await this.injectedScript();
|
const injectedScript = await this.injectedScript();
|
||||||
await debugScript.evaluate((debugScript: DebugScript, injectedScript) => debugScript.initialize(injectedScript), injectedScript);
|
await debugScript.evaluate((debugScript: DebugScript, { injectedScript, options }) => debugScript.initialize(injectedScript, options), { injectedScript, options });
|
||||||
return debugScript;
|
return debugScript;
|
||||||
}).catch(e => undefined);
|
}).catch(e => undefined);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user