chore: allow editing aria template in recorder (tests) (#33522)
This commit is contained in:
parent
c29f573243
commit
503f74da90
11
package-lock.json
generated
11
package-lock.json
generated
|
|
@ -2910,10 +2910,11 @@
|
||||||
"periscopic": "^3.1.0"
|
"periscopic": "^3.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/codemirror-shadow-1": {
|
"node_modules/codemirror": {
|
||||||
"version": "0.0.1",
|
"version": "5.65.18",
|
||||||
"resolved": "https://registry.npmjs.org/codemirror-shadow-1/-/codemirror-shadow-1-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.18.tgz",
|
||||||
"integrity": "sha512-kD3OZpCCHr3LHRKfbGx5IogHTWq4Uo9jH2bXPVa7/n6ppkgI66rx4tniQY1BpqWp/JNhQmQsXhQoaZ1TH6t0xQ=="
|
"integrity": "sha512-Gaz4gHnkbHMGgahNt3CA5HBk5lLQBqmD/pBgeB4kQU6OedZmqMBjlRF0LSrp2tJ4wlLNPm2FfaUd1pDy0mdlpA==",
|
||||||
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
|
|
@ -8112,7 +8113,7 @@
|
||||||
"packages/web": {
|
"packages/web": {
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"codemirror-shadow-1": "0.0.1",
|
"codemirror": "5.65.18",
|
||||||
"xterm": "^5.1.0",
|
"xterm": "^5.1.0",
|
||||||
"xterm-addon-fit": "^0.7.0"
|
"xterm-addon-fit": "^0.7.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ This project incorporates components from the projects listed below. The origina
|
||||||
- balanced-match@1.0.2 (https://github.com/juliangruber/balanced-match)
|
- balanced-match@1.0.2 (https://github.com/juliangruber/balanced-match)
|
||||||
- brace-expansion@1.1.11 (https://github.com/juliangruber/brace-expansion)
|
- brace-expansion@1.1.11 (https://github.com/juliangruber/brace-expansion)
|
||||||
- buffer-crc32@0.2.13 (https://github.com/brianloveswords/buffer-crc32)
|
- buffer-crc32@0.2.13 (https://github.com/brianloveswords/buffer-crc32)
|
||||||
- codemirror-shadow-1@0.0.1 (https://github.com/codemirror/CodeMirror)
|
- codemirror@5.65.18 (https://github.com/codemirror/CodeMirror)
|
||||||
- colors@1.4.0 (https://github.com/Marak/colors.js)
|
- colors@1.4.0 (https://github.com/Marak/colors.js)
|
||||||
- commander@8.3.0 (https://github.com/tj/commander.js)
|
- commander@8.3.0 (https://github.com/tj/commander.js)
|
||||||
- concat-map@0.0.1 (https://github.com/substack/node-concat-map)
|
- concat-map@0.0.1 (https://github.com/substack/node-concat-map)
|
||||||
|
|
@ -208,7 +208,7 @@ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEAL
|
||||||
=========================================
|
=========================================
|
||||||
END OF buffer-crc32@0.2.13 AND INFORMATION
|
END OF buffer-crc32@0.2.13 AND INFORMATION
|
||||||
|
|
||||||
%% codemirror-shadow-1@0.0.1 NOTICES AND INFORMATION BEGIN HERE
|
%% codemirror@5.65.18 NOTICES AND INFORMATION BEGIN HERE
|
||||||
=========================================
|
=========================================
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
|
@ -232,7 +232,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
=========================================
|
=========================================
|
||||||
END OF codemirror-shadow-1@0.0.1 AND INFORMATION
|
END OF codemirror@5.65.18 AND INFORMATION
|
||||||
|
|
||||||
%% colors@1.4.0 NOTICES AND INFORMATION BEGIN HERE
|
%% colors@1.4.0 NOTICES AND INFORMATION BEGIN HERE
|
||||||
=========================================
|
=========================================
|
||||||
|
|
|
||||||
|
|
@ -204,7 +204,7 @@ export type MatcherReceived = {
|
||||||
|
|
||||||
export function matchesAriaTree(rootElement: Element, template: AriaTemplateNode): { matches: AriaNode[], received: MatcherReceived } {
|
export function matchesAriaTree(rootElement: Element, template: AriaTemplateNode): { matches: AriaNode[], received: MatcherReceived } {
|
||||||
const root = generateAriaTree(rootElement);
|
const root = generateAriaTree(rootElement);
|
||||||
const matches = matchesNodeDeep(root, template);
|
const matches = matchesNodeDeep(root, template, false);
|
||||||
return {
|
return {
|
||||||
matches,
|
matches,
|
||||||
received: {
|
received: {
|
||||||
|
|
@ -215,8 +215,9 @@ export function matchesAriaTree(rootElement: Element, template: AriaTemplateNode
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAllByAria(rootElement: Element, template: AriaTemplateNode): Element[] {
|
export function getAllByAria(rootElement: Element, template: AriaTemplateNode): Element[] {
|
||||||
const result = matchesAriaTree(rootElement, template);
|
const root = generateAriaTree(rootElement);
|
||||||
return result.matches.map(n => n.element);
|
const matches = matchesNodeDeep(root, template, true);
|
||||||
|
return matches.map(n => n.element);
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchesNode(node: AriaNode | string, template: AriaTemplateNode, depth: number): boolean {
|
function matchesNode(node: AriaNode | string, template: AriaTemplateNode, depth: number): boolean {
|
||||||
|
|
@ -265,12 +266,12 @@ function containsList(children: (AriaNode | string)[], template: AriaTemplateNod
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchesNodeDeep(root: AriaNode, template: AriaTemplateNode): AriaNode[] {
|
function matchesNodeDeep(root: AriaNode, template: AriaTemplateNode, collectAll: boolean): AriaNode[] {
|
||||||
const results: AriaNode[] = [];
|
const results: AriaNode[] = [];
|
||||||
const visit = (node: AriaNode | string): boolean => {
|
const visit = (node: AriaNode | string): boolean => {
|
||||||
if (matchesNode(node, template, 0)) {
|
if (matchesNode(node, template, 0)) {
|
||||||
results.push(node as AriaNode);
|
results.push(node as AriaNode);
|
||||||
return true;
|
return !collectAll;
|
||||||
}
|
}
|
||||||
if (typeof node === 'string')
|
if (typeof node === 'string')
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -207,9 +207,9 @@ class InspectTool implements RecorderTool {
|
||||||
class RecordActionTool implements RecorderTool {
|
class RecordActionTool implements RecorderTool {
|
||||||
private _recorder: Recorder;
|
private _recorder: Recorder;
|
||||||
private _performingActions = new Set<actions.PerformOnRecordAction>();
|
private _performingActions = new Set<actions.PerformOnRecordAction>();
|
||||||
private _hoveredModel: HighlightModeWithSelector | null = null;
|
private _hoveredModel: HighlightModelWithSelector | null = null;
|
||||||
private _hoveredElement: HTMLElement | null = null;
|
private _hoveredElement: HTMLElement | null = null;
|
||||||
private _activeModel: HighlightModeWithSelector | null = null;
|
private _activeModel: HighlightModelWithSelector | null = null;
|
||||||
private _expectProgrammaticKeyUp = false;
|
private _expectProgrammaticKeyUp = false;
|
||||||
private _pendingClickAction: { action: actions.ClickAction, timeout: number } | undefined;
|
private _pendingClickAction: { action: actions.ClickAction, timeout: number } | undefined;
|
||||||
|
|
||||||
|
|
@ -605,7 +605,7 @@ class RecordActionTool implements RecorderTool {
|
||||||
|
|
||||||
class TextAssertionTool implements RecorderTool {
|
class TextAssertionTool implements RecorderTool {
|
||||||
private _recorder: Recorder;
|
private _recorder: Recorder;
|
||||||
private _hoverHighlight: HighlightModeWithSelector | null = null;
|
private _hoverHighlight: HighlightModelWithSelector | null = null;
|
||||||
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>();
|
||||||
|
|
@ -1460,7 +1460,7 @@ type HighlightModel = HighlightOptions & {
|
||||||
elements: Element[];
|
elements: Element[];
|
||||||
};
|
};
|
||||||
|
|
||||||
type HighlightModeWithSelector = HighlightModel & {
|
type HighlightModelWithSelector = HighlightModel & {
|
||||||
selector: string;
|
selector: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ export type AriaTemplateNode = AriaTemplateRoleNode | AriaTemplateTextNode;
|
||||||
export function parseYamlTemplate(fragment: ParsedYaml): AriaTemplateNode {
|
export function parseYamlTemplate(fragment: ParsedYaml): AriaTemplateNode {
|
||||||
const result: AriaTemplateNode = { kind: 'role', role: 'fragment' };
|
const result: AriaTemplateNode = { kind: 'role', role: 'fragment' };
|
||||||
populateNode(result, fragment);
|
populateNode(result, fragment);
|
||||||
|
if (result.children && result.children.length === 1)
|
||||||
|
return result.children[0];
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ This project incorporates components from the projects listed below. The origina
|
||||||
- chalk@4.1.2 (https://github.com/chalk/chalk)
|
- chalk@4.1.2 (https://github.com/chalk/chalk)
|
||||||
- chokidar@3.6.0 (https://github.com/paulmillr/chokidar)
|
- chokidar@3.6.0 (https://github.com/paulmillr/chokidar)
|
||||||
- ci-info@3.9.0 (https://github.com/watson/ci-info)
|
- ci-info@3.9.0 (https://github.com/watson/ci-info)
|
||||||
- codemirror-shadow-1@0.0.1 (https://github.com/codemirror/CodeMirror)
|
- codemirror@5.65.18 (https://github.com/codemirror/CodeMirror)
|
||||||
- color-convert@1.9.3 (https://github.com/Qix-/color-convert)
|
- color-convert@1.9.3 (https://github.com/Qix-/color-convert)
|
||||||
- color-convert@2.0.1 (https://github.com/Qix-/color-convert)
|
- color-convert@2.0.1 (https://github.com/Qix-/color-convert)
|
||||||
- color-name@1.1.3 (https://github.com/dfcreative/color-name)
|
- color-name@1.1.3 (https://github.com/dfcreative/color-name)
|
||||||
|
|
@ -3103,7 +3103,7 @@ SOFTWARE.
|
||||||
=========================================
|
=========================================
|
||||||
END OF ci-info@3.9.0 AND INFORMATION
|
END OF ci-info@3.9.0 AND INFORMATION
|
||||||
|
|
||||||
%% codemirror-shadow-1@0.0.1 NOTICES AND INFORMATION BEGIN HERE
|
%% codemirror@5.65.18 NOTICES AND INFORMATION BEGIN HERE
|
||||||
=========================================
|
=========================================
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
|
@ -3127,7 +3127,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
=========================================
|
=========================================
|
||||||
END OF codemirror-shadow-1@0.0.1 AND INFORMATION
|
END OF codemirror@5.65.18 AND INFORMATION
|
||||||
|
|
||||||
%% color-convert@1.9.3 NOTICES AND INFORMATION BEGIN HERE
|
%% color-convert@1.9.3 NOTICES AND INFORMATION BEGIN HERE
|
||||||
=========================================
|
=========================================
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import { asLocator } from '@isomorphic/locatorGenerators';
|
||||||
import { toggleTheme } from '@web/theme';
|
import { toggleTheme } from '@web/theme';
|
||||||
import { copy } from '@web/uiUtils';
|
import { copy } from '@web/uiUtils';
|
||||||
import yaml from 'yaml';
|
import yaml from 'yaml';
|
||||||
import type { YAMLError } from 'yaml';
|
|
||||||
import { parseAriaKey } from '@isomorphic/ariaSnapshot';
|
import { parseAriaKey } from '@isomorphic/ariaSnapshot';
|
||||||
import type { AriaKeyError, ParsedYaml } from '@isomorphic/ariaSnapshot';
|
import type { AriaKeyError, ParsedYaml } from '@isomorphic/ariaSnapshot';
|
||||||
|
|
||||||
|
|
@ -199,7 +198,7 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||||
{
|
{
|
||||||
id: 'aria',
|
id: 'aria',
|
||||||
title: 'Aria snapshot',
|
title: 'Aria snapshot',
|
||||||
render: () => <CodeMirrorWrapper text={ariaSnapshot || ''} language={'yaml'} readOnly={false} onChange={onAriaEditorChange} highlight={ariaSnapshotErrors} wrapLines={true} />
|
render: () => <CodeMirrorWrapper text={ariaSnapshot || ''} language={'yaml'} readOnly={false} onChange={onAriaEditorChange} highlight={ariaSnapshotErrors} wrapLines={false} />
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
selectedTab={selectedTab}
|
selectedTab={selectedTab}
|
||||||
|
|
@ -211,32 +210,31 @@ export const Recorder: React.FC<RecorderProps> = ({
|
||||||
|
|
||||||
function parseAriaSnapshot(ariaSnapshot: string): { fragment?: ParsedYaml, errors: SourceHighlight[] } {
|
function parseAriaSnapshot(ariaSnapshot: string): { fragment?: ParsedYaml, errors: SourceHighlight[] } {
|
||||||
const lineCounter = new yaml.LineCounter();
|
const lineCounter = new yaml.LineCounter();
|
||||||
let yamlDoc: yaml.Document;
|
const yamlDoc = yaml.parseDocument(ariaSnapshot, {
|
||||||
try {
|
|
||||||
yamlDoc = yaml.parseDocument(ariaSnapshot, {
|
|
||||||
keepSourceTokens: true,
|
keepSourceTokens: true,
|
||||||
lineCounter,
|
lineCounter,
|
||||||
|
prettyErrors: false,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
|
||||||
const error = e as YAMLError;
|
|
||||||
const pos = error.linePos?.[0];
|
|
||||||
return {
|
|
||||||
errors: [{
|
|
||||||
line: pos?.line || 0,
|
|
||||||
type: 'error',
|
|
||||||
message: error.message,
|
|
||||||
}],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const errors: SourceHighlight[] = [];
|
const errors: SourceHighlight[] = [];
|
||||||
|
for (const error of yamlDoc.errors) {
|
||||||
|
errors.push({
|
||||||
|
line: lineCounter.linePos(error.pos[0]).line,
|
||||||
|
type: 'error',
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (yamlDoc.errors.length)
|
||||||
|
return { errors };
|
||||||
|
|
||||||
const handleKey = (key: yaml.Scalar<string>) => {
|
const handleKey = (key: yaml.Scalar<string>) => {
|
||||||
try {
|
try {
|
||||||
parseAriaKey(key.value);
|
parseAriaKey(key.value);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
const keyError = e as AriaKeyError;
|
const keyError = e as AriaKeyError;
|
||||||
errors.push({
|
errors.push({
|
||||||
message: keyError.message,
|
message: keyError.shortMessage,
|
||||||
line: lineCounter.linePos(key.srcToken!.offset + keyError.pos).line,
|
line: lineCounter.linePos(key.srcToken!.offset + keyError.pos).line,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"scripts": {},
|
"scripts": {},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"codemirror-shadow-1": "0.0.1",
|
"codemirror": "5.65.18",
|
||||||
"xterm": "^5.1.0",
|
"xterm": "^5.1.0",
|
||||||
"xterm-addon-fit": "^0.7.0"
|
"xterm-addon-fit": "^0.7.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import codemirror from 'codemirror-shadow-1';
|
import codemirror from 'codemirror';
|
||||||
import type codemirrorType from 'codemirror';
|
import type codemirrorType from 'codemirror';
|
||||||
import 'codemirror-shadow-1/lib/codemirror.css';
|
import 'codemirror/lib/codemirror.css';
|
||||||
import 'codemirror-shadow-1/mode/css/css';
|
import 'codemirror/mode/css/css';
|
||||||
import 'codemirror-shadow-1/mode/htmlmixed/htmlmixed';
|
import 'codemirror/mode/htmlmixed/htmlmixed';
|
||||||
import 'codemirror-shadow-1/mode/javascript/javascript';
|
import 'codemirror/mode/javascript/javascript';
|
||||||
import 'codemirror-shadow-1/mode/python/python';
|
import 'codemirror/mode/python/python';
|
||||||
import 'codemirror-shadow-1/mode/clike/clike';
|
import 'codemirror/mode/clike/clike';
|
||||||
import 'codemirror-shadow-1/mode/markdown/markdown';
|
import 'codemirror/mode/markdown/markdown';
|
||||||
import 'codemirror-shadow-1/addon/mode/simple';
|
import 'codemirror/addon/mode/simple';
|
||||||
import 'codemirror-shadow-1/mode/yaml/yaml';
|
import 'codemirror/mode/yaml/yaml';
|
||||||
|
|
||||||
export type CodeMirror = typeof codemirrorType;
|
export type CodeMirror = typeof codemirrorType;
|
||||||
export default codemirror;
|
export default codemirror;
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,13 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
|
||||||
return;
|
return;
|
||||||
|
|
||||||
let valueChanged = false;
|
let valueChanged = false;
|
||||||
if (codemirror.getValue() !== text) {
|
// CodeMirror has a bug that renders cursor poorly on a last line.
|
||||||
codemirror.setValue(text);
|
let normalizedText = text;
|
||||||
|
if (!readOnly && !wrapLines && !normalizedText.endsWith('\n'))
|
||||||
|
normalizedText = normalizedText + '\n';
|
||||||
|
|
||||||
|
if (codemirror.getValue() !== normalizedText) {
|
||||||
|
codemirror.setValue(normalizedText);
|
||||||
valueChanged = true;
|
valueChanged = true;
|
||||||
if (focusOnChange) {
|
if (focusOnChange) {
|
||||||
codemirror.execCommand('selectAll');
|
codemirror.execCommand('selectAll');
|
||||||
|
|
@ -170,7 +175,7 @@ export const CodeMirrorWrapper: React.FC<SourceProps> = ({
|
||||||
if (changeListener)
|
if (changeListener)
|
||||||
codemirror.off('change', changeListener);
|
codemirror.off('change', changeListener);
|
||||||
};
|
};
|
||||||
}, [codemirror, text, highlight, revealLine, focusOnChange, onChange]);
|
}, [codemirror, text, highlight, revealLine, focusOnChange, onChange, readOnly]);
|
||||||
|
|
||||||
return <div data-testid={dataTestId} className='cm-wrapper' ref={codemirrorElement} onClick={onCodeMirrorClick}></div>;
|
return <div data-testid={dataTestId} className='cm-wrapper' ref={codemirrorElement} onClick={onCodeMirrorClick}></div>;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test, expect } from './inspectorTest';
|
import { test, expect } from './inspectorTest';
|
||||||
|
import { roundBox } from '../../page/pageTest';
|
||||||
|
|
||||||
test.describe(() => {
|
test.describe(() => {
|
||||||
test.skip(({ mode }) => mode !== 'default');
|
test.skip(({ mode }) => mode !== 'default');
|
||||||
|
|
@ -59,4 +60,90 @@ test.describe(() => {
|
||||||
await expect.poll(() =>
|
await expect.poll(() =>
|
||||||
recorder.text('C#')).toContain(`await Expect(page.GetByRole(AriaRole.Button)).ToMatchAriaSnapshotAsync("- button /Submit \\\\d+/");`);
|
recorder.text('C#')).toContain(`await Expect(page.GetByRole(AriaRole.Button)).ToMatchAriaSnapshotAsync("- button /Submit \\\\d+/");`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should inspect aria snapshot', async ({ openRecorder }) => {
|
||||||
|
const { recorder } = await openRecorder();
|
||||||
|
await recorder.setContentAndWait(`<main><button>Submit</button></main>`);
|
||||||
|
await recorder.page.click('x-pw-tool-item.pick-locator');
|
||||||
|
await recorder.page.hover('button');
|
||||||
|
await recorder.trustedClick();
|
||||||
|
await recorder.recorderPage.getByRole('tab', { name: 'Aria snapshot ' }).click();
|
||||||
|
await expect(recorder.recorderPage.locator('.tab-aria .CodeMirror')).toMatchAriaSnapshot(`
|
||||||
|
- textbox
|
||||||
|
- text: '- button "Submit"'
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should update aria snapshot highlight', async ({ openRecorder }) => {
|
||||||
|
const { recorder } = await openRecorder();
|
||||||
|
await recorder.setContentAndWait(`<main>
|
||||||
|
<button>Submit</button>
|
||||||
|
<button>Cancel</button>
|
||||||
|
</main>`);
|
||||||
|
|
||||||
|
const submitButton = recorder.page.getByRole('button', { name: 'Submit' });
|
||||||
|
const cancelButton = recorder.page.getByRole('button', { name: 'Cancel' });
|
||||||
|
|
||||||
|
await recorder.recorderPage.getByRole('button', { name: 'Record' }).click();
|
||||||
|
|
||||||
|
await recorder.page.click('x-pw-tool-item.pick-locator');
|
||||||
|
await submitButton.hover();
|
||||||
|
await recorder.trustedClick();
|
||||||
|
await recorder.recorderPage.getByRole('tab', { name: 'Aria snapshot ' }).click();
|
||||||
|
await expect(recorder.recorderPage.locator('.tab-aria .CodeMirror')).toMatchAriaSnapshot(`
|
||||||
|
- text: '- button "Submit"'
|
||||||
|
`);
|
||||||
|
|
||||||
|
await recorder.recorderPage.locator('.tab-aria .CodeMirror').click();
|
||||||
|
await recorder.recorderPage.keyboard.press('ArrowLeft');
|
||||||
|
for (let i = 0; i < '"Submit"'.length; i++)
|
||||||
|
await recorder.recorderPage.keyboard.press('Backspace');
|
||||||
|
|
||||||
|
{
|
||||||
|
// No accessible name => two boxes.
|
||||||
|
const box11 = roundBox(await submitButton.boundingBox());
|
||||||
|
const box12 = roundBox(await recorder.page.locator('x-pw-highlight').first().boundingBox());
|
||||||
|
expect(box11).toEqual(box12);
|
||||||
|
|
||||||
|
const box21 = roundBox(await cancelButton.boundingBox());
|
||||||
|
const box22 = roundBox(await recorder.page.locator('x-pw-highlight').last().boundingBox());
|
||||||
|
expect(box21).toEqual(box22);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Different button.
|
||||||
|
await recorder.recorderPage.locator('.tab-aria .CodeMirror').pressSequentially('"Cancel"');
|
||||||
|
await expect(recorder.page.locator('x-pw-highlight')).toBeVisible();
|
||||||
|
const box1 = roundBox(await cancelButton.boundingBox());
|
||||||
|
const box2 = roundBox(await recorder.page.locator('x-pw-highlight').boundingBox());
|
||||||
|
expect(box1).toEqual(box2);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should show aria snapshot error', async ({ openRecorder }) => {
|
||||||
|
const { recorder } = await openRecorder();
|
||||||
|
await recorder.setContentAndWait(`<main>
|
||||||
|
<button>Submit</button>
|
||||||
|
<button>Cancel</button>
|
||||||
|
</main>`);
|
||||||
|
|
||||||
|
const submitButton = recorder.page.getByRole('button', { name: 'Submit' });
|
||||||
|
await recorder.recorderPage.getByRole('button', { name: 'Record' }).click();
|
||||||
|
|
||||||
|
await recorder.page.click('x-pw-tool-item.pick-locator');
|
||||||
|
await submitButton.hover();
|
||||||
|
await recorder.trustedClick();
|
||||||
|
|
||||||
|
await recorder.recorderPage.getByRole('tab', { name: 'Aria snapshot ' }).click();
|
||||||
|
await expect(recorder.recorderPage.locator('.tab-aria .CodeMirror')).toMatchAriaSnapshot(`
|
||||||
|
- text: '- button "Submit"'
|
||||||
|
`);
|
||||||
|
|
||||||
|
await recorder.recorderPage.locator('.tab-aria .CodeMirror').click();
|
||||||
|
await recorder.recorderPage.keyboard.press('ArrowLeft');
|
||||||
|
await recorder.recorderPage.keyboard.press('Backspace');
|
||||||
|
await expect(recorder.recorderPage.locator('.tab-aria .CodeMirror')).toMatchAriaSnapshot(`
|
||||||
|
- text: '- button "Submit Unterminated string'
|
||||||
|
`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,8 @@
|
||||||
import type { Page } from 'playwright-core';
|
import type { Page } from 'playwright-core';
|
||||||
import { test as it, expect, Recorder } from './inspectorTest';
|
import { test as it, expect, Recorder } from './inspectorTest';
|
||||||
import { waitForTestLog } from '../../config/utils';
|
import { waitForTestLog } from '../../config/utils';
|
||||||
|
import { roundBox } from '../../page/pageTest';
|
||||||
|
import type { BoundingBox } from '../../page/pageTest';
|
||||||
|
|
||||||
it('should resume when closing inspector', async ({ page, recorderPageGetter, closeRecorder, mode }) => {
|
it('should resume when closing inspector', async ({ page, recorderPageGetter, closeRecorder, mode }) => {
|
||||||
it.skip(mode !== 'default');
|
it.skip(mode !== 'default');
|
||||||
|
|
@ -385,7 +386,7 @@ it.describe('pause', () => {
|
||||||
})();
|
})();
|
||||||
const recorderPage = await recorderPageGetter();
|
const recorderPage = await recorderPageGetter();
|
||||||
|
|
||||||
const box1Promise = waitForTestLog<Box>(page, 'Highlight box for test: ');
|
const box1Promise = waitForTestLog<BoundingBox>(page, 'Highlight box for test: ');
|
||||||
await recorderPage.getByText('Locator', { exact: true }).click();
|
await recorderPage.getByText('Locator', { exact: true }).click();
|
||||||
await recorderPage.locator('.tabbed-pane .CodeMirror').click();
|
await recorderPage.locator('.tabbed-pane .CodeMirror').click();
|
||||||
await recorderPage.keyboard.type('getByText(\'Submit\')');
|
await recorderPage.keyboard.type('getByText(\'Submit\')');
|
||||||
|
|
@ -407,7 +408,7 @@ it.describe('pause', () => {
|
||||||
})();
|
})();
|
||||||
const recorderPage = await recorderPageGetter();
|
const recorderPage = await recorderPageGetter();
|
||||||
|
|
||||||
const box1Promise = waitForTestLog<Box>(page, 'Highlight box for test: ');
|
const box1Promise = waitForTestLog<BoundingBox>(page, 'Highlight box for test: ');
|
||||||
await recorderPage.getByText('Locator', { exact: true }).click();
|
await recorderPage.getByText('Locator', { exact: true }).click();
|
||||||
await recorderPage.locator('.tabbed-pane .CodeMirror').click();
|
await recorderPage.locator('.tabbed-pane .CodeMirror').click();
|
||||||
await recorderPage.keyboard.type('GetByText("Submit")');
|
await recorderPage.keyboard.type('GetByText("Submit")');
|
||||||
|
|
@ -472,7 +473,7 @@ it.describe('pause', () => {
|
||||||
})();
|
})();
|
||||||
const recorderPage = await recorderPageGetter();
|
const recorderPage = await recorderPageGetter();
|
||||||
|
|
||||||
const box1Promise = waitForTestLog<Box>(page, 'Highlight box for test: ');
|
const box1Promise = waitForTestLog<BoundingBox>(page, 'Highlight box for test: ');
|
||||||
await recorderPage.click('[title="Step over (F10)"]');
|
await recorderPage.click('[title="Step over (F10)"]');
|
||||||
const box2 = roundBox((await page.locator('#target').boundingBox())!);
|
const box2 = roundBox((await page.locator('#target').boundingBox())!);
|
||||||
const box1 = roundBox(await box1Promise);
|
const box1 = roundBox(await box1Promise);
|
||||||
|
|
@ -514,13 +515,3 @@ async function sanitizeLog(recorderPage: Page): Promise<string[]> {
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Box = { x: number, y: number, width: number, height: number };
|
|
||||||
function roundBox(box: Box): Box {
|
|
||||||
return {
|
|
||||||
x: Math.round(box.x * 1000),
|
|
||||||
y: Math.round(box.y * 1000),
|
|
||||||
width: Math.round(box.width * 1000),
|
|
||||||
height: Math.round(box.height * 1000),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { test as it, expect } from './pageTest';
|
import { test as it, expect, roundBox } from './pageTest';
|
||||||
import type { Locator } from 'playwright-core';
|
|
||||||
|
|
||||||
type BoundingBox = Awaited<ReturnType<Locator['boundingBox']>>;
|
|
||||||
|
|
||||||
it.skip(({ mode }) => mode !== 'default', 'Highlight element has a closed shadow-root on != default');
|
it.skip(({ mode }) => mode !== 'default', 'Highlight element has a closed shadow-root on != default');
|
||||||
|
|
||||||
|
|
@ -30,12 +27,3 @@ it('should highlight locator', async ({ page }) => {
|
||||||
const box2 = roundBox(await page.locator('x-pw-highlight').boundingBox());
|
const box2 = roundBox(await page.locator('x-pw-highlight').boundingBox());
|
||||||
expect(box1).toEqual(box2);
|
expect(box1).toEqual(box2);
|
||||||
});
|
});
|
||||||
|
|
||||||
function roundBox(box: BoundingBox): BoundingBox {
|
|
||||||
return {
|
|
||||||
x: Math.round(box.x),
|
|
||||||
y: Math.round(box.y),
|
|
||||||
width: Math.round(box.width),
|
|
||||||
height: Math.round(box.height),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Frame, Page, TestType } from '@playwright/test';
|
import type { Frame, Page, TestType, Locator } from '@playwright/test';
|
||||||
import type { PlatformWorkerFixtures } from '../config/platformFixtures';
|
import type { PlatformWorkerFixtures } from '../config/platformFixtures';
|
||||||
import type { TestModeTestFixtures, TestModeWorkerFixtures, TestModeWorkerOptions } from '../config/testModeFixtures';
|
import type { TestModeTestFixtures, TestModeWorkerFixtures, TestModeWorkerOptions } from '../config/testModeFixtures';
|
||||||
import { androidTest } from '../android/androidTest';
|
import { androidTest } from '../android/androidTest';
|
||||||
|
|
@ -26,6 +26,7 @@ import type { ServerFixtures, ServerWorkerOptions } from '../config/serverFixtur
|
||||||
export { expect } from '@playwright/test';
|
export { expect } from '@playwright/test';
|
||||||
|
|
||||||
let impl: TestType<PageTestFixtures & ServerFixtures & TestModeTestFixtures, PageWorkerFixtures & PlatformWorkerFixtures & TestModeWorkerFixtures & TestModeWorkerOptions & ServerWorkerOptions> = browserTest;
|
let impl: TestType<PageTestFixtures & ServerFixtures & TestModeTestFixtures, PageWorkerFixtures & PlatformWorkerFixtures & TestModeWorkerFixtures & TestModeWorkerOptions & ServerWorkerOptions> = browserTest;
|
||||||
|
export type BoundingBox = Awaited<ReturnType<Locator['boundingBox']>>;
|
||||||
|
|
||||||
if (process.env.PWPAGE_IMPL === 'android')
|
if (process.env.PWPAGE_IMPL === 'android')
|
||||||
impl = androidTest;
|
impl = androidTest;
|
||||||
|
|
@ -43,3 +44,12 @@ export async function rafraf(target: Page | Frame, count = 1) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function roundBox(box: BoundingBox): BoundingBox {
|
||||||
|
return {
|
||||||
|
x: Math.round(box.x),
|
||||||
|
y: Math.round(box.y),
|
||||||
|
width: Math.round(box.width),
|
||||||
|
height: Math.round(box.height),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@ This project incorporates components from the projects listed below. The origina
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const packages = await checkDir('node_modules/codemirror-shadow-1');
|
const packages = await checkDir('node_modules/codemirror');
|
||||||
for (const [key, value] of Object.entries(packages)) {
|
for (const [key, value] of Object.entries(packages)) {
|
||||||
if (value.licenseText)
|
if (value.licenseText)
|
||||||
allPackages[key] = value;
|
allPackages[key] = value;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue