mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: move actions types to recorder (#32839)
This commit is contained in:
parent
0d79291604
commit
c105de4436
@ -15,7 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { BrowserContextOptions } from '../../../types/types';
|
import type { BrowserContextOptions } from '../../../types/types';
|
||||||
import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
||||||
|
import type * as actions from '@recorder/actions';
|
||||||
import { sanitizeDeviceOptions, toClickOptionsForSourceCode, toKeyboardModifiers, toSignalMap } from './language';
|
import { sanitizeDeviceOptions, toClickOptionsForSourceCode, toKeyboardModifiers, toSignalMap } from './language';
|
||||||
import { escapeWithQuotes, asLocator } from '../../utils';
|
import { escapeWithQuotes, asLocator } from '../../utils';
|
||||||
import { deviceDescriptors } from '../deviceDescriptors';
|
import { deviceDescriptors } from '../deviceDescriptors';
|
||||||
@ -45,14 +46,14 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||||||
this._mode = mode;
|
this._mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAction(actionInContext: ActionInContext): string {
|
generateAction(actionInContext: actions.ActionInContext): string {
|
||||||
const action = this._generateActionInner(actionInContext);
|
const action = this._generateActionInner(actionInContext);
|
||||||
if (action)
|
if (action)
|
||||||
return action;
|
return action;
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
_generateActionInner(actionInContext: ActionInContext): string {
|
_generateActionInner(actionInContext: actions.ActionInContext): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
if (this._mode !== 'library' && (action.name === 'openPage' || action.name === 'closePage'))
|
if (this._mode !== 'library' && (action.name === 'openPage' || action.name === 'closePage'))
|
||||||
return '';
|
return '';
|
||||||
@ -101,7 +102,7 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||||||
return formatter.format();
|
return formatter.format();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateActionCall(subject: string, actionInContext: ActionInContext): string {
|
private _generateActionCall(subject: string, actionInContext: actions.ActionInContext): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
switch (action.name) {
|
switch (action.name) {
|
||||||
case 'openPage':
|
case 'openPage':
|
||||||
|
@ -16,7 +16,8 @@
|
|||||||
|
|
||||||
import type { BrowserContextOptions } from '../../../types/types';
|
import type { BrowserContextOptions } from '../../../types/types';
|
||||||
import type * as types from '../types';
|
import type * as types from '../types';
|
||||||
import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
import type * as actions from '@recorder/actions';
|
||||||
|
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
||||||
import { toClickOptionsForSourceCode, toKeyboardModifiers, toSignalMap } from './language';
|
import { toClickOptionsForSourceCode, toKeyboardModifiers, toSignalMap } from './language';
|
||||||
import { deviceDescriptors } from '../deviceDescriptors';
|
import { deviceDescriptors } from '../deviceDescriptors';
|
||||||
import { JavaScriptFormatter } from './javascript';
|
import { JavaScriptFormatter } from './javascript';
|
||||||
@ -44,7 +45,7 @@ export class JavaLanguageGenerator implements LanguageGenerator {
|
|||||||
this._mode = mode;
|
this._mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAction(actionInContext: ActionInContext): string {
|
generateAction(actionInContext: actions.ActionInContext): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
const pageAlias = actionInContext.frame.pageAlias;
|
const pageAlias = actionInContext.frame.pageAlias;
|
||||||
const offset = this._mode === 'junit' ? 4 : 6;
|
const offset = this._mode === 'junit' ? 4 : 6;
|
||||||
@ -90,7 +91,7 @@ export class JavaLanguageGenerator implements LanguageGenerator {
|
|||||||
return formatter.format();
|
return formatter.format();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateActionCall(subject: string, actionInContext: ActionInContext, inFrameLocator: boolean): string {
|
private _generateActionCall(subject: string, actionInContext: actions.ActionInContext, inFrameLocator: boolean): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
switch (action.name) {
|
switch (action.name) {
|
||||||
case 'openPage':
|
case 'openPage':
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { BrowserContextOptions } from '../../../types/types';
|
import type { BrowserContextOptions } from '../../../types/types';
|
||||||
import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
||||||
|
import type * as actions from '@recorder/actions';
|
||||||
import { sanitizeDeviceOptions, toSignalMap, toKeyboardModifiers, toClickOptionsForSourceCode } from './language';
|
import { sanitizeDeviceOptions, toSignalMap, toKeyboardModifiers, toClickOptionsForSourceCode } from './language';
|
||||||
import { deviceDescriptors } from '../deviceDescriptors';
|
import { deviceDescriptors } from '../deviceDescriptors';
|
||||||
import { escapeWithQuotes, asLocator } from '../../utils';
|
import { escapeWithQuotes, asLocator } from '../../utils';
|
||||||
@ -33,7 +34,7 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator {
|
|||||||
this._isTest = isTest;
|
this._isTest = isTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAction(actionInContext: ActionInContext): string {
|
generateAction(actionInContext: actions.ActionInContext): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
if (this._isTest && (action.name === 'openPage' || action.name === 'closePage'))
|
if (this._isTest && (action.name === 'openPage' || action.name === 'closePage'))
|
||||||
return '';
|
return '';
|
||||||
@ -74,7 +75,7 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator {
|
|||||||
return formatter.format();
|
return formatter.format();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateActionCall(subject: string, actionInContext: ActionInContext): string {
|
private _generateActionCall(subject: string, actionInContext: actions.ActionInContext): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
switch (action.name) {
|
switch (action.name) {
|
||||||
case 'openPage':
|
case 'openPage':
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { asLocator } from '../../utils';
|
import { asLocator } from '../../utils';
|
||||||
import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
import type * as actions from '@recorder/actions';
|
||||||
|
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
||||||
|
|
||||||
export class JsonlLanguageGenerator implements LanguageGenerator {
|
export class JsonlLanguageGenerator implements LanguageGenerator {
|
||||||
id = 'jsonl';
|
id = 'jsonl';
|
||||||
@ -23,7 +24,7 @@ export class JsonlLanguageGenerator implements LanguageGenerator {
|
|||||||
name = 'JSONL';
|
name = 'JSONL';
|
||||||
highlighter = 'javascript' as Language;
|
highlighter = 'javascript' as Language;
|
||||||
|
|
||||||
generateAction(actionInContext: ActionInContext): string {
|
generateAction(actionInContext: actions.ActionInContext): string {
|
||||||
const locator = (actionInContext.action as any).selector ? JSON.parse(asLocator('jsonl', (actionInContext.action as any).selector)) : undefined;
|
const locator = (actionInContext.action as any).selector ? JSON.parse(asLocator('jsonl', (actionInContext.action as any).selector)) : undefined;
|
||||||
const entry = {
|
const entry = {
|
||||||
...actionInContext.action,
|
...actionInContext.action,
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { BrowserContextOptions } from '../../..';
|
import type { BrowserContextOptions } from '../../..';
|
||||||
import type * as actions from '../recorder/recorderActions';
|
import type * as actions from '@recorder/actions';
|
||||||
import type * as types from '../types';
|
import type * as types from '../types';
|
||||||
import type { ActionInContext, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
import type { LanguageGenerator, LanguageGeneratorOptions } from './types';
|
||||||
|
|
||||||
export function generateCode(actions: ActionInContext[], languageGenerator: LanguageGenerator, options: LanguageGeneratorOptions) {
|
export function generateCode(actions: actions.ActionInContext[], languageGenerator: LanguageGenerator, options: LanguageGeneratorOptions) {
|
||||||
const header = languageGenerator.generateHeader(options);
|
const header = languageGenerator.generateHeader(options);
|
||||||
const footer = languageGenerator.generateFooter(options.saveStorage);
|
const footer = languageGenerator.generateFooter(options.saveStorage);
|
||||||
const actionTexts = actions.map(a => languageGenerator.generateAction(a)).filter(Boolean);
|
const actionTexts = actions.map(a => languageGenerator.generateAction(a)).filter(Boolean);
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { BrowserContextOptions } from '../../../types/types';
|
import type { BrowserContextOptions } from '../../../types/types';
|
||||||
import type { ActionInContext, Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
import type { Language, LanguageGenerator, LanguageGeneratorOptions } from './types';
|
||||||
|
import type * as actions from '@recorder/actions';
|
||||||
import { sanitizeDeviceOptions, toSignalMap, toKeyboardModifiers, toClickOptionsForSourceCode } from './language';
|
import { sanitizeDeviceOptions, toSignalMap, toKeyboardModifiers, toClickOptionsForSourceCode } from './language';
|
||||||
import { escapeWithQuotes, toSnakeCase, asLocator } from '../../utils';
|
import { escapeWithQuotes, toSnakeCase, asLocator } from '../../utils';
|
||||||
import { deviceDescriptors } from '../deviceDescriptors';
|
import { deviceDescriptors } from '../deviceDescriptors';
|
||||||
@ -40,7 +41,7 @@ export class PythonLanguageGenerator implements LanguageGenerator {
|
|||||||
this._asyncPrefix = isAsync ? 'async ' : '';
|
this._asyncPrefix = isAsync ? 'async ' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAction(actionInContext: ActionInContext): string {
|
generateAction(actionInContext: actions.ActionInContext): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
if (this._isPyTest && (action.name === 'openPage' || action.name === 'closePage'))
|
if (this._isPyTest && (action.name === 'openPage' || action.name === 'closePage'))
|
||||||
return '';
|
return '';
|
||||||
@ -83,7 +84,7 @@ export class PythonLanguageGenerator implements LanguageGenerator {
|
|||||||
return formatter.format();
|
return formatter.format();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _generateActionCall(subject: string, actionInContext: ActionInContext): string {
|
private _generateActionCall(subject: string, actionInContext: actions.ActionInContext): string {
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
switch (action.name) {
|
switch (action.name) {
|
||||||
case 'openPage':
|
case 'openPage':
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { BrowserContextOptions, LaunchOptions } from '../../../types/types';
|
import type { BrowserContextOptions, LaunchOptions } from '../../../types/types';
|
||||||
import type * as actions from '../recorder/recorderActions';
|
import type * as actions from '@recorder/actions';
|
||||||
import type { Language } from '../../utils';
|
import type { Language } from '../../utils';
|
||||||
export type { Language } from '../../utils';
|
export type { Language } from '../../utils';
|
||||||
|
|
||||||
@ -27,24 +27,12 @@ export type LanguageGeneratorOptions = {
|
|||||||
saveStorage?: string;
|
saveStorage?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FrameDescription = {
|
|
||||||
pageAlias: string;
|
|
||||||
framePath: string[];
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ActionInContext = {
|
|
||||||
frame: FrameDescription;
|
|
||||||
description?: string;
|
|
||||||
action: actions.Action;
|
|
||||||
timestamp: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export interface LanguageGenerator {
|
export interface LanguageGenerator {
|
||||||
id: string;
|
id: string;
|
||||||
groupName: string;
|
groupName: string;
|
||||||
name: string;
|
name: string;
|
||||||
highlighter: Language;
|
highlighter: Language;
|
||||||
generateHeader(options: LanguageGeneratorOptions): string;
|
generateHeader(options: LanguageGeneratorOptions): string;
|
||||||
generateAction(actionInContext: ActionInContext): string;
|
generateAction(actionInContext: actions.ActionInContext): string;
|
||||||
generateFooter(saveStorage: string | undefined): string;
|
generateFooter(saveStorage: string | undefined): string;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes';
|
import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes';
|
||||||
import type * as actions from '../../recorder/recorderActions';
|
import type * as actions from '@recorder/actions';
|
||||||
import type { InjectedScript } from '../injectedScript';
|
import type { InjectedScript } from '../injectedScript';
|
||||||
import { Recorder } from './recorder';
|
import { Recorder } from './recorder';
|
||||||
import type { RecorderDelegate } from './recorder';
|
import type { RecorderDelegate } from './recorder';
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type * as actions from '../../recorder/recorderActions';
|
import type * as actions from '@recorder/actions';
|
||||||
import type { InjectedScript } from '../injectedScript';
|
import type { InjectedScript } from '../injectedScript';
|
||||||
import type { Point } from '../../../common/types';
|
import type { Point } from '../../../common/types';
|
||||||
import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes';
|
import type { Mode, OverlayState, UIState } from '@recorder/recorderTypes';
|
||||||
|
@ -28,6 +28,7 @@ import type { CallMetadata, InstrumentationListener, SdkObject } from './instrum
|
|||||||
import { ContextRecorder, generateFrameSelector } from './recorder/contextRecorder';
|
import { ContextRecorder, generateFrameSelector } from './recorder/contextRecorder';
|
||||||
import type { IRecorderAppFactory, IRecorderApp, IRecorder } from './recorder/recorderFrontend';
|
import type { IRecorderAppFactory, IRecorderApp, IRecorder } from './recorder/recorderFrontend';
|
||||||
import { buildFullSelector, metadataToCallLog } from './recorder/recorderUtils';
|
import { buildFullSelector, metadataToCallLog } from './recorder/recorderUtils';
|
||||||
|
import type * as actions from '@recorder/actions';
|
||||||
|
|
||||||
const recorderSymbol = Symbol('recorderSymbol');
|
const recorderSymbol = Symbol('recorderSymbol');
|
||||||
|
|
||||||
@ -135,8 +136,9 @@ export class Recorder implements InstrumentationListener, IRecorder {
|
|||||||
this._context.instrumentation.removeListener(this);
|
this._context.instrumentation.removeListener(this);
|
||||||
this._recorderApp?.close().catch(() => {});
|
this._recorderApp?.close().catch(() => {});
|
||||||
});
|
});
|
||||||
this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[] }) => {
|
this._contextRecorder.on(ContextRecorder.Events.Change, (data: { sources: Source[], actions: actions.ActionInContext[] }) => {
|
||||||
this._recorderSources = data.sources;
|
this._recorderSources = data.sources;
|
||||||
|
recorderApp.setActions(data.actions);
|
||||||
this._pushAllSources();
|
this._pushAllSources();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -21,12 +21,12 @@ import * as recorderSource from '../../generated/pollingRecorderSource';
|
|||||||
import { eventsHelper, monotonicTime, quoteCSSAttributeValue, type RegisteredListener } from '../../utils';
|
import { eventsHelper, monotonicTime, quoteCSSAttributeValue, type RegisteredListener } from '../../utils';
|
||||||
import { raceAgainstDeadline } from '../../utils/timeoutRunner';
|
import { raceAgainstDeadline } from '../../utils/timeoutRunner';
|
||||||
import { BrowserContext } from '../browserContext';
|
import { BrowserContext } from '../browserContext';
|
||||||
import type { ActionInContext, FrameDescription, LanguageGeneratorOptions, Language, LanguageGenerator } from '../codegen/types';
|
import type { LanguageGeneratorOptions, Language, LanguageGenerator } from '../codegen/types';
|
||||||
import { languageSet } from '../codegen/languages';
|
import { languageSet } from '../codegen/languages';
|
||||||
import type { Dialog } from '../dialog';
|
import type { Dialog } from '../dialog';
|
||||||
import { Frame } from '../frames';
|
import { Frame } from '../frames';
|
||||||
import { Page } from '../page';
|
import { Page } from '../page';
|
||||||
import type * as actions from './recorderActions';
|
import type * as actions from '@recorder/actions';
|
||||||
import { ThrottledFile } from './throttledFile';
|
import { ThrottledFile } from './throttledFile';
|
||||||
import { RecorderCollection } from './recorderCollection';
|
import { RecorderCollection } from './recorderCollection';
|
||||||
import { generateCode } from '../codegen/language';
|
import { generateCode } from '../codegen/language';
|
||||||
@ -34,7 +34,7 @@ import { generateCode } from '../codegen/language';
|
|||||||
type BindingSource = { frame: Frame, page: Page };
|
type BindingSource = { frame: Frame, page: Page };
|
||||||
|
|
||||||
export interface ContextRecorderDelegate {
|
export interface ContextRecorderDelegate {
|
||||||
rewriteActionInContext?(pageAliases: Map<Page, string>, actionInContext: ActionInContext): Promise<void>;
|
rewriteActionInContext?(pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ContextRecorder extends EventEmitter {
|
export class ContextRecorder extends EventEmitter {
|
||||||
@ -76,7 +76,7 @@ export class ContextRecorder extends EventEmitter {
|
|||||||
};
|
};
|
||||||
|
|
||||||
this._collection = new RecorderCollection(codegenMode, context, this._pageAliases);
|
this._collection = new RecorderCollection(codegenMode, context, this._pageAliases);
|
||||||
this._collection.on('change', (actions: ActionInContext[]) => {
|
this._collection.on('change', (actions: actions.ActionInContext[]) => {
|
||||||
this._recorderSources = [];
|
this._recorderSources = [];
|
||||||
for (const languageGenerator of this._orderedLanguages) {
|
for (const languageGenerator of this._orderedLanguages) {
|
||||||
const { header, footer, actionTexts, text } = generateCode(actions, languageGenerator, languageGeneratorOptions);
|
const { header, footer, actionTexts, text } = generateCode(actions, languageGenerator, languageGeneratorOptions);
|
||||||
@ -97,7 +97,10 @@ export class ContextRecorder extends EventEmitter {
|
|||||||
if (languageGenerator === this._orderedLanguages[0])
|
if (languageGenerator === this._orderedLanguages[0])
|
||||||
this._throttledOutputFile?.setContent(source.text);
|
this._throttledOutputFile?.setContent(source.text);
|
||||||
}
|
}
|
||||||
this.emit(ContextRecorder.Events.Change, { sources: this._recorderSources });
|
this.emit(ContextRecorder.Events.Change, {
|
||||||
|
sources: this._recorderSources,
|
||||||
|
actions
|
||||||
|
});
|
||||||
});
|
});
|
||||||
context.on(BrowserContext.Events.BeforeClose, () => {
|
context.on(BrowserContext.Events.BeforeClose, () => {
|
||||||
this._throttledOutputFile?.flush();
|
this._throttledOutputFile?.flush();
|
||||||
@ -205,14 +208,14 @@ export class ContextRecorder extends EventEmitter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _describeMainFrame(page: Page): FrameDescription {
|
private _describeMainFrame(page: Page): actions.FrameDescription {
|
||||||
return {
|
return {
|
||||||
pageAlias: this._pageAliases.get(page)!,
|
pageAlias: this._pageAliases.get(page)!,
|
||||||
framePath: [],
|
framePath: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _describeFrame(frame: Frame): Promise<FrameDescription> {
|
private async _describeFrame(frame: Frame): Promise<actions.FrameDescription> {
|
||||||
return {
|
return {
|
||||||
pageAlias: this._pageAliases.get(frame._page)!,
|
pageAlias: this._pageAliases.get(frame._page)!,
|
||||||
framePath: await generateFrameSelector(frame),
|
framePath: await generateFrameSelector(frame),
|
||||||
@ -223,9 +226,9 @@ export class ContextRecorder extends EventEmitter {
|
|||||||
return this._params.testIdAttributeName || this._context.selectors().testIdAttributeName() || 'data-testid';
|
return this._params.testIdAttributeName || this._context.selectors().testIdAttributeName() || 'data-testid';
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _createActionInContext(frame: Frame, action: actions.Action): Promise<ActionInContext> {
|
private async _createActionInContext(frame: Frame, action: actions.Action): Promise<actions.ActionInContext> {
|
||||||
const frameDescription = await this._describeFrame(frame);
|
const frameDescription = await this._describeFrame(frame);
|
||||||
const actionInContext: ActionInContext = {
|
const actionInContext: actions.ActionInContext = {
|
||||||
frame: frameDescription,
|
frame: frameDescription,
|
||||||
action,
|
action,
|
||||||
description: undefined,
|
description: undefined,
|
||||||
|
@ -20,27 +20,14 @@ import type { Page } from '../page';
|
|||||||
import { ProgressController } from '../progress';
|
import { ProgressController } from '../progress';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { serverSideCallMetadata } from '../instrumentation';
|
import { serverSideCallMetadata } from '../instrumentation';
|
||||||
import type { CallLog, EventData, Mode, Source } from '@recorder/recorderTypes';
|
import type { CallLog, Mode, Source } from '@recorder/recorderTypes';
|
||||||
import { isUnderTest } from '../../utils';
|
import { isUnderTest } from '../../utils';
|
||||||
import { mime } from '../../utilsBundle';
|
import { mime } from '../../utilsBundle';
|
||||||
import { syncLocalStorageWithSettings } from '../launchApp';
|
import { syncLocalStorageWithSettings } from '../launchApp';
|
||||||
import type { BrowserContext } from '../browserContext';
|
import type { BrowserContext } from '../browserContext';
|
||||||
import { launchApp } from '../launchApp';
|
import { launchApp } from '../launchApp';
|
||||||
import type { IRecorder, IRecorderApp, IRecorderAppFactory } from './recorderFrontend';
|
import type { IRecorder, IRecorderApp, IRecorderAppFactory } from './recorderFrontend';
|
||||||
|
import type * as actions from '@recorder/actions';
|
||||||
declare global {
|
|
||||||
interface Window {
|
|
||||||
playwrightSetFile: (file: string) => void;
|
|
||||||
playwrightSetMode: (mode: Mode) => void;
|
|
||||||
playwrightSetPaused: (paused: boolean) => void;
|
|
||||||
playwrightSetSources: (sources: Source[]) => void;
|
|
||||||
playwrightSetOverlayVisible: (visible: boolean) => void;
|
|
||||||
playwrightSetSelector: (selector: string, focus?: boolean) => void;
|
|
||||||
playwrightUpdateLogs: (callLogs: CallLog[]) => void;
|
|
||||||
dispatch(data: EventData): Promise<void>;
|
|
||||||
saveSettings?(): Promise<void>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EmptyRecorderApp extends EventEmitter implements IRecorderApp {
|
export class EmptyRecorderApp extends EventEmitter implements IRecorderApp {
|
||||||
wsEndpointForTest: undefined;
|
wsEndpointForTest: undefined;
|
||||||
@ -51,6 +38,7 @@ export class EmptyRecorderApp extends EventEmitter implements IRecorderApp {
|
|||||||
async setSelector(selector: string, userGesture?: boolean): Promise<void> {}
|
async setSelector(selector: string, userGesture?: boolean): Promise<void> {}
|
||||||
async updateCallLogs(callLogs: CallLog[]): Promise<void> {}
|
async updateCallLogs(callLogs: CallLog[]): Promise<void> {}
|
||||||
async setSources(sources: Source[]): Promise<void> {}
|
async setSources(sources: Source[]): Promise<void> {}
|
||||||
|
async setActions(actions: actions.ActionInContext[]): Promise<void> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RecorderApp extends EventEmitter implements IRecorderApp {
|
export class RecorderApp extends EventEmitter implements IRecorderApp {
|
||||||
@ -167,6 +155,9 @@ export class RecorderApp extends EventEmitter implements IRecorderApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setActions(actions: actions.ActionInContext[]): Promise<void> {
|
||||||
|
}
|
||||||
|
|
||||||
async setSelector(selector: string, userGesture?: boolean): Promise<void> {
|
async setSelector(selector: string, userGesture?: boolean): Promise<void> {
|
||||||
if (userGesture) {
|
if (userGesture) {
|
||||||
if (this._recorder?.mode() === 'inspecting') {
|
if (this._recorder?.mode() === 'inspecting') {
|
||||||
|
@ -17,8 +17,8 @@
|
|||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import type { Frame } from '../frames';
|
import type { Frame } from '../frames';
|
||||||
import type { Page } from '../page';
|
import type { Page } from '../page';
|
||||||
import type { Signal } from './recorderActions';
|
import type { Signal } from '../../../../recorder/src/actions';
|
||||||
import type { ActionInContext } from '../codegen/types';
|
import type * as actions from '@recorder/actions';
|
||||||
import { monotonicTime } from '../../utils/time';
|
import { monotonicTime } from '../../utils/time';
|
||||||
import { callMetadataForAction, collapseActions, traceEventsToAction } from './recorderUtils';
|
import { callMetadataForAction, collapseActions, traceEventsToAction } from './recorderUtils';
|
||||||
import { serializeError } from '../errors';
|
import { serializeError } from '../errors';
|
||||||
@ -28,7 +28,7 @@ import { isUnderTest } from '../../utils/debug';
|
|||||||
import type { BrowserContext } from '../browserContext';
|
import type { BrowserContext } from '../browserContext';
|
||||||
|
|
||||||
export class RecorderCollection extends EventEmitter {
|
export class RecorderCollection extends EventEmitter {
|
||||||
private _actions: ActionInContext[] = [];
|
private _actions: actions.ActionInContext[] = [];
|
||||||
private _enabled = false;
|
private _enabled = false;
|
||||||
private _pageAliases: Map<Page, string>;
|
private _pageAliases: Map<Page, string>;
|
||||||
private _context: BrowserContext;
|
private _context: BrowserContext;
|
||||||
@ -55,13 +55,13 @@ export class RecorderCollection extends EventEmitter {
|
|||||||
this._enabled = enabled;
|
this._enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
async performAction(actionInContext: ActionInContext) {
|
async performAction(actionInContext: actions.ActionInContext) {
|
||||||
await this._addAction(actionInContext, async callMetadata => {
|
await this._addAction(actionInContext, async callMetadata => {
|
||||||
await performAction(callMetadata, this._pageAliases, actionInContext);
|
await performAction(callMetadata, this._pageAliases, actionInContext);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
addRecordedAction(actionInContext: ActionInContext) {
|
addRecordedAction(actionInContext: actions.ActionInContext) {
|
||||||
if (['openPage', 'closePage'].includes(actionInContext.action.name)) {
|
if (['openPage', 'closePage'].includes(actionInContext.action.name)) {
|
||||||
this._actions.push(actionInContext);
|
this._actions.push(actionInContext);
|
||||||
this._fireChange();
|
this._fireChange();
|
||||||
@ -70,7 +70,7 @@ export class RecorderCollection extends EventEmitter {
|
|||||||
this._addAction(actionInContext).catch(() => {});
|
this._addAction(actionInContext).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _addAction(actionInContext: ActionInContext, callback?: (callMetadata: CallMetadata) => Promise<void>) {
|
private async _addAction(actionInContext: actions.ActionInContext, callback?: (callMetadata: CallMetadata) => Promise<void>) {
|
||||||
if (!this._enabled)
|
if (!this._enabled)
|
||||||
return;
|
return;
|
||||||
if (actionInContext.action.name === 'openPage' || actionInContext.action.name === 'closePage') {
|
if (actionInContext.action.name === 'openPage' || actionInContext.action.name === 'closePage') {
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import type * as actions from '@recorder/actions';
|
||||||
import type { CallLog, Mode, Source } from '@recorder/recorderTypes';
|
import type { CallLog, Mode, Source } from '@recorder/recorderTypes';
|
||||||
import type { EventEmitter } from 'events';
|
import type { EventEmitter } from 'events';
|
||||||
|
|
||||||
@ -31,6 +32,7 @@ export interface IRecorderApp extends EventEmitter {
|
|||||||
setSelector(selector: string, userGesture?: boolean): Promise<void>;
|
setSelector(selector: string, userGesture?: boolean): Promise<void>;
|
||||||
updateCallLogs(callLogs: CallLog[]): Promise<void>;
|
updateCallLogs(callLogs: CallLog[]): Promise<void>;
|
||||||
setSources(sources: Source[]): Promise<void>;
|
setSources(sources: Source[]): Promise<void>;
|
||||||
|
setActions(actions: actions.ActionInContext[]): Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type IRecorderAppFactory = (recorder: IRecorder) => Promise<IRecorderApp>;
|
export type IRecorderAppFactory = (recorder: IRecorder) => Promise<IRecorderApp>;
|
||||||
|
@ -24,6 +24,7 @@ import type { BrowserContext } from '../browserContext';
|
|||||||
import type { HttpServer, Transport } from '../../utils/httpServer';
|
import type { HttpServer, Transport } from '../../utils/httpServer';
|
||||||
import type { Page } from '../page';
|
import type { Page } from '../page';
|
||||||
import { ManualPromise } from '../../utils/manualPromise';
|
import { ManualPromise } from '../../utils/manualPromise';
|
||||||
|
import type * as actions from '@recorder/actions';
|
||||||
|
|
||||||
export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp {
|
export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp {
|
||||||
readonly wsEndpointForTest: string | undefined;
|
readonly wsEndpointForTest: string | undefined;
|
||||||
@ -84,6 +85,10 @@ export class RecorderInTraceViewer extends EventEmitter implements IRecorderApp
|
|||||||
this.close();
|
this.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setActions(actions: actions.ActionInContext[]): Promise<void> {
|
||||||
|
this._transport.deliverEvent('setActions', { actions });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openApp(trace: string, options?: TraceViewerServerOptions & { headless?: boolean }): Promise<{ wsEndpointForTest: string | undefined, tracePage: Page, traceServer: HttpServer }> {
|
async function openApp(trace: string, options?: TraceViewerServerOptions & { headless?: boolean }): Promise<{ wsEndpointForTest: string | undefined, tracePage: Page, traceServer: HttpServer }> {
|
||||||
|
@ -16,14 +16,13 @@
|
|||||||
|
|
||||||
import { serializeExpectedTextValues } from '../../utils';
|
import { serializeExpectedTextValues } from '../../utils';
|
||||||
import { toKeyboardModifiers } from '../codegen/language';
|
import { toKeyboardModifiers } from '../codegen/language';
|
||||||
import type { ActionInContext } from '../codegen/types';
|
|
||||||
import type { CallMetadata } from '../instrumentation';
|
import type { CallMetadata } from '../instrumentation';
|
||||||
import type { Page } from '../page';
|
import type { Page } from '../page';
|
||||||
import type * as actions from './recorderActions';
|
import type * as actions from '@recorder/actions';
|
||||||
import type * as types from '../types';
|
import type * as types from '../types';
|
||||||
import { buildFullSelector, mainFrameForAction } from './recorderUtils';
|
import { buildFullSelector, mainFrameForAction } from './recorderUtils';
|
||||||
|
|
||||||
export async function performAction(callMetadata: CallMetadata, pageAliases: Map<Page, string>, actionInContext: ActionInContext) {
|
export async function performAction(callMetadata: CallMetadata, pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext) {
|
||||||
const mainFrame = mainFrameForAction(pageAliases, actionInContext);
|
const mainFrame = mainFrameForAction(pageAliases, actionInContext);
|
||||||
const { action } = actionInContext;
|
const { action } = actionInContext;
|
||||||
|
|
||||||
|
@ -17,9 +17,8 @@
|
|||||||
import type { CallMetadata } from '../instrumentation';
|
import type { CallMetadata } from '../instrumentation';
|
||||||
import type { CallLog, CallLogStatus } from '@recorder/recorderTypes';
|
import type { CallLog, CallLogStatus } from '@recorder/recorderTypes';
|
||||||
import type { Page } from '../page';
|
import type { Page } from '../page';
|
||||||
import type { ActionInContext } from '../codegen/types';
|
|
||||||
import type { Frame } from '../frames';
|
import type { Frame } from '../frames';
|
||||||
import type * as actions from './recorderActions';
|
import type * as actions from '@recorder/actions';
|
||||||
import type * as channels from '@protocol/channels';
|
import type * as channels from '@protocol/channels';
|
||||||
import type * as trace from '@trace/trace';
|
import type * as trace from '@trace/trace';
|
||||||
import { fromKeyboardModifiers, toKeyboardModifiers } from '../codegen/language';
|
import { fromKeyboardModifiers, toKeyboardModifiers } from '../codegen/language';
|
||||||
@ -60,7 +59,7 @@ export function buildFullSelector(framePath: string[], selector: string) {
|
|||||||
return [...framePath, selector].join(' >> internal:control=enter-frame >> ');
|
return [...framePath, selector].join(' >> internal:control=enter-frame >> ');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mainFrameForAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext): Frame {
|
export function mainFrameForAction(pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext): Frame {
|
||||||
const pageAlias = actionInContext.frame.pageAlias;
|
const pageAlias = actionInContext.frame.pageAlias;
|
||||||
const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0];
|
const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0];
|
||||||
if (!page)
|
if (!page)
|
||||||
@ -68,7 +67,7 @@ export function mainFrameForAction(pageAliases: Map<Page, string>, actionInConte
|
|||||||
return page.mainFrame();
|
return page.mainFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function frameForAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext, action: actions.ActionWithSelector): Promise<Frame> {
|
export async function frameForAction(pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext, action: actions.ActionWithSelector): Promise<Frame> {
|
||||||
const pageAlias = actionInContext.frame.pageAlias;
|
const pageAlias = actionInContext.frame.pageAlias;
|
||||||
const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0];
|
const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0];
|
||||||
if (!page)
|
if (!page)
|
||||||
@ -80,7 +79,7 @@ export async function frameForAction(pageAliases: Map<Page, string>, actionInCon
|
|||||||
return result.frame;
|
return result.frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceParamsForAction(actionInContext: ActionInContext): { method: string, params: any } {
|
export function traceParamsForAction(actionInContext: actions.ActionInContext): { method: string, params: any } {
|
||||||
const { action } = actionInContext;
|
const { action } = actionInContext;
|
||||||
|
|
||||||
switch (action.name) {
|
switch (action.name) {
|
||||||
@ -191,7 +190,7 @@ export function traceParamsForAction(actionInContext: ActionInContext): { method
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function callMetadataForAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext): { callMetadata: CallMetadata, mainFrame: Frame } {
|
export function callMetadataForAction(pageAliases: Map<Page, string>, actionInContext: actions.ActionInContext): { callMetadata: CallMetadata, mainFrame: Frame } {
|
||||||
const mainFrame = mainFrameForAction(pageAliases, actionInContext);
|
const mainFrame = mainFrameForAction(pageAliases, actionInContext);
|
||||||
const { method, params } = traceParamsForAction(actionInContext);
|
const { method, params } = traceParamsForAction(actionInContext);
|
||||||
|
|
||||||
@ -212,8 +211,8 @@ export function callMetadataForAction(pageAliases: Map<Page, string>, actionInCo
|
|||||||
return { callMetadata, mainFrame };
|
return { callMetadata, mainFrame };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function traceEventsToAction(events: trace.TraceEvent[]): ActionInContext[] {
|
export function traceEventsToAction(events: trace.TraceEvent[]): actions.ActionInContext[] {
|
||||||
const result: ActionInContext[] = [];
|
const result: actions.ActionInContext[] = [];
|
||||||
const pageAliases = new Map<string, string>();
|
const pageAliases = new Map<string, string>();
|
||||||
let lastDownloadOrdinal = 0;
|
let lastDownloadOrdinal = 0;
|
||||||
let lastDialogOrdinal = 0;
|
let lastDialogOrdinal = 0;
|
||||||
@ -487,8 +486,8 @@ export function traceEventsToAction(events: trace.TraceEvent[]): ActionInContext
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function collapseActions(actions: ActionInContext[]): ActionInContext[] {
|
export function collapseActions(actions: actions.ActionInContext[]): actions.ActionInContext[] {
|
||||||
const result: ActionInContext[] = [];
|
const result: actions.ActionInContext[] = [];
|
||||||
for (const action of actions) {
|
for (const action of actions) {
|
||||||
const lastAction = result[result.length - 1];
|
const lastAction = result[result.length - 1];
|
||||||
const isSameAction = lastAction && lastAction.action.name === action.action.name && lastAction.frame.pageAlias === action.frame.pageAlias && lastAction.frame.framePath.join('|') === action.frame.framePath.join('|');
|
const isSameAction = lastAction && lastAction.action.name === action.action.name && lastAction.frame.pageAlias === action.frame.pageAlias && lastAction.frame.framePath.join('|') === action.frame.framePath.join('|');
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Point } from '../../common/types';
|
type Point = { x: number, y: number };
|
||||||
|
|
||||||
export type ActionName =
|
export type ActionName =
|
||||||
'check' |
|
'check' |
|
||||||
@ -143,3 +143,15 @@ export type DialogSignal = BaseSignal & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Signal = NavigationSignal | PopupSignal | DownloadSignal | DialogSignal;
|
export type Signal = NavigationSignal | PopupSignal | DownloadSignal | DialogSignal;
|
||||||
|
|
||||||
|
export type FrameDescription = {
|
||||||
|
pageAlias: string;
|
||||||
|
framePath: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ActionInContext = {
|
||||||
|
frame: FrameDescription;
|
||||||
|
description?: string;
|
||||||
|
action: Action;
|
||||||
|
timestamp: number;
|
||||||
|
};
|
@ -18,6 +18,8 @@ import type { Language } from 'playwright-core/src/utils/isomorphic/locatorGener
|
|||||||
import type { ResourceSnapshot } from '@trace/snapshot';
|
import type { ResourceSnapshot } from '@trace/snapshot';
|
||||||
import type * as trace from '@trace/trace';
|
import type * as trace from '@trace/trace';
|
||||||
|
|
||||||
|
// *Entry structures are used to pass the trace between the sw and the page.
|
||||||
|
|
||||||
export type ContextEntry = {
|
export type ContextEntry = {
|
||||||
origin: 'testRunner'|'library';
|
origin: 'testRunner'|'library';
|
||||||
traceUrl: string;
|
traceUrl: string;
|
||||||
|
@ -18,7 +18,7 @@ import type { Language } from '@isomorphic/locatorGenerators';
|
|||||||
import type { ResourceSnapshot } from '@trace/snapshot';
|
import type { ResourceSnapshot } from '@trace/snapshot';
|
||||||
import type * as trace from '@trace/trace';
|
import type * as trace from '@trace/trace';
|
||||||
import type { ActionTraceEvent } from '@trace/trace';
|
import type { ActionTraceEvent } from '@trace/trace';
|
||||||
import type { ContextEntry, PageEntry } from '../types/entries';
|
import type { ActionEntry, ContextEntry, PageEntry } from '../types/entries';
|
||||||
import type { StackFrame } from '@protocol/channels';
|
import type { StackFrame } from '@protocol/channels';
|
||||||
|
|
||||||
const contextSymbol = Symbol('context');
|
const contextSymbol = Symbol('context');
|
||||||
@ -39,9 +39,8 @@ export type SourceModel = {
|
|||||||
content: string | undefined;
|
content: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ActionTraceEventInContext = ActionTraceEvent & {
|
export type ActionTraceEventInContext = ActionEntry & {
|
||||||
context: ContextEntry;
|
context: ContextEntry;
|
||||||
log: { time: number, message: string }[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ActionTreeItem = {
|
export type ActionTreeItem = {
|
||||||
|
@ -37,6 +37,7 @@ import { Toolbar } from '@web/components/toolbar';
|
|||||||
import { ToolbarButton, ToolbarSeparator } from '@web/components/toolbarButton';
|
import { ToolbarButton, ToolbarSeparator } from '@web/components/toolbarButton';
|
||||||
import { toggleTheme } from '@web/theme';
|
import { toggleTheme } from '@web/theme';
|
||||||
import { SourceChooser } from '@web/components/sourceChooser';
|
import { SourceChooser } from '@web/components/sourceChooser';
|
||||||
|
import type * as actions from '@recorder/actions';
|
||||||
|
|
||||||
const searchParams = new URLSearchParams(window.location.search);
|
const searchParams = new URLSearchParams(window.location.search);
|
||||||
const guid = searchParams.get('ws');
|
const guid = searchParams.get('ws');
|
||||||
@ -45,6 +46,7 @@ const traceLocation = searchParams.get('trace') + '.json';
|
|||||||
export const RecorderView: React.FunctionComponent = () => {
|
export const RecorderView: React.FunctionComponent = () => {
|
||||||
const [connection, setConnection] = React.useState<Connection | null>(null);
|
const [connection, setConnection] = React.useState<Connection | null>(null);
|
||||||
const [sources, setSources] = React.useState<Source[]>([]);
|
const [sources, setSources] = React.useState<Source[]>([]);
|
||||||
|
const [, setActions] = React.useState<actions.ActionInContext[]>([]);
|
||||||
const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean, sha1: string } | undefined>();
|
const [model, setModel] = React.useState<{ model: MultiTraceModel, isLive: boolean, sha1: string } | undefined>();
|
||||||
const [mode, setMode] = React.useState<Mode>('none');
|
const [mode, setMode] = React.useState<Mode>('none');
|
||||||
const [counter, setCounter] = React.useState(0);
|
const [counter, setCounter] = React.useState(0);
|
||||||
@ -54,7 +56,7 @@ export const RecorderView: React.FunctionComponent = () => {
|
|||||||
const wsURL = new URL(`../${guid}`, window.location.toString());
|
const wsURL = new URL(`../${guid}`, window.location.toString());
|
||||||
wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
|
wsURL.protocol = (window.location.protocol === 'https:' ? 'wss:' : 'ws:');
|
||||||
const webSocket = new WebSocket(wsURL.toString());
|
const webSocket = new WebSocket(wsURL.toString());
|
||||||
setConnection(new Connection(webSocket, { setMode, setSources }));
|
setConnection(new Connection(webSocket, { setMode, setSources, setActions }));
|
||||||
return () => {
|
return () => {
|
||||||
webSocket.close();
|
webSocket.close();
|
||||||
};
|
};
|
||||||
@ -336,8 +338,9 @@ const SnapshotContainer: React.FunctionComponent<{
|
|||||||
};
|
};
|
||||||
|
|
||||||
type ConnectionOptions = {
|
type ConnectionOptions = {
|
||||||
setSources: (sources: Source[]) => void;
|
|
||||||
setMode: (mode: Mode) => void;
|
setMode: (mode: Mode) => void;
|
||||||
|
setSources: (sources: Source[]) => void;
|
||||||
|
setActions: (actions: actions.ActionInContext[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Connection {
|
class Connection {
|
||||||
@ -387,14 +390,18 @@ class Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _dispatchEvent(method: string, params?: any) {
|
private _dispatchEvent(method: string, params?: any) {
|
||||||
|
if (method === 'setMode') {
|
||||||
|
const { mode } = params as { mode: Mode };
|
||||||
|
this._options.setMode(mode);
|
||||||
|
}
|
||||||
if (method === 'setSources') {
|
if (method === 'setSources') {
|
||||||
const { sources } = params as { sources: Source[] };
|
const { sources } = params as { sources: Source[] };
|
||||||
this._options.setSources(sources);
|
this._options.setSources(sources);
|
||||||
window.playwrightSourcesEchoForTest = sources;
|
window.playwrightSourcesEchoForTest = sources;
|
||||||
}
|
}
|
||||||
if (method === 'setMode') {
|
if (method === 'setActions') {
|
||||||
const { mode } = params as { mode: Mode };
|
const { actions } = params as { actions: actions.ActionInContext[] };
|
||||||
this._options.setMode(mode);
|
this._options.setActions(actions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -162,7 +162,7 @@ export function useSetting<S>(name: string | undefined, defaultValue: S): [S, Re
|
|||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
saveSettings?(): Promise<void>;
|
saveSettings?(): void;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user