chore: allow editing aria template in recorder (tests) (#33522)

This commit is contained in:
Pavel Feldman 2024-11-08 17:18:51 -08:00 committed by GitHub
parent c29f573243
commit 503f74da90
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 166 additions and 83 deletions

11
package-lock.json generated
View file

@ -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"
} }

View file

@ -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
========================================= =========================================

View file

@ -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;

View file

@ -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;
}; };

View file

@ -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;
} }

View file

@ -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
========================================= =========================================

View file

@ -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 { keepSourceTokens: true,
yamlDoc = yaml.parseDocument(ariaSnapshot, { lineCounter,
keepSourceTokens: true, prettyErrors: false,
lineCounter, });
});
} 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',
}); });

View file

@ -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"
} }

View file

@ -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;

View file

@ -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>;
}; };

View file

@ -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'
`);
});
}); });

View file

@ -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),
};
}

View file

@ -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),
};
}

View file

@ -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),
};
}

View file

@ -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;