mirror of
https://github.com/microsoft/playwright.git
synced 2025-06-26 21:40:17 +00:00
chore: generate match snapshot (#33105)
This commit is contained in:
parent
23b1012c70
commit
4b1fbde2ad
@ -146,6 +146,8 @@ export class CSharpLanguageGenerator implements LanguageGenerator {
|
|||||||
const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmptyAsync()`;
|
const assertion = action.value ? `ToHaveValueAsync(${quote(action.value)})` : `ToBeEmptyAsync()`;
|
||||||
return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
return `await Expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||||
}
|
}
|
||||||
|
case 'assertSnapshot':
|
||||||
|
return `await Expect(${subject}.${this._asLocator(action.selector)}).ToMatchAriaSnapshotAsync(${quote(action.snapshot)});`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -133,6 +133,8 @@ export class JavaLanguageGenerator implements LanguageGenerator {
|
|||||||
const assertion = action.value ? `hasValue(${quote(action.value)})` : `isEmpty()`;
|
const assertion = action.value ? `hasValue(${quote(action.value)})` : `isEmpty()`;
|
||||||
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${assertion};`;
|
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).${assertion};`;
|
||||||
}
|
}
|
||||||
|
case 'assertSnapshot':
|
||||||
|
return `assertThat(${subject}.${this._asLocator(action.selector, inFrameLocator)}).matchesAriaSnapshot(${quote(action.snapshot)});`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -117,6 +117,8 @@ export class JavaScriptLanguageGenerator implements LanguageGenerator {
|
|||||||
const assertion = action.value ? `toHaveValue(${quote(action.value)})` : `toBeEmpty()`;
|
const assertion = action.value ? `toHaveValue(${quote(action.value)})` : `toBeEmpty()`;
|
||||||
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||||
}
|
}
|
||||||
|
case 'assertSnapshot':
|
||||||
|
return `${this._isTest ? '' : '// '}await expect(${subject}.${this._asLocator(action.selector)}).toMatchAriaSnapshot(${quoteMultiline(action.snapshot)});`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,11 +230,13 @@ export class JavaScriptFormatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prepend(text: string) {
|
prepend(text: string) {
|
||||||
this._lines = text.trim().split('\n').map(line => line.trim()).concat(this._lines);
|
const trim = isMultilineString(text) ? (line: string) => line : (line: string) => line.trim();
|
||||||
|
this._lines = text.trim().split('\n').map(trim).concat(this._lines);
|
||||||
}
|
}
|
||||||
|
|
||||||
add(text: string) {
|
add(text: string) {
|
||||||
this._lines.push(...text.trim().split('\n').map(line => line.trim()));
|
const trim = isMultilineString(text) ? (line: string) => line : (line: string) => line.trim();
|
||||||
|
this._lines.push(...text.trim().split('\n').map(trim));
|
||||||
}
|
}
|
||||||
|
|
||||||
newLine() {
|
newLine() {
|
||||||
@ -269,3 +273,14 @@ function wrapWithStep(description: string | undefined, body: string) {
|
|||||||
${body}
|
${body}
|
||||||
});` : body;
|
});` : body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function quoteMultiline(text: string, indent = ' ') {
|
||||||
|
const lines = text.split('\n');
|
||||||
|
if (lines.length === 1)
|
||||||
|
return '`' + text.replace(/`/g, '\\`').replace(/\${/g, '\\${') + '`';
|
||||||
|
return '`\n' + lines.map(line => indent + line.replace(/`/g, '\\`').replace(/\${/g, '\\${')).join('\n') + `\n${indent}\``;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMultilineString(text: string) {
|
||||||
|
return text.match(/`[\S\s]*`/)?.[0].includes('\n');
|
||||||
|
}
|
||||||
|
|||||||
@ -126,6 +126,8 @@ export class PythonLanguageGenerator implements LanguageGenerator {
|
|||||||
const assertion = action.value ? `to_have_value(${quote(action.value)})` : `to_be_empty()`;
|
const assertion = action.value ? `to_have_value(${quote(action.value)})` : `to_be_empty()`;
|
||||||
return `expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
return `expect(${subject}.${this._asLocator(action.selector)}).${assertion};`;
|
||||||
}
|
}
|
||||||
|
case 'assertSnapshot':
|
||||||
|
return `expect(${subject}.${this._asLocator(action.selector)}).to_match_aria_snapshot(${quote(action.snapshot)})`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -90,8 +90,7 @@ export function generateAriaTree(rootElement: Element): AriaNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
beginAriaCaches();
|
beginAriaCaches();
|
||||||
const result = toAriaNode(rootElement);
|
const ariaRoot: AriaNode = { role: '' };
|
||||||
const ariaRoot = result?.ariaNode || { role: '' };
|
|
||||||
try {
|
try {
|
||||||
visit(ariaRoot, rootElement);
|
visit(ariaRoot, rootElement);
|
||||||
} finally {
|
} finally {
|
||||||
@ -218,7 +217,11 @@ function nodeMatches(root: AriaNode, template: AriaTemplateNode): boolean {
|
|||||||
|
|
||||||
export function renderAriaTree(ariaNode: AriaNode): string {
|
export function renderAriaTree(ariaNode: AriaNode): string {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
const visit = (ariaNode: AriaNode, indent: string) => {
|
const visit = (ariaNode: AriaNode | string, indent: string) => {
|
||||||
|
if (typeof ariaNode === 'string') {
|
||||||
|
lines.push(indent + '- text: ' + escapeYamlString(ariaNode));
|
||||||
|
return;
|
||||||
|
}
|
||||||
let line = `${indent}- ${ariaNode.role}`;
|
let line = `${indent}- ${ariaNode.role}`;
|
||||||
if (ariaNode.name)
|
if (ariaNode.name)
|
||||||
line += ` ${escapeWithQuotes(ariaNode.name, '"')}`;
|
line += ` ${escapeWithQuotes(ariaNode.name, '"')}`;
|
||||||
@ -231,14 +234,16 @@ export function renderAriaTree(ariaNode: AriaNode): string {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lines.push(line + (ariaNode.children ? ':' : ''));
|
lines.push(line + (ariaNode.children ? ':' : ''));
|
||||||
for (const child of ariaNode.children || []) {
|
for (const child of ariaNode.children || [])
|
||||||
if (typeof child === 'string')
|
|
||||||
lines.push(indent + ' - text: ' + escapeYamlString(child));
|
|
||||||
else
|
|
||||||
visit(child, indent + ' ');
|
visit(child, indent + ' ');
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
if (ariaNode.role === '') {
|
||||||
|
// Render fragment.
|
||||||
|
for (const child of ariaNode.children || [])
|
||||||
|
visit(child, '');
|
||||||
|
} else {
|
||||||
visit(ariaNode, '');
|
visit(ariaNode, '');
|
||||||
|
}
|
||||||
return lines.join('\n');
|
return lines.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -220,6 +220,11 @@ x-pw-tool-item.value > x-div {
|
|||||||
clip-path: url(#icon-symbol-constant);
|
clip-path: url(#icon-symbol-constant);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
x-pw-tool-item.snapshot > x-div {
|
||||||
|
/* codicon: eye */
|
||||||
|
clip-path: url(#icon-gist);
|
||||||
|
}
|
||||||
|
|
||||||
x-pw-tool-item.accept > x-div {
|
x-pw-tool-item.accept > x-div {
|
||||||
clip-path: url(#icon-check);
|
clip-path: url(#icon-check);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } fr
|
|||||||
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
|
||||||
import type { Language } from '../../utils/isomorphic/locatorGenerators';
|
import type { Language } from '../../utils/isomorphic/locatorGenerators';
|
||||||
import { cacheNormalizedWhitespaces, normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils';
|
import { cacheNormalizedWhitespaces, normalizeWhiteSpace, trimStringWithEllipsis } from '../../utils/isomorphic/stringUtils';
|
||||||
import { matchesAriaTree } from './ariaSnapshot';
|
import { matchesAriaTree, renderedAriaTree } from './ariaSnapshot';
|
||||||
|
|
||||||
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
|
export type FrameExpectParams = Omit<channels.FrameExpectParams, 'expectedValue'> & { expectedValue?: any };
|
||||||
|
|
||||||
@ -206,6 +206,10 @@ export class InjectedScript {
|
|||||||
return new Set<Element>(result.map(r => r.element));
|
return new Set<Element>(result.map(r => r.element));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderedAriaTree(target: Element): string {
|
||||||
|
return renderedAriaTree(target);
|
||||||
|
}
|
||||||
|
|
||||||
querySelectorAll(selector: ParsedSelector, root: Node): Element[] {
|
querySelectorAll(selector: ParsedSelector, root: Node): Element[] {
|
||||||
if (selector.capture !== undefined) {
|
if (selector.capture !== undefined) {
|
||||||
if (selector.parts.some(part => part.name === 'nth'))
|
if (selector.parts.some(part => part.name === 'nth'))
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -0,0 +1 @@
|
|||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.57 1.14l3.28 3.3.15.36v9.7l-.5.5h-11l-.5-.5v-13l.5-.5h7.72l.35.14zM10 5h3l-3-3v3zM3 2v12h10V6H9.5L9 5.5V2H3zm2.062 7.533l1.817-1.828L6.17 7 4 9.179v.707l2.171 2.174.707-.707-1.816-1.82zM8.8 7.714l.7-.709 2.189 2.175v.709L9.5 12.062l-.705-.709 1.831-1.82L8.8 7.714z"/></svg>
|
||||||
|
After Width: | Height: | Size: 429 B |
@ -608,9 +608,9 @@ class TextAssertionTool implements RecorderTool {
|
|||||||
private _action: actions.AssertAction | null = null;
|
private _action: actions.AssertAction | null = null;
|
||||||
private _dialog: Dialog;
|
private _dialog: Dialog;
|
||||||
private _textCache = new Map<Element | ShadowRoot, ElementText>();
|
private _textCache = new Map<Element | ShadowRoot, ElementText>();
|
||||||
private _kind: 'text' | 'value';
|
private _kind: 'text' | 'value' | 'snapshot';
|
||||||
|
|
||||||
constructor(recorder: Recorder, kind: 'text' | 'value') {
|
constructor(recorder: Recorder, kind: 'text' | 'value' | 'snapshot') {
|
||||||
this._recorder = recorder;
|
this._recorder = recorder;
|
||||||
this._kind = kind;
|
this._kind = kind;
|
||||||
this._dialog = new Dialog(recorder);
|
this._dialog = new Dialog(recorder);
|
||||||
@ -656,7 +656,7 @@ class TextAssertionTool implements RecorderTool {
|
|||||||
const target = this._recorder.deepEventTarget(event);
|
const target = this._recorder.deepEventTarget(event);
|
||||||
if (this._hoverHighlight?.elements[0] === target)
|
if (this._hoverHighlight?.elements[0] === target)
|
||||||
return;
|
return;
|
||||||
if (this._kind === 'text')
|
if (this._kind === 'text' || this._kind === 'snapshot')
|
||||||
this._hoverHighlight = this._recorder.injectedScript.utils.elementText(this._textCache, target).full ? { elements: [target], selector: '' } : null;
|
this._hoverHighlight = this._recorder.injectedScript.utils.elementText(this._textCache, target).full ? { elements: [target], selector: '' } : null;
|
||||||
else
|
else
|
||||||
this._hoverHighlight = this._elementHasValue(target) ? this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null;
|
this._hoverHighlight = this._elementHasValue(target) ? this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName }) : null;
|
||||||
@ -704,6 +704,18 @@ class TextAssertionTool implements RecorderTool {
|
|||||||
value: (target as (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)).value,
|
value: (target as (HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement)).value,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
} if (this._kind === 'snapshot') {
|
||||||
|
this._hoverHighlight = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });
|
||||||
|
this._hoverHighlight.color = '#8acae480';
|
||||||
|
// forTextExpect can update the target, re-highlight it.
|
||||||
|
this._recorder.updateHighlight(this._hoverHighlight, true);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: 'assertSnapshot',
|
||||||
|
selector: this._hoverHighlight.selector,
|
||||||
|
signals: [],
|
||||||
|
snapshot: this._recorder.injectedScript.renderedAriaTree(target),
|
||||||
|
};
|
||||||
} else {
|
} else {
|
||||||
this._hoverHighlight = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });
|
this._hoverHighlight = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });
|
||||||
this._hoverHighlight.color = '#8acae480';
|
this._hoverHighlight.color = '#8acae480';
|
||||||
@ -727,6 +739,8 @@ class TextAssertionTool implements RecorderTool {
|
|||||||
return String(action.checked);
|
return String(action.checked);
|
||||||
if (action?.name === 'assertValue')
|
if (action?.name === 'assertValue')
|
||||||
return action.value;
|
return action.value;
|
||||||
|
if (action?.name === 'assertSnapshot')
|
||||||
|
return action.snapshot;
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -742,13 +756,19 @@ class TextAssertionTool implements RecorderTool {
|
|||||||
if (!this._hoverHighlight?.elements[0])
|
if (!this._hoverHighlight?.elements[0])
|
||||||
return;
|
return;
|
||||||
this._action = this._generateAction();
|
this._action = this._generateAction();
|
||||||
if (!this._action || this._action.name !== 'assertText')
|
if (this._action?.name === 'assertText') {
|
||||||
return;
|
this._showTextDialog(this._action);
|
||||||
|
} else if (this._action?.name === 'assertSnapshot') {
|
||||||
|
this._recorder.recordAction(this._action);
|
||||||
|
this._recorder.setMode('recording');
|
||||||
|
this._recorder.overlay?.flashToolSucceeded('assertingSnapshot');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const action = this._action;
|
private _showTextDialog(action: actions.AssertTextAction) {
|
||||||
const textElement = this._recorder.document.createElement('textarea');
|
const textElement = this._recorder.document.createElement('textarea');
|
||||||
textElement.setAttribute('spellcheck', 'false');
|
textElement.setAttribute('spellcheck', 'false');
|
||||||
textElement.value = this._renderValue(this._action);
|
textElement.value = this._renderValue(action);
|
||||||
textElement.classList.add('text-editor');
|
textElement.classList.add('text-editor');
|
||||||
|
|
||||||
const updateAndValidate = () => {
|
const updateAndValidate = () => {
|
||||||
@ -796,6 +816,7 @@ class Overlay {
|
|||||||
private _assertVisibilityToggle: HTMLElement;
|
private _assertVisibilityToggle: HTMLElement;
|
||||||
private _assertTextToggle: HTMLElement;
|
private _assertTextToggle: HTMLElement;
|
||||||
private _assertValuesToggle: HTMLElement;
|
private _assertValuesToggle: HTMLElement;
|
||||||
|
private _assertSnapshotToggle: HTMLElement;
|
||||||
private _offsetX = 0;
|
private _offsetX = 0;
|
||||||
private _dragState: { offsetX: number, dragStart: { x: number, y: number } } | undefined;
|
private _dragState: { offsetX: number, dragStart: { x: number, y: number } } | undefined;
|
||||||
private _measure: { width: number, height: number } = { width: 0, height: 0 };
|
private _measure: { width: number, height: number } = { width: 0, height: 0 };
|
||||||
@ -842,6 +863,12 @@ class Overlay {
|
|||||||
this._assertValuesToggle.appendChild(this._recorder.document.createElement('x-div'));
|
this._assertValuesToggle.appendChild(this._recorder.document.createElement('x-div'));
|
||||||
toolsListElement.appendChild(this._assertValuesToggle);
|
toolsListElement.appendChild(this._assertValuesToggle);
|
||||||
|
|
||||||
|
this._assertSnapshotToggle = this._recorder.document.createElement('x-pw-tool-item');
|
||||||
|
this._assertSnapshotToggle.title = 'Assert snapshot';
|
||||||
|
this._assertSnapshotToggle.classList.add('snapshot');
|
||||||
|
this._assertSnapshotToggle.appendChild(this._recorder.document.createElement('x-div'));
|
||||||
|
toolsListElement.appendChild(this._assertSnapshotToggle);
|
||||||
|
|
||||||
this._updateVisualPosition();
|
this._updateVisualPosition();
|
||||||
this._refreshListeners();
|
this._refreshListeners();
|
||||||
}
|
}
|
||||||
@ -865,6 +892,7 @@ class Overlay {
|
|||||||
'assertingText': 'recording-inspecting',
|
'assertingText': 'recording-inspecting',
|
||||||
'assertingVisibility': 'recording-inspecting',
|
'assertingVisibility': 'recording-inspecting',
|
||||||
'assertingValue': 'recording-inspecting',
|
'assertingValue': 'recording-inspecting',
|
||||||
|
'assertingSnapshot': 'recording-inspecting',
|
||||||
};
|
};
|
||||||
this._recorder.setMode(newMode[this._recorder.state.mode]);
|
this._recorder.setMode(newMode[this._recorder.state.mode]);
|
||||||
}),
|
}),
|
||||||
@ -880,6 +908,10 @@ class Overlay {
|
|||||||
if (!this._assertValuesToggle.classList.contains('disabled'))
|
if (!this._assertValuesToggle.classList.contains('disabled'))
|
||||||
this._recorder.setMode(this._recorder.state.mode === 'assertingValue' ? 'recording' : 'assertingValue');
|
this._recorder.setMode(this._recorder.state.mode === 'assertingValue' ? 'recording' : 'assertingValue');
|
||||||
}),
|
}),
|
||||||
|
addEventListener(this._assertSnapshotToggle, 'click', () => {
|
||||||
|
if (!this._assertSnapshotToggle.classList.contains('disabled'))
|
||||||
|
this._recorder.setMode(this._recorder.state.mode === 'assertingSnapshot' ? 'recording' : 'assertingSnapshot');
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -902,6 +934,8 @@ class Overlay {
|
|||||||
this._assertTextToggle.classList.toggle('disabled', state.mode === 'none' || state.mode === 'standby' || state.mode === 'inspecting');
|
this._assertTextToggle.classList.toggle('disabled', state.mode === 'none' || state.mode === 'standby' || state.mode === 'inspecting');
|
||||||
this._assertValuesToggle.classList.toggle('active', state.mode === 'assertingValue');
|
this._assertValuesToggle.classList.toggle('active', state.mode === 'assertingValue');
|
||||||
this._assertValuesToggle.classList.toggle('disabled', state.mode === 'none' || state.mode === 'standby' || state.mode === 'inspecting');
|
this._assertValuesToggle.classList.toggle('disabled', state.mode === 'none' || state.mode === 'standby' || state.mode === 'inspecting');
|
||||||
|
this._assertSnapshotToggle.classList.toggle('active', state.mode === 'assertingSnapshot');
|
||||||
|
this._assertSnapshotToggle.classList.toggle('disabled', state.mode === 'none' || state.mode === 'standby' || state.mode === 'inspecting');
|
||||||
if (this._offsetX !== state.overlay.offsetX) {
|
if (this._offsetX !== state.overlay.offsetX) {
|
||||||
this._offsetX = state.overlay.offsetX;
|
this._offsetX = state.overlay.offsetX;
|
||||||
this._updateVisualPosition();
|
this._updateVisualPosition();
|
||||||
@ -912,8 +946,14 @@ class Overlay {
|
|||||||
this._showOverlay();
|
this._showOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
flashToolSucceeded(tool: 'assertingVisibility' | 'assertingValue') {
|
flashToolSucceeded(tool: 'assertingVisibility' | 'assertingSnapshot' | 'assertingValue') {
|
||||||
const element = tool === 'assertingVisibility' ? this._assertVisibilityToggle : this._assertValuesToggle;
|
let element: Element;
|
||||||
|
if (tool === 'assertingVisibility')
|
||||||
|
element = this._assertVisibilityToggle;
|
||||||
|
else if (tool === 'assertingSnapshot')
|
||||||
|
element = this._assertSnapshotToggle;
|
||||||
|
else
|
||||||
|
element = this._assertValuesToggle;
|
||||||
element.classList.add('succeeded');
|
element.classList.add('succeeded');
|
||||||
this._recorder.injectedScript.builtinSetTimeout(() => element.classList.remove('succeeded'), 2000);
|
this._recorder.injectedScript.builtinSetTimeout(() => element.classList.remove('succeeded'), 2000);
|
||||||
}
|
}
|
||||||
@ -1004,6 +1044,7 @@ export class Recorder {
|
|||||||
'assertingText': new TextAssertionTool(this, 'text'),
|
'assertingText': new TextAssertionTool(this, 'text'),
|
||||||
'assertingVisibility': new InspectTool(this, true),
|
'assertingVisibility': new InspectTool(this, true),
|
||||||
'assertingValue': new TextAssertionTool(this, 'value'),
|
'assertingValue': new TextAssertionTool(this, 'value'),
|
||||||
|
'assertingSnapshot': new TextAssertionTool(this, 'snapshot'),
|
||||||
};
|
};
|
||||||
this._currentTool = this._tools.none;
|
this._currentTool = this._tools.none;
|
||||||
if (injectedScript.window.top === injectedScript.window) {
|
if (injectedScript.window.top === injectedScript.window) {
|
||||||
|
|||||||
@ -216,7 +216,7 @@ export class Recorder implements InstrumentationListener, IRecorder {
|
|||||||
this._highlightedSelector = '';
|
this._highlightedSelector = '';
|
||||||
this._mode = mode;
|
this._mode = mode;
|
||||||
this._recorderApp?.setMode(this._mode);
|
this._recorderApp?.setMode(this._mode);
|
||||||
this._contextRecorder.setEnabled(this._mode === 'recording' || this._mode === 'assertingText' || this._mode === 'assertingVisibility' || this._mode === 'assertingValue');
|
this._contextRecorder.setEnabled(this._mode === 'recording' || this._mode === 'assertingText' || this._mode === 'assertingVisibility' || this._mode === 'assertingValue' || this._mode === 'assertingSnapshot');
|
||||||
this._debugger.setMuted(this._mode === 'recording' || this._mode === 'assertingText' || this._mode === 'assertingVisibility' || this._mode === 'assertingValue');
|
this._debugger.setMuted(this._mode === 'recording' || this._mode === 'assertingText' || this._mode === 'assertingVisibility' || this._mode === 'assertingValue');
|
||||||
if (this._mode !== 'none' && this._mode !== 'standby' && this._context.pages().length === 1)
|
if (this._mode !== 'none' && this._mode !== 'standby' && this._context.pages().length === 1)
|
||||||
this._context.pages()[0].bringToFront().catch(() => {});
|
this._context.pages()[0].bringToFront().catch(() => {});
|
||||||
|
|||||||
@ -130,6 +130,15 @@ export function traceParamsForAction(actionInContext: recorderActions.ActionInCo
|
|||||||
};
|
};
|
||||||
return { method: 'expect', params };
|
return { method: 'expect', params };
|
||||||
}
|
}
|
||||||
|
case 'assertSnapshot': {
|
||||||
|
const params: channels.FrameExpectParams = {
|
||||||
|
selector,
|
||||||
|
expression: 'to.match.snapshot',
|
||||||
|
expectedText: [],
|
||||||
|
isNot: false,
|
||||||
|
};
|
||||||
|
return { method: 'expect', params };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,8 @@ export type ActionName =
|
|||||||
'assertText' |
|
'assertText' |
|
||||||
'assertValue' |
|
'assertValue' |
|
||||||
'assertChecked' |
|
'assertChecked' |
|
||||||
'assertVisible';
|
'assertVisible' |
|
||||||
|
'assertSnapshot';
|
||||||
|
|
||||||
export type ActionBase = {
|
export type ActionBase = {
|
||||||
name: ActionName,
|
name: ActionName,
|
||||||
@ -113,8 +114,13 @@ export type AssertVisibleAction = ActionWithSelector & {
|
|||||||
name: 'assertVisible',
|
name: 'assertVisible',
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Action = ClickAction | CheckAction | ClosesPageAction | OpenPageAction | UncheckAction | FillAction | NavigateAction | PressAction | SelectAction | SetInputFilesAction | AssertTextAction | AssertValueAction | AssertCheckedAction | AssertVisibleAction;
|
export type AssertSnapshotAction = ActionWithSelector & {
|
||||||
export type AssertAction = AssertCheckedAction | AssertValueAction | AssertTextAction | AssertVisibleAction;
|
name: 'assertSnapshot',
|
||||||
|
snapshot: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Action = ClickAction | CheckAction | ClosesPageAction | OpenPageAction | UncheckAction | FillAction | NavigateAction | PressAction | SelectAction | SetInputFilesAction | AssertTextAction | AssertValueAction | AssertCheckedAction | AssertVisibleAction | AssertSnapshotAction;
|
||||||
|
export type AssertAction = AssertCheckedAction | AssertValueAction | AssertTextAction | AssertVisibleAction | AssertSnapshotAction;
|
||||||
export type PerformOnRecordAction = ClickAction | CheckAction | UncheckAction | PressAction | SelectAction;
|
export type PerformOnRecordAction = ClickAction | CheckAction | UncheckAction | PressAction | SelectAction;
|
||||||
|
|
||||||
// Signals.
|
// Signals.
|
||||||
|
|||||||
@ -116,6 +116,7 @@ export const Recorder: React.FC<RecorderProps> = ({
|
|||||||
'assertingText': 'recording-inspecting',
|
'assertingText': 'recording-inspecting',
|
||||||
'assertingVisibility': 'recording-inspecting',
|
'assertingVisibility': 'recording-inspecting',
|
||||||
'assertingValue': 'recording-inspecting',
|
'assertingValue': 'recording-inspecting',
|
||||||
|
'assertingSnapshot': 'recording-inspecting',
|
||||||
}[mode];
|
}[mode];
|
||||||
window.dispatch({ event: 'setMode', params: { mode: newMode } }).catch(() => { });
|
window.dispatch({ event: 'setMode', params: { mode: newMode } }).catch(() => { });
|
||||||
}}></ToolbarButton>
|
}}></ToolbarButton>
|
||||||
|
|||||||
@ -26,7 +26,8 @@ export type Mode =
|
|||||||
| 'recording-inspecting'
|
| 'recording-inspecting'
|
||||||
| 'standby'
|
| 'standby'
|
||||||
| 'assertingVisibility'
|
| 'assertingVisibility'
|
||||||
| 'assertingValue';
|
| 'assertingValue'
|
||||||
|
| 'assertingSnapshot';
|
||||||
|
|
||||||
export type EventData = {
|
export type EventData = {
|
||||||
event:
|
event:
|
||||||
|
|||||||
@ -78,7 +78,7 @@ test('should match complex', async ({ page }) => {
|
|||||||
test('should match regex', async ({ page }) => {
|
test('should match regex', async ({ page }) => {
|
||||||
await page.setContent(`<h1>Issues 12</h1>`);
|
await page.setContent(`<h1>Issues 12</h1>`);
|
||||||
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
await expect(page.locator('body')).toMatchAriaSnapshot(`
|
||||||
- heading /Issues \\d+/
|
- heading ${/Issues \d+/}
|
||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -178,13 +178,16 @@ test('expected formatter', async ({ page }) => {
|
|||||||
- heading "todos"
|
- heading "todos"
|
||||||
- textbox "Wrong text"
|
- textbox "Wrong text"
|
||||||
`, { timeout: 1 }).catch(e => e);
|
`, { timeout: 1 }).catch(e => e);
|
||||||
expect(stripAnsi(error.message)).toContain(`- Expected - 3
|
|
||||||
|
expect(stripAnsi(error.message)).toContain(`
|
||||||
|
Locator: locator('body')
|
||||||
|
- Expected - 4
|
||||||
+ Received string + 3
|
+ Received string + 3
|
||||||
|
|
||||||
-
|
-
|
||||||
+ - :
|
|
||||||
+ - banner:
|
+ - banner:
|
||||||
- heading "todos"
|
- - heading "todos"
|
||||||
|
+ - heading "todos"
|
||||||
- - textbox "Wrong text"
|
- - textbox "Wrong text"
|
||||||
-
|
-
|
||||||
+ - textbox "What needs to be done?"`);
|
+ - textbox "What needs to be done?"`);
|
||||||
|
|||||||
@ -64,6 +64,7 @@ const iconNames = [
|
|||||||
'check',
|
'check',
|
||||||
'close',
|
'close',
|
||||||
'pass',
|
'pass',
|
||||||
|
'gist',
|
||||||
];
|
];
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user