chore: move recorder trace to action collector (#32597)
This commit is contained in:
parent
d051495c7a
commit
de08e729ae
|
|
@ -75,7 +75,7 @@ export class ContextRecorder extends EventEmitter {
|
||||||
saveStorage: params.saveStorage,
|
saveStorage: params.saveStorage,
|
||||||
};
|
};
|
||||||
|
|
||||||
const collection = new RecorderCollection(params.mode === 'recording');
|
const collection = new RecorderCollection(this._pageAliases, params.mode === 'recording');
|
||||||
collection.on('change', () => {
|
collection.on('change', () => {
|
||||||
this._recorderSources = [];
|
this._recorderSources = [];
|
||||||
for (const languageGenerator of this._orderedLanguages) {
|
for (const languageGenerator of this._orderedLanguages) {
|
||||||
|
|
@ -163,7 +163,7 @@ export class ContextRecorder extends EventEmitter {
|
||||||
// First page is called page, others are called popup1, popup2, etc.
|
// First page is called page, others are called popup1, popup2, etc.
|
||||||
const frame = page.mainFrame();
|
const frame = page.mainFrame();
|
||||||
page.on('close', () => {
|
page.on('close', () => {
|
||||||
this._collection.addAction({
|
this._collection.addRecordedAction({
|
||||||
frame: this._describeMainFrame(page),
|
frame: this._describeMainFrame(page),
|
||||||
committed: true,
|
committed: true,
|
||||||
action: {
|
action: {
|
||||||
|
|
@ -185,7 +185,7 @@ export class ContextRecorder extends EventEmitter {
|
||||||
if (page.opener()) {
|
if (page.opener()) {
|
||||||
this._onPopup(page.opener()!, page);
|
this._onPopup(page.opener()!, page);
|
||||||
} else {
|
} else {
|
||||||
this._collection.addAction({
|
this._collection.addRecordedAction({
|
||||||
frame: this._describeMainFrame(page),
|
frame: this._describeMainFrame(page),
|
||||||
committed: true,
|
committed: true,
|
||||||
action: {
|
action: {
|
||||||
|
|
@ -236,14 +236,15 @@ export class ContextRecorder extends EventEmitter {
|
||||||
|
|
||||||
await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext);
|
await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext);
|
||||||
|
|
||||||
this._collection.willPerformAction(actionInContext);
|
const callMetadata = await this._collection.willPerformAction(actionInContext);
|
||||||
const success = await performAction(this._pageAliases, actionInContext);
|
if (!callMetadata)
|
||||||
if (success) {
|
return;
|
||||||
this._collection.didPerformAction(actionInContext);
|
const error = await performAction(callMetadata, this._pageAliases, actionInContext).then(() => undefined).catch((e: Error) => e);
|
||||||
|
await this._collection.didPerformAction(callMetadata, actionInContext, error);
|
||||||
|
if (error)
|
||||||
|
actionInContext.committed = true;
|
||||||
|
else
|
||||||
this._setCommittedAfterTimeout(actionInContext);
|
this._setCommittedAfterTimeout(actionInContext);
|
||||||
} else {
|
|
||||||
this._collection.performedActionFailed(actionInContext);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _recordAction(frame: Frame, action: actions.Action) {
|
private async _recordAction(frame: Frame, action: actions.Action) {
|
||||||
|
|
@ -260,7 +261,7 @@ export class ContextRecorder extends EventEmitter {
|
||||||
await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext);
|
await this._delegate.rewriteActionInContext?.(this._pageAliases, actionInContext);
|
||||||
|
|
||||||
this._setCommittedAfterTimeout(actionInContext);
|
this._setCommittedAfterTimeout(actionInContext);
|
||||||
this._collection.addAction(actionInContext);
|
this._collection.addRecordedAction(actionInContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setCommittedAfterTimeout(actionInContext: ActionInContext) {
|
private _setCommittedAfterTimeout(actionInContext: ActionInContext) {
|
||||||
|
|
|
||||||
|
|
@ -16,18 +16,25 @@
|
||||||
|
|
||||||
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 { Signal } from './recorderActions';
|
import type { Signal } from './recorderActions';
|
||||||
import type { ActionInContext } from '../codegen/types';
|
import type { ActionInContext } from '../codegen/types';
|
||||||
|
import type { CallMetadata } from '@protocol/callMetadata';
|
||||||
|
import { createGuid } from '../../utils/crypto';
|
||||||
|
import { monotonicTime } from '../../utils/time';
|
||||||
|
import { mainFrameForAction, traceParamsForAction } from './recorderUtils';
|
||||||
|
|
||||||
export class RecorderCollection extends EventEmitter {
|
export class RecorderCollection extends EventEmitter {
|
||||||
private _currentAction: ActionInContext | null = null;
|
private _currentAction: ActionInContext | null = null;
|
||||||
private _lastAction: ActionInContext | null = null;
|
private _lastAction: ActionInContext | null = null;
|
||||||
private _actions: ActionInContext[] = [];
|
private _actions: ActionInContext[] = [];
|
||||||
private _enabled: boolean;
|
private _enabled: boolean;
|
||||||
|
private _pageAliases: Map<Page, string>;
|
||||||
|
|
||||||
constructor(enabled: boolean) {
|
constructor(pageAliases: Map<Page, string>, enabled: boolean) {
|
||||||
super();
|
super();
|
||||||
this._enabled = enabled;
|
this._enabled = enabled;
|
||||||
|
this._pageAliases = pageAliases;
|
||||||
this.restart();
|
this.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,29 +53,55 @@ export class RecorderCollection extends EventEmitter {
|
||||||
this._enabled = enabled;
|
this._enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
addAction(action: ActionInContext) {
|
async willPerformAction(actionInContext: ActionInContext): Promise<CallMetadata | null> {
|
||||||
if (!this._enabled)
|
if (!this._enabled)
|
||||||
return;
|
return null;
|
||||||
this.willPerformAction(action);
|
const mainFrame = mainFrameForAction(this._pageAliases, actionInContext);
|
||||||
this.didPerformAction(action);
|
|
||||||
|
const { action } = actionInContext;
|
||||||
|
const callMetadata: CallMetadata = {
|
||||||
|
id: `call@${createGuid()}`,
|
||||||
|
apiName: 'frame.' + action.name,
|
||||||
|
objectId: mainFrame.guid,
|
||||||
|
pageId: mainFrame._page.guid,
|
||||||
|
frameId: mainFrame.guid,
|
||||||
|
startTime: monotonicTime(),
|
||||||
|
endTime: 0,
|
||||||
|
type: 'Frame',
|
||||||
|
method: action.name,
|
||||||
|
params: traceParamsForAction(actionInContext),
|
||||||
|
log: [],
|
||||||
|
};
|
||||||
|
await mainFrame.instrumentation.onBeforeCall(mainFrame, callMetadata);
|
||||||
|
this._currentAction = actionInContext;
|
||||||
|
return callMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
willPerformAction(action: ActionInContext) {
|
async didPerformAction(callMetadata: CallMetadata, actionInContext: ActionInContext, error?: Error) {
|
||||||
if (!this._enabled)
|
if (!this._enabled)
|
||||||
return;
|
return;
|
||||||
this._currentAction = action;
|
|
||||||
}
|
|
||||||
|
|
||||||
performedActionFailed(action: ActionInContext) {
|
if (error) {
|
||||||
if (!this._enabled)
|
// Do not clear current action on delayed error.
|
||||||
return;
|
if (this._currentAction === actionInContext)
|
||||||
if (this._currentAction === action)
|
this._currentAction = null;
|
||||||
|
} else {
|
||||||
this._currentAction = null;
|
this._currentAction = null;
|
||||||
|
this._actions.push(actionInContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._lastAction = actionInContext;
|
||||||
|
const mainFrame = mainFrameForAction(this._pageAliases, actionInContext);
|
||||||
|
callMetadata.endTime = monotonicTime();
|
||||||
|
await mainFrame.instrumentation.onAfterCall(mainFrame, callMetadata);
|
||||||
|
|
||||||
|
this.emit('change');
|
||||||
}
|
}
|
||||||
|
|
||||||
didPerformAction(actionInContext: ActionInContext) {
|
addRecordedAction(actionInContext: ActionInContext) {
|
||||||
if (!this._enabled)
|
if (!this._enabled)
|
||||||
return;
|
return;
|
||||||
|
this._currentAction = null;
|
||||||
const action = actionInContext.action;
|
const action = actionInContext.action;
|
||||||
let eraseLastAction = false;
|
let eraseLastAction = false;
|
||||||
if (this._lastAction && this._lastAction.frame.pageAlias === actionInContext.frame.pageAlias) {
|
if (this._lastAction && this._lastAction.frame.pageAlias === actionInContext.frame.pageAlias) {
|
||||||
|
|
@ -81,14 +114,12 @@ export class RecorderCollection extends EventEmitter {
|
||||||
if (lastAction && action.name === 'navigate' && lastAction.name === 'navigate') {
|
if (lastAction && action.name === 'navigate' && lastAction.name === 'navigate') {
|
||||||
if (action.url === lastAction.url) {
|
if (action.url === lastAction.url) {
|
||||||
// Already at a target URL.
|
// Already at a target URL.
|
||||||
this._currentAction = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._lastAction = actionInContext;
|
this._lastAction = actionInContext;
|
||||||
this._currentAction = null;
|
|
||||||
if (eraseLastAction)
|
if (eraseLastAction)
|
||||||
this._actions.pop();
|
this._actions.pop();
|
||||||
this._actions.push(actionInContext);
|
this._actions.push(actionInContext);
|
||||||
|
|
@ -125,7 +156,7 @@ export class RecorderCollection extends EventEmitter {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (signal.name === 'navigation' && frame._page.mainFrame() === frame) {
|
if (signal.name === 'navigation' && frame._page.mainFrame() === frame) {
|
||||||
this.addAction({
|
this.addRecordedAction({
|
||||||
frame: {
|
frame: {
|
||||||
pageAlias,
|
pageAlias,
|
||||||
framePath: [],
|
framePath: [],
|
||||||
|
|
|
||||||
|
|
@ -14,118 +14,117 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { createGuid, monotonicTime, 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 { ActionInContext } from '../codegen/types';
|
||||||
import type { Frame } from '../frames';
|
|
||||||
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 './recorderActions';
|
||||||
import type * as types from '../types';
|
import type * as types from '../types';
|
||||||
import { buildFullSelector } from './recorderUtils';
|
import { buildFullSelector, mainFrameForAction } from './recorderUtils';
|
||||||
|
|
||||||
async function innerPerformAction(mainFrame: Frame, action: string, params: any, cb: (callMetadata: CallMetadata) => Promise<any>): Promise<boolean> {
|
export async function performAction(callMetadata: CallMetadata, pageAliases: Map<Page, string>, actionInContext: ActionInContext) {
|
||||||
const callMetadata: CallMetadata = {
|
const mainFrame = mainFrameForAction(pageAliases, actionInContext);
|
||||||
id: `call@${createGuid()}`,
|
|
||||||
apiName: 'frame.' + action,
|
|
||||||
objectId: mainFrame.guid,
|
|
||||||
pageId: mainFrame._page.guid,
|
|
||||||
frameId: mainFrame.guid,
|
|
||||||
startTime: monotonicTime(),
|
|
||||||
endTime: 0,
|
|
||||||
type: 'Frame',
|
|
||||||
method: action,
|
|
||||||
params,
|
|
||||||
log: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
await mainFrame.instrumentation.onBeforeCall(mainFrame, callMetadata);
|
|
||||||
await cb(callMetadata);
|
|
||||||
} catch (e) {
|
|
||||||
callMetadata.endTime = monotonicTime();
|
|
||||||
await mainFrame.instrumentation.onAfterCall(mainFrame, callMetadata);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
callMetadata.endTime = monotonicTime();
|
|
||||||
await mainFrame.instrumentation.onAfterCall(mainFrame, callMetadata);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function performAction(pageAliases: Map<Page, string>, actionInContext: ActionInContext): Promise<boolean> {
|
|
||||||
const pageAlias = actionInContext.frame.pageAlias;
|
|
||||||
const page = [...pageAliases.entries()].find(([, alias]) => pageAlias === alias)?.[0];
|
|
||||||
if (!page)
|
|
||||||
throw new Error('Internal error: page not found');
|
|
||||||
const mainFrame = page.mainFrame();
|
|
||||||
const { action } = actionInContext;
|
const { action } = actionInContext;
|
||||||
|
|
||||||
const kActionTimeout = 5000;
|
const kActionTimeout = 5000;
|
||||||
|
|
||||||
if (action.name === 'navigate')
|
if (action.name === 'navigate') {
|
||||||
return await innerPerformAction(mainFrame, 'goto', { url: action.url }, callMetadata => mainFrame.goto(callMetadata, action.url, { timeout: kActionTimeout }));
|
await mainFrame.goto(callMetadata, action.url, { timeout: kActionTimeout });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (action.name === 'openPage')
|
if (action.name === 'openPage')
|
||||||
throw Error('Not reached');
|
throw Error('Not reached');
|
||||||
if (action.name === 'closePage')
|
|
||||||
return await innerPerformAction(mainFrame, 'close', {}, callMetadata => mainFrame._page.close(callMetadata));
|
if (action.name === 'closePage') {
|
||||||
|
await mainFrame._page.close(callMetadata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const selector = buildFullSelector(actionInContext.frame.framePath, action.selector);
|
const selector = buildFullSelector(actionInContext.frame.framePath, action.selector);
|
||||||
|
|
||||||
if (action.name === 'click') {
|
if (action.name === 'click') {
|
||||||
const options = toClickOptions(action);
|
const options = toClickOptions(action);
|
||||||
return await innerPerformAction(mainFrame, 'click', { selector }, callMetadata => mainFrame.click(callMetadata, selector, { ...options, timeout: kActionTimeout, strict: true }));
|
await mainFrame.click(callMetadata, selector, { ...options, timeout: kActionTimeout, strict: true });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.name === 'press') {
|
if (action.name === 'press') {
|
||||||
const modifiers = toKeyboardModifiers(action.modifiers);
|
const modifiers = toKeyboardModifiers(action.modifiers);
|
||||||
const shortcut = [...modifiers, action.key].join('+');
|
const shortcut = [...modifiers, action.key].join('+');
|
||||||
return await innerPerformAction(mainFrame, 'press', { selector, key: shortcut }, callMetadata => mainFrame.press(callMetadata, selector, shortcut, { timeout: kActionTimeout, strict: true }));
|
await mainFrame.press(callMetadata, selector, shortcut, { timeout: kActionTimeout, strict: true });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (action.name === 'fill')
|
|
||||||
return await innerPerformAction(mainFrame, 'fill', { selector, text: action.text }, callMetadata => mainFrame.fill(callMetadata, selector, action.text, { timeout: kActionTimeout, strict: true }));
|
if (action.name === 'fill') {
|
||||||
if (action.name === 'setInputFiles')
|
await mainFrame.fill(callMetadata, selector, action.text, { timeout: kActionTimeout, strict: true });
|
||||||
return await innerPerformAction(mainFrame, 'setInputFiles', { selector, files: action.files }, callMetadata => mainFrame.setInputFiles(callMetadata, selector, { selector, payloads: [], timeout: kActionTimeout, strict: true }));
|
return;
|
||||||
if (action.name === 'check')
|
}
|
||||||
return await innerPerformAction(mainFrame, 'check', { selector }, callMetadata => mainFrame.check(callMetadata, selector, { timeout: kActionTimeout, strict: true }));
|
|
||||||
if (action.name === 'uncheck')
|
if (action.name === 'setInputFiles') {
|
||||||
return await innerPerformAction(mainFrame, 'uncheck', { selector }, callMetadata => mainFrame.uncheck(callMetadata, selector, { timeout: kActionTimeout, strict: true }));
|
await mainFrame.setInputFiles(callMetadata, selector, { selector, payloads: [], timeout: kActionTimeout, strict: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.name === 'check') {
|
||||||
|
await mainFrame.check(callMetadata, selector, { timeout: kActionTimeout, strict: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.name === 'uncheck') {
|
||||||
|
await mainFrame.uncheck(callMetadata, selector, { timeout: kActionTimeout, strict: true });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (action.name === 'select') {
|
if (action.name === 'select') {
|
||||||
const values = action.options.map(value => ({ value }));
|
const values = action.options.map(value => ({ value }));
|
||||||
return await innerPerformAction(mainFrame, 'selectOption', { selector, values }, callMetadata => mainFrame.selectOption(callMetadata, selector, [], values, { timeout: kActionTimeout, strict: true }));
|
await mainFrame.selectOption(callMetadata, selector, [], values, { timeout: kActionTimeout, strict: true });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.name === 'assertChecked') {
|
if (action.name === 'assertChecked') {
|
||||||
return await innerPerformAction(mainFrame, 'expect', { selector }, callMetadata => mainFrame.expect(callMetadata, selector, {
|
await mainFrame.expect(callMetadata, selector, {
|
||||||
selector,
|
selector,
|
||||||
expression: 'to.be.checked',
|
expression: 'to.be.checked',
|
||||||
isNot: !action.checked,
|
isNot: !action.checked,
|
||||||
timeout: kActionTimeout,
|
timeout: kActionTimeout,
|
||||||
}));
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.name === 'assertText') {
|
if (action.name === 'assertText') {
|
||||||
return await innerPerformAction(mainFrame, 'expect', { selector }, callMetadata => mainFrame.expect(callMetadata, selector, {
|
await mainFrame.expect(callMetadata, selector, {
|
||||||
selector,
|
selector,
|
||||||
expression: 'to.have.text',
|
expression: 'to.have.text',
|
||||||
expectedText: serializeExpectedTextValues([action.text], { matchSubstring: true, normalizeWhiteSpace: true }),
|
expectedText: serializeExpectedTextValues([action.text], { matchSubstring: true, normalizeWhiteSpace: true }),
|
||||||
isNot: false,
|
isNot: false,
|
||||||
timeout: kActionTimeout,
|
timeout: kActionTimeout,
|
||||||
}));
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.name === 'assertValue') {
|
if (action.name === 'assertValue') {
|
||||||
return await innerPerformAction(mainFrame, 'expect', { selector }, callMetadata => mainFrame.expect(callMetadata, selector, {
|
await mainFrame.expect(callMetadata, selector, {
|
||||||
selector,
|
selector,
|
||||||
expression: 'to.have.value',
|
expression: 'to.have.value',
|
||||||
expectedValue: action.value,
|
expectedValue: action.value,
|
||||||
isNot: false,
|
isNot: false,
|
||||||
timeout: kActionTimeout,
|
timeout: kActionTimeout,
|
||||||
}));
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.name === 'assertVisible') {
|
if (action.name === 'assertVisible') {
|
||||||
return await innerPerformAction(mainFrame, 'expect', { selector }, callMetadata => mainFrame.expect(callMetadata, selector, {
|
await mainFrame.expect(callMetadata, selector, {
|
||||||
selector,
|
selector,
|
||||||
expression: 'to.be.visible',
|
expression: 'to.be.visible',
|
||||||
isNot: false,
|
isNot: false,
|
||||||
timeout: kActionTimeout,
|
timeout: kActionTimeout,
|
||||||
}));
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Internal error: unexpected action ' + (action as any).name);
|
throw new Error('Internal error: unexpected action ' + (action as any).name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,8 @@ import type { Page } from '../page';
|
||||||
import type { ActionInContext } from '../codegen/types';
|
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 './recorderActions';
|
||||||
|
import { toKeyboardModifiers } from '../codegen/language';
|
||||||
|
import { serializeExpectedTextValues } from '../../utils/expectUtils';
|
||||||
|
|
||||||
export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog {
|
export function metadataToCallLog(metadata: CallMetadata, status: CallLogStatus): CallLog {
|
||||||
let title = metadata.apiName || metadata.method;
|
let title = metadata.apiName || metadata.method;
|
||||||
|
|
@ -72,3 +74,58 @@ export async function frameForAction(pageAliases: Map<Page, string>, actionInCon
|
||||||
throw new Error('Internal error: frame not found');
|
throw new Error('Internal error: frame not found');
|
||||||
return result.frame;
|
return result.frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function traceParamsForAction(actionInContext: ActionInContext) {
|
||||||
|
const { action } = actionInContext;
|
||||||
|
|
||||||
|
switch (action.name) {
|
||||||
|
case 'navigate': return { url: action.url };
|
||||||
|
case 'openPage': return {};
|
||||||
|
case 'closePage': return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const selector = buildFullSelector(actionInContext.frame.framePath, action.selector);
|
||||||
|
switch (action.name) {
|
||||||
|
case 'click': return { selector, clickCount: action.clickCount };
|
||||||
|
case 'press': {
|
||||||
|
const modifiers = toKeyboardModifiers(action.modifiers);
|
||||||
|
const shortcut = [...modifiers, action.key].join('+');
|
||||||
|
return { selector, key: shortcut };
|
||||||
|
}
|
||||||
|
case 'fill': return { selector, text: action.text };
|
||||||
|
case 'setInputFiles': return { selector, files: action.files };
|
||||||
|
case 'check': return { selector };
|
||||||
|
case 'uncheck': return { selector };
|
||||||
|
case 'select': return { selector, values: action.options.map(value => ({ value })) };
|
||||||
|
case 'assertChecked': {
|
||||||
|
return {
|
||||||
|
selector,
|
||||||
|
expression: 'to.be.checked',
|
||||||
|
isNot: !action.checked,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'assertText': {
|
||||||
|
return {
|
||||||
|
selector,
|
||||||
|
expression: 'to.have.text',
|
||||||
|
expectedText: serializeExpectedTextValues([action.text], { matchSubstring: true, normalizeWhiteSpace: true }),
|
||||||
|
isNot: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'assertValue': {
|
||||||
|
return {
|
||||||
|
selector,
|
||||||
|
expression: 'to.have.value',
|
||||||
|
expectedValue: action.value,
|
||||||
|
isNot: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
case 'assertVisible': {
|
||||||
|
return {
|
||||||
|
selector,
|
||||||
|
expression: 'to.be.visible',
|
||||||
|
isNot: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue