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()`;
 | 
			
		||||
        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()`;
 | 
			
		||||
        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()`;
 | 
			
		||||
        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) {
 | 
			
		||||
    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) {
 | 
			
		||||
    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() {
 | 
			
		||||
@ -269,3 +273,14 @@ function wrapWithStep(description: string | undefined, body: string) {
 | 
			
		||||
${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()`;
 | 
			
		||||
        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();
 | 
			
		||||
  const result = toAriaNode(rootElement);
 | 
			
		||||
  const ariaRoot = result?.ariaNode || { role: '' };
 | 
			
		||||
  const ariaRoot: AriaNode = { role: '' };
 | 
			
		||||
  try {
 | 
			
		||||
    visit(ariaRoot, rootElement);
 | 
			
		||||
  } finally {
 | 
			
		||||
@ -218,7 +217,11 @@ function nodeMatches(root: AriaNode, template: AriaTemplateNode): boolean {
 | 
			
		||||
 | 
			
		||||
export function renderAriaTree(ariaNode: AriaNode): 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}`;
 | 
			
		||||
    if (ariaNode.name)
 | 
			
		||||
      line += ` ${escapeWithQuotes(ariaNode.name, '"')}`;
 | 
			
		||||
@ -231,14 +234,16 @@ export function renderAriaTree(ariaNode: AriaNode): string {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    lines.push(line + (ariaNode.children ? ':' : ''));
 | 
			
		||||
    for (const child of ariaNode.children || []) {
 | 
			
		||||
      if (typeof child === 'string')
 | 
			
		||||
        lines.push(indent + '  - text: ' + escapeYamlString(child));
 | 
			
		||||
      else
 | 
			
		||||
        visit(child, indent + '  ');
 | 
			
		||||
    }
 | 
			
		||||
    for (const child of ariaNode.children || [])
 | 
			
		||||
      visit(child, indent + '  ');
 | 
			
		||||
  };
 | 
			
		||||
  visit(ariaNode, '');
 | 
			
		||||
  if (ariaNode.role === '') {
 | 
			
		||||
    // Render fragment.
 | 
			
		||||
    for (const child of ariaNode.children || [])
 | 
			
		||||
      visit(child, '');
 | 
			
		||||
  } else {
 | 
			
		||||
    visit(ariaNode, '');
 | 
			
		||||
  }
 | 
			
		||||
  return lines.join('\n');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -220,6 +220,11 @@ x-pw-tool-item.value > x-div {
 | 
			
		||||
  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 {
 | 
			
		||||
  clip-path: url(#icon-check);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ import { kLayoutSelectorNames, type LayoutSelectorName, layoutSelectorScore } fr
 | 
			
		||||
import { asLocator } from '../../utils/isomorphic/locatorGenerators';
 | 
			
		||||
import type { Language } from '../../utils/isomorphic/locatorGenerators';
 | 
			
		||||
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 };
 | 
			
		||||
 | 
			
		||||
@ -206,6 +206,10 @@ export class InjectedScript {
 | 
			
		||||
    return new Set<Element>(result.map(r => r.element));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  renderedAriaTree(target: Element): string {
 | 
			
		||||
    return renderedAriaTree(target);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  querySelectorAll(selector: ParsedSelector, root: Node): Element[] {
 | 
			
		||||
    if (selector.capture !== undefined) {
 | 
			
		||||
      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 _dialog: Dialog;
 | 
			
		||||
  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._kind = kind;
 | 
			
		||||
    this._dialog = new Dialog(recorder);
 | 
			
		||||
@ -656,7 +656,7 @@ class TextAssertionTool implements RecorderTool {
 | 
			
		||||
    const target = this._recorder.deepEventTarget(event);
 | 
			
		||||
    if (this._hoverHighlight?.elements[0] === target)
 | 
			
		||||
      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;
 | 
			
		||||
    else
 | 
			
		||||
      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,
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
    } 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 {
 | 
			
		||||
      this._hoverHighlight = this._recorder.injectedScript.generateSelector(target, { testIdAttributeName: this._recorder.state.testIdAttributeName, forTextExpect: true });
 | 
			
		||||
      this._hoverHighlight.color = '#8acae480';
 | 
			
		||||
@ -727,6 +739,8 @@ class TextAssertionTool implements RecorderTool {
 | 
			
		||||
      return String(action.checked);
 | 
			
		||||
    if (action?.name === 'assertValue')
 | 
			
		||||
      return action.value;
 | 
			
		||||
    if (action?.name === 'assertSnapshot')
 | 
			
		||||
      return action.snapshot;
 | 
			
		||||
    return '';
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -742,13 +756,19 @@ class TextAssertionTool implements RecorderTool {
 | 
			
		||||
    if (!this._hoverHighlight?.elements[0])
 | 
			
		||||
      return;
 | 
			
		||||
    this._action = this._generateAction();
 | 
			
		||||
    if (!this._action || this._action.name !== 'assertText')
 | 
			
		||||
      return;
 | 
			
		||||
    if (this._action?.name === 'assertText') {
 | 
			
		||||
      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');
 | 
			
		||||
    textElement.setAttribute('spellcheck', 'false');
 | 
			
		||||
    textElement.value = this._renderValue(this._action);
 | 
			
		||||
    textElement.value = this._renderValue(action);
 | 
			
		||||
    textElement.classList.add('text-editor');
 | 
			
		||||
 | 
			
		||||
    const updateAndValidate = () => {
 | 
			
		||||
@ -796,6 +816,7 @@ class Overlay {
 | 
			
		||||
  private _assertVisibilityToggle: HTMLElement;
 | 
			
		||||
  private _assertTextToggle: HTMLElement;
 | 
			
		||||
  private _assertValuesToggle: HTMLElement;
 | 
			
		||||
  private _assertSnapshotToggle: HTMLElement;
 | 
			
		||||
  private _offsetX = 0;
 | 
			
		||||
  private _dragState: { offsetX: number, dragStart: { x: number, y: number } } | undefined;
 | 
			
		||||
  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'));
 | 
			
		||||
    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._refreshListeners();
 | 
			
		||||
  }
 | 
			
		||||
@ -865,6 +892,7 @@ class Overlay {
 | 
			
		||||
          'assertingText': 'recording-inspecting',
 | 
			
		||||
          'assertingVisibility': 'recording-inspecting',
 | 
			
		||||
          'assertingValue': 'recording-inspecting',
 | 
			
		||||
          'assertingSnapshot': 'recording-inspecting',
 | 
			
		||||
        };
 | 
			
		||||
        this._recorder.setMode(newMode[this._recorder.state.mode]);
 | 
			
		||||
      }),
 | 
			
		||||
@ -880,6 +908,10 @@ class Overlay {
 | 
			
		||||
        if (!this._assertValuesToggle.classList.contains('disabled'))
 | 
			
		||||
          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._assertValuesToggle.classList.toggle('active', state.mode === 'assertingValue');
 | 
			
		||||
    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) {
 | 
			
		||||
      this._offsetX = state.overlay.offsetX;
 | 
			
		||||
      this._updateVisualPosition();
 | 
			
		||||
@ -912,8 +946,14 @@ class Overlay {
 | 
			
		||||
      this._showOverlay();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  flashToolSucceeded(tool: 'assertingVisibility' | 'assertingValue') {
 | 
			
		||||
    const element = tool === 'assertingVisibility' ? this._assertVisibilityToggle : this._assertValuesToggle;
 | 
			
		||||
  flashToolSucceeded(tool: 'assertingVisibility' | 'assertingSnapshot' | 'assertingValue') {
 | 
			
		||||
    let element: Element;
 | 
			
		||||
    if (tool === 'assertingVisibility')
 | 
			
		||||
      element = this._assertVisibilityToggle;
 | 
			
		||||
    else if (tool === 'assertingSnapshot')
 | 
			
		||||
      element = this._assertSnapshotToggle;
 | 
			
		||||
    else
 | 
			
		||||
      element = this._assertValuesToggle;
 | 
			
		||||
    element.classList.add('succeeded');
 | 
			
		||||
    this._recorder.injectedScript.builtinSetTimeout(() => element.classList.remove('succeeded'), 2000);
 | 
			
		||||
  }
 | 
			
		||||
@ -1004,6 +1044,7 @@ export class Recorder {
 | 
			
		||||
      'assertingText': new TextAssertionTool(this, 'text'),
 | 
			
		||||
      'assertingVisibility': new InspectTool(this, true),
 | 
			
		||||
      'assertingValue': new TextAssertionTool(this, 'value'),
 | 
			
		||||
      'assertingSnapshot': new TextAssertionTool(this, 'snapshot'),
 | 
			
		||||
    };
 | 
			
		||||
    this._currentTool = this._tools.none;
 | 
			
		||||
    if (injectedScript.window.top === injectedScript.window) {
 | 
			
		||||
 | 
			
		||||
@ -216,7 +216,7 @@ export class Recorder implements InstrumentationListener, IRecorder {
 | 
			
		||||
    this._highlightedSelector = '';
 | 
			
		||||
    this._mode = 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');
 | 
			
		||||
    if (this._mode !== 'none' && this._mode !== 'standby' && this._context.pages().length === 1)
 | 
			
		||||
      this._context.pages()[0].bringToFront().catch(() => {});
 | 
			
		||||
 | 
			
		||||
@ -130,6 +130,15 @@ export function traceParamsForAction(actionInContext: recorderActions.ActionInCo
 | 
			
		||||
      };
 | 
			
		||||
      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' |
 | 
			
		||||
  'assertValue' |
 | 
			
		||||
  'assertChecked' |
 | 
			
		||||
  'assertVisible';
 | 
			
		||||
  'assertVisible' |
 | 
			
		||||
  'assertSnapshot';
 | 
			
		||||
 | 
			
		||||
export type ActionBase = {
 | 
			
		||||
  name: ActionName,
 | 
			
		||||
@ -113,8 +114,13 @@ export type AssertVisibleAction = ActionWithSelector & {
 | 
			
		||||
  name: 'assertVisible',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type Action = ClickAction | CheckAction | ClosesPageAction | OpenPageAction | UncheckAction | FillAction | NavigateAction | PressAction | SelectAction | SetInputFilesAction | AssertTextAction | AssertValueAction | AssertCheckedAction | AssertVisibleAction;
 | 
			
		||||
export type AssertAction = AssertCheckedAction | AssertValueAction | AssertTextAction | AssertVisibleAction;
 | 
			
		||||
export type AssertSnapshotAction = ActionWithSelector & {
 | 
			
		||||
  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;
 | 
			
		||||
 | 
			
		||||
// Signals.
 | 
			
		||||
 | 
			
		||||
@ -116,6 +116,7 @@ export const Recorder: React.FC<RecorderProps> = ({
 | 
			
		||||
          'assertingText': 'recording-inspecting',
 | 
			
		||||
          'assertingVisibility': 'recording-inspecting',
 | 
			
		||||
          'assertingValue': 'recording-inspecting',
 | 
			
		||||
          'assertingSnapshot': 'recording-inspecting',
 | 
			
		||||
        }[mode];
 | 
			
		||||
        window.dispatch({ event: 'setMode', params: { mode: newMode } }).catch(() => { });
 | 
			
		||||
      }}></ToolbarButton>
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,8 @@ export type Mode =
 | 
			
		||||
  | 'recording-inspecting'
 | 
			
		||||
  | 'standby'
 | 
			
		||||
  | 'assertingVisibility'
 | 
			
		||||
  | 'assertingValue';
 | 
			
		||||
  | 'assertingValue'
 | 
			
		||||
  | 'assertingSnapshot';
 | 
			
		||||
 | 
			
		||||
export type EventData = {
 | 
			
		||||
  event:
 | 
			
		||||
 | 
			
		||||
@ -78,7 +78,7 @@ test('should match complex', async ({ page }) => {
 | 
			
		||||
test('should match regex', async ({ page }) => {
 | 
			
		||||
  await page.setContent(`<h1>Issues 12</h1>`);
 | 
			
		||||
  await expect(page.locator('body')).toMatchAriaSnapshot(`
 | 
			
		||||
    - heading /Issues \\d+/
 | 
			
		||||
    - heading ${/Issues \d+/}
 | 
			
		||||
  `);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -178,14 +178,17 @@ test('expected formatter', async ({ page }) => {
 | 
			
		||||
    - heading "todos"
 | 
			
		||||
    - textbox "Wrong text"
 | 
			
		||||
  `, { 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
 | 
			
		||||
 | 
			
		||||
-
 | 
			
		||||
+ - :
 | 
			
		||||
+   - banner:
 | 
			
		||||
      - heading "todos"
 | 
			
		||||
+ - banner:
 | 
			
		||||
-     - heading "todos"
 | 
			
		||||
+   - heading "todos"
 | 
			
		||||
-     - textbox "Wrong text"
 | 
			
		||||
-   
 | 
			
		||||
+     - textbox "What needs to be done?"`);
 | 
			
		||||
+   - textbox "What needs to be done?"`);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -64,6 +64,7 @@ const iconNames = [
 | 
			
		||||
  'check',
 | 
			
		||||
  'close',
 | 
			
		||||
  'pass',
 | 
			
		||||
  'gist',
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
(async () => {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user