[Recorder] turn injected tests into unit tests (part 2) Interaction tests are removed with this CL. Bug: 402372244 Change-Id: Ia6fb66f9765b576dac2ddec0a5f270e865e69f37 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6343238 Auto-Submit: Alex Rudenko <alexrudenko@chromium.org> Commit-Queue: Nikolay Vitkov <nvitkov@chromium.org> Reviewed-by: Nikolay Vitkov <nvitkov@chromium.org>
diff --git a/front_end/panels/recorder/injected.test.ts b/front_end/panels/recorder/injected.test.ts index 6f3f470..a87c26e 100644 --- a/front_end/panels/recorder/injected.test.ts +++ b/front_end/panels/recorder/injected.test.ts
@@ -84,4 +84,176 @@ ] ]); }); + + it('should get selectors for elements with custom selector attributes', async () => { + const window = await createSandbox(); + const targets = [ + ...window.document.querySelectorAll('.custom-selector-attribute'), + window.document.querySelector('#shadow-root-with-custom-selectors')?.shadowRoot?.querySelector('button') as + HTMLButtonElement, + ]; + const selectors = targets.map( + window.DevToolsRecorder.recordingClientForTesting.getSelectors, + ); + assert.deepEqual(selectors, [ + [ + [ + '[data-testid=\'unique\']', + ], + [ + 'xpath///*[@data-testid="unique"]', + ], + [ + 'pierce/[data-testid=\'unique\']', + ] + ], + [ + ['[data-testid=\'\\31 23456789\']'], ['xpath///*[@data-testid="123456789"]'], + ['pierce/[data-testid=\'\\31 23456789\']'], ['text/Custom selector (invalid'] + ], + [ + ['[data-qa=\'custom-id\']', '[data-testid=\'shadow\\ button\']'], ['pierce/[data-testid=\'shadow\\ button\']'], + ['text/Shadow button'] + ] + ]); + }); + + it('should get selectors for shadow root elements', async () => { + const window = await createSandbox(); + const selectors = window.DevToolsRecorder.recordingClientForTesting.getSelectors( + window.document.querySelector('main') + ?.querySelector('shadow-css-selector-element') + ?.shadowRoot?.querySelector('#insideShadowRoot')!, + ); + assert.deepEqual(selectors, [ + ['main > shadow-css-selector-element', '#insideShadowRoot'], + ['pierce/main > shadow-css-selector-element', 'pierce/#insideShadowRoot'] + ]); + }); + + it('should get an ARIA selector for shadow root elements', async () => { + const window = await createSandbox(); + const selectors = window.DevToolsRecorder.recordingClientForTesting.getSelectors( + window.document.querySelector('[aria-role="main"]') + ?.querySelector('shadow-aria-selector-element') + ?.shadowRoot?.querySelector('button')!, + ); + assert.deepEqual(selectors, [ + ['aria/[role="main"]', 'aria/login'], ['div:nth-of-type(2) > shadow-aria-selector-element', 'button'], + ['pierce/div:nth-of-type(2) > shadow-aria-selector-element', 'pierce/button'] + ]); + }); + + it('should not get an ARIA selector if the target element has no name or role', async () => { + const window = await createSandbox(); + const selectors = window.DevToolsRecorder.recordingClientForTesting.getSelectors( + window.document.querySelector('#no-aria-name-or-role')!); + assert.deepEqual( + selectors, + [['#no-aria-name-or-role'], ['xpath///*[@id="no-aria-name-or-role"]'], ['pierce/#no-aria-name-or-role']]); + }); + + describe('CSS selectors', () => { + it('should query CSS selectors', async () => { + const window = await createSandbox(); + const results = [ + window.DevToolsRecorder.recordingClientForTesting + .queryCSSSelectorAllForTesting( + ['[data-qa=custom-id]', '[data-testid=shadow\\ button]'], + ) + .length, + window.DevToolsRecorder.recordingClientForTesting + .queryCSSSelectorAllForTesting( + ['[data-qa=custom-id]'], + ) + .length, + window.DevToolsRecorder.recordingClientForTesting + .queryCSSSelectorAllForTesting( + '[data-qa=custom-id]', + ) + .length, + window.DevToolsRecorder.recordingClientForTesting + .queryCSSSelectorAllForTesting( + '.doesnotexist', + ) + .length, + window.DevToolsRecorder.recordingClientForTesting + .queryCSSSelectorAllForTesting( + ['[data-qa=custom-id]', '.doesnotexist'], + ) + .length, + window.DevToolsRecorder.recordingClientForTesting + .queryCSSSelectorAllForTesting( + ['#notunique'], + ) + .length, + ]; + assert.deepEqual(results, [1, 1, 1, 0, 0, 2]); + }); + + it('should return not-optimized CSS selectors for duplicate elements', async () => { + const window = await createSandbox(); + const selectors = + window.DevToolsRecorder.recordingClientForTesting.getSelectors(window.document.querySelector('#notunique')!); + assert.deepEqual(selectors, [ + ['div:nth-of-type(3) > div:nth-of-type(2)'], ['xpath///*[@id="notunique"]'], + ['pierce/div:nth-of-type(3) > div:nth-of-type(2)'] + ]); + }); + }); + + describe('Text selectors', () => { + const getSelectorOfButtonWithLength = async (length: number) => { + const window = await createSandbox(); + const selector = `#buttonWithLength${length}`; + const target = window.document.querySelector(selector); + if (!target) { + throw new Error(`${selector} could not be found.`); + } + if (target.innerHTML.length !== length) { + throw new Error(`${selector} is not of length ${length}`); + } + return window.DevToolsRecorder.recordingClientForTesting.getTextSelector( + target, + ); + }; + const MINIMUM_LENGTH = 12; + const MAXIMUM_LENGTH = 64; + const SAME_PREFIX_TEXT_LENGTH = 32; + + it('should return a text selector for elements < minimum length', async () => { + const selectors = await getSelectorOfButtonWithLength(MINIMUM_LENGTH - 1); + assert.deepEqual(selectors, ['text/length a 11']); + }); + it('should return a text selector for elements == minimum length', async () => { + const selectors = await getSelectorOfButtonWithLength(MINIMUM_LENGTH); + assert.deepEqual(selectors, ['text/length aa 12']); + }); + it('should return a text selector for elements == maximum length', async () => { + const selectors = await getSelectorOfButtonWithLength(MAXIMUM_LENGTH); + assert.deepEqual(selectors, ['text/length aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaa 64']); + }); + it('should not return a text selector for elements > maximum length', async () => { + const selectors = await getSelectorOfButtonWithLength(MAXIMUM_LENGTH + 1); + assert.isUndefined(selectors); + }); + it('should return a text selector correctly with same prefix elements', async () => { + let selectors = await getSelectorOfButtonWithLength( + SAME_PREFIX_TEXT_LENGTH, + ); + assert.deepEqual(selectors, ['text/length aaaaaaaaa aaaaaaaaa aa 32']); + selectors = await getSelectorOfButtonWithLength( + SAME_PREFIX_TEXT_LENGTH + 1, + ); + assert.deepEqual(selectors, ['text/length aaaaaaaaa aaaaaaaaa aaa 33']); + }); + it('should trim text selectors', async () => { + const window = await createSandbox(); + assert.deepEqual( + window.DevToolsRecorder.recordingClientForTesting.getTextSelector( + window.document.querySelector('#buttonWithNewLines')!, + ), + ['text/with newlines']); + }); + }); });
diff --git a/front_end/ui/components/docs/recorder_injected/BUILD.gn b/front_end/ui/components/docs/recorder_injected/BUILD.gn index dc3b8e3..7517391 100644 --- a/front_end/ui/components/docs/recorder_injected/BUILD.gn +++ b/front_end/ui/components/docs/recorder_injected/BUILD.gn
@@ -5,18 +5,7 @@ import("../../../../../scripts/build/ninja/copy.gni") import("../../../../../scripts/build/typescript/typescript.gni") -ts_library("ts") { - testonly = true - sources = [ "basic.ts" ] - deps = [ - "../../../../panels/recorder/injected:bundle", - "../../../../testing", - ] -} - copy_to_gen("recorder_injected") { testonly = true sources = [ "basic.html" ] - - deps = [ ":ts" ] }
diff --git a/front_end/ui/components/docs/recorder_injected/basic.html b/front_end/ui/components/docs/recorder_injected/basic.html index dd8d69c..082fde7 100644 --- a/front_end/ui/components/docs/recorder_injected/basic.html +++ b/front_end/ui/components/docs/recorder_injected/basic.html
@@ -96,6 +96,5 @@ <div id="notunique"></div> <div id="notunique"></div> - <script type="module" src="./basic.js"></script> </body> </html>
diff --git a/front_end/ui/components/docs/recorder_injected/basic.ts b/front_end/ui/components/docs/recorder_injected/basic.ts deleted file mode 100644 index a43df15..0000000 --- a/front_end/ui/components/docs/recorder_injected/basic.ts +++ /dev/null
@@ -1,5 +0,0 @@ -// Copyright 2023 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import '../../../../panels/recorder/injected/injected.js';
diff --git a/test/interactions/BUILD.gn b/test/interactions/BUILD.gn index 2c6edbe..fd5877a 100644 --- a/test/interactions/BUILD.gn +++ b/test/interactions/BUILD.gn
@@ -17,7 +17,6 @@ "helpers", "panels/explain", "panels/performance/timeline", - "panels/recorder/injected", "text_editor", "tree_outline", "ui/components",
diff --git a/test/interactions/panels/recorder/injected/BUILD.gn b/test/interactions/panels/recorder/injected/BUILD.gn deleted file mode 100644 index 8281df5..0000000 --- a/test/interactions/panels/recorder/injected/BUILD.gn +++ /dev/null
@@ -1,17 +0,0 @@ -# Copyright 2023 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("../../../../../scripts/build/typescript/typescript.gni") - -node_ts_library("injected") { - sources = [ "injected_test.ts" ] - - deps = [ - "../../../../../front_end/panels/recorder/injected:bundle", - "../../../../../front_end/panels/recorder/models:bundle", - "../../../../../test/e2e/helpers", - "../../../../../test/interactions/helpers", - "../../../../../test/shared", - ] -}
diff --git a/test/interactions/panels/recorder/injected/injected_test.json b/test/interactions/panels/recorder/injected/injected_test.json deleted file mode 100644 index 2d0a972..0000000 --- a/test/interactions/panels/recorder/injected/injected_test.json +++ /dev/null
@@ -1,108 +0,0 @@ -{ - "Injected should get selectors for an element - 1": [ - [ - "#buttonNoARIA" - ], - [ - "xpath///*[@id=\"buttonNoARIA\"]" - ], - [ - "pierce/#buttonNoARIA" - ] - ], - "Injected should get selectors for elements with custom selector attributes - 1": [ - [ - [ - "[data-testid='unique']" - ], - [ - "xpath///*[@data-testid=\"unique\"]" - ], - [ - "pierce/[data-testid='unique']" - ] - ], - [ - [ - "[data-testid='\\31 23456789']" - ], - [ - "xpath///*[@data-testid=\"123456789\"]" - ], - [ - "pierce/[data-testid='\\31 23456789']" - ], - [ - "text/Custom selector (invalid" - ] - ], - [ - [ - "[data-qa='custom-id']", - "[data-testid='shadow\\ button']" - ], - [ - "pierce/[data-testid='shadow\\ button']" - ], - [ - "text/Shadow button" - ] - ] - ], - "Injected should get selectors for shadow root elements - 1": [ - [ - "main > shadow-css-selector-element", - "#insideShadowRoot" - ], - [ - "pierce/main > shadow-css-selector-element", - "pierce/#insideShadowRoot" - ] - ], - "Injected should get an ARIA selector for shadow root elements - 1": [ - [ - "aria/[role=\"main\"]", - "aria/login" - ], - [ - "div:nth-of-type(2) > shadow-aria-selector-element", - "button" - ], - [ - "pierce/div:nth-of-type(2) > shadow-aria-selector-element", - "pierce/button" - ] - ], - "Injected should not get an ARIA selector if the target element has no name or role - 1": [ - [ - "#no-aria-name-or-role" - ], - [ - "xpath///*[@id=\"no-aria-name-or-role\"]" - ], - [ - "pierce/#no-aria-name-or-role" - ] - ], - "Injected CSS selectors should return not-optimized CSS selectors for duplicate elements - 1": [ - "div:nth-of-type(3) > div:nth-of-type(2)" - ], - "Injected Text selectors should return a text selector for elements < minimum length - 1": [ - "text/length a 11" - ], - "Injected Text selectors should return a text selector for elements == minimum length - 1": [ - "text/length aa 12" - ], - "Injected Text selectors should return a text selector for elements == maximum length - 1": [ - "text/length aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaaaaaaa aaaa 64" - ], - "Injected Text selectors should return a text selector correctly with same prefix elements - Smaller": [ - "text/length aaaaaaaaa aaaaaaaaa aa 32" - ], - "Injected Text selectors should return a text selector correctly with same prefix elements - Larger": [ - "text/length aaaaaaaaa aaaaaaaaa aaa 33" - ], - "Injected Text selectors should trim text selectors - 1": [ - "text/with newlines" - ] -} \ No newline at end of file
diff --git a/test/interactions/panels/recorder/injected/injected_test.ts b/test/interactions/panels/recorder/injected/injected_test.ts deleted file mode 100644 index 5afaeef..0000000 --- a/test/interactions/panels/recorder/injected/injected_test.ts +++ /dev/null
@@ -1,253 +0,0 @@ -// Copyright 2023 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import {assert} from 'chai'; - -import type {DevToolsRecorder} from '../../../../../front_end/panels/recorder/injected/injected.js'; -import type {Schema} from '../../../../../front_end/panels/recorder/models/models.js'; -import { - loadComponentDocExample, -} from '../../../../../test/interactions/helpers/shared.js'; -import {getBrowserAndPages} from '../../../../../test/shared/helper.js'; -import {assertMatchesJSONSnapshot} from '../../../../../test/shared/snapshots.js'; - -describe('Injected', () => { - beforeEach(async () => { - await loadComponentDocExample('recorder_injected/basic.html'); - - const {frontend} = getBrowserAndPages(); - await frontend.evaluate(() => { - (window.DevToolsRecorder as DevToolsRecorder) - .startRecording( - { - // We don't have the access to the actual bindings here. Therefore, the test assumes - // that the markup is explicitly annotated with the following attributes. - getAccessibleName: (element: Node) => { - if (!('getAttribute' in element)) { - return ''; - } - return (element as Element).getAttribute('aria-name') || ''; - }, - getAccessibleRole: (element: Node) => { - if (!('getAttribute' in element)) { - return 'generic'; - } - return (element as Element).getAttribute('aria-role') || ''; - }, - }, - { - debug: false, - allowUntrustedEvents: true, - selectorTypesToRecord: [ - 'xpath', - 'css', - 'text', - 'aria', - 'pierce', - ] as Schema.SelectorType[], - }, - ); - }); - }); - - afterEach(async () => { - const {frontend} = getBrowserAndPages(); - await frontend.evaluate(() => { - window.DevToolsRecorder.stopRecording(); - }); - }); - - it('should get selectors for an element', async () => { - const {frontend} = getBrowserAndPages(); - const selectors = await frontend.evaluate(() => { - const target = document.querySelector('#buttonNoARIA'); - if (!target) { - throw new Error('#buttonNoARIA not found'); - } - return window.DevToolsRecorder.recordingClientForTesting.getSelectors( - target, - ); - }); - assertMatchesJSONSnapshot(selectors); - }); - - it('should get selectors for elements with custom selector attributes', async () => { - const {frontend} = getBrowserAndPages(); - const selectors = await frontend.evaluate(() => { - const targets = [ - ...document.querySelectorAll('.custom-selector-attribute'), - document.querySelector('#shadow-root-with-custom-selectors')?.shadowRoot?.querySelector('button') as - HTMLButtonElement, - ]; - return targets.map( - window.DevToolsRecorder.recordingClientForTesting.getSelectors, - ); - }); - assertMatchesJSONSnapshot(selectors); - }); - - it('should get selectors for shadow root elements', async () => { - const {frontend} = getBrowserAndPages(); - const selectors = await frontend.evaluate(() => { - const target = document.querySelector('main') - ?.querySelector('shadow-css-selector-element') - ?.shadowRoot?.querySelector('#insideShadowRoot'); - if (!target) { - throw new Error('#insideShadowRoot is not found'); - } - return window.DevToolsRecorder.recordingClientForTesting.getSelectors( - target, - ); - }); - assertMatchesJSONSnapshot(selectors); - }); - - it('should get an ARIA selector for shadow root elements', async () => { - const {frontend} = getBrowserAndPages(); - const selectors = await frontend.evaluate(() => { - const target = document.querySelector('[aria-role="main"]') - ?.querySelector('shadow-aria-selector-element') - ?.shadowRoot?.querySelector('button'); - if (!target) { - throw new Error('button is not found'); - } - return window.DevToolsRecorder.recordingClientForTesting.getSelectors( - target, - ); - }); - assertMatchesJSONSnapshot(selectors); - }); - - it('should not get an ARIA selector if the target element has no name or role', async () => { - const {frontend} = getBrowserAndPages(); - const selectors = await frontend.evaluate(() => { - const target = document.querySelector('#no-aria-name-or-role'); - if (!target) { - throw new Error('button is not found'); - } - return window.DevToolsRecorder.recordingClientForTesting.getSelectors( - target, - ); - }); - assertMatchesJSONSnapshot(selectors); - }); - - describe('CSS selectors', () => { - it('should query CSS selectors', async () => { - const {frontend} = getBrowserAndPages(); - const results = await frontend.evaluate(() => { - return [ - window.DevToolsRecorder.recordingClientForTesting - .queryCSSSelectorAllForTesting( - ['[data-qa=custom-id]', '[data-testid=shadow\\ button]'], - ) - .length, - window.DevToolsRecorder.recordingClientForTesting - .queryCSSSelectorAllForTesting( - ['[data-qa=custom-id]'], - ) - .length, - window.DevToolsRecorder.recordingClientForTesting - .queryCSSSelectorAllForTesting( - '[data-qa=custom-id]', - ) - .length, - window.DevToolsRecorder.recordingClientForTesting - .queryCSSSelectorAllForTesting( - '.doesnotexist', - ) - .length, - window.DevToolsRecorder.recordingClientForTesting - .queryCSSSelectorAllForTesting( - ['[data-qa=custom-id]', '.doesnotexist'], - ) - .length, - window.DevToolsRecorder.recordingClientForTesting - .queryCSSSelectorAllForTesting( - ['#notunique'], - ) - .length, - ]; - }); - assert.deepEqual(results, [1, 1, 1, 0, 0, 2]); - }); - - it('should return not-optimized CSS selectors for duplicate elements', async () => { - const {frontend} = getBrowserAndPages(); - const selector = await frontend.evaluate(() => { - const target = document.querySelector('#notunique'); - if (!target) { - throw new Error('#notunique is not found'); - } - return window.DevToolsRecorder.recordingClientForTesting.getCSSSelector( - target, - ); - }); - assertMatchesJSONSnapshot(selector); - }); - }); - - describe('Text selectors', () => { - const getSelectorOfButtonWithLength = (length: number) => { - const {frontend} = getBrowserAndPages(); - return frontend.evaluate(length => { - const selector = `#buttonWithLength${length}`; - const target = document.querySelector(selector); - if (!target) { - throw new Error(`${selector} could not be found.`); - } - if (target.innerHTML.length !== length) { - throw new Error(`${selector} is not of length ${length}`); - } - return window.DevToolsRecorder.recordingClientForTesting.getTextSelector( - target, - ); - }, length); - }; - const MINIMUM_LENGTH = 12; - const MAXIMUM_LENGTH = 64; - const SAME_PREFIX_TEXT_LENGTH = 32; - - it('should return a text selector for elements < minimum length', async () => { - const selectors = await getSelectorOfButtonWithLength(MINIMUM_LENGTH - 1); - assertMatchesJSONSnapshot(selectors); - }); - it('should return a text selector for elements == minimum length', async () => { - const selectors = await getSelectorOfButtonWithLength(MINIMUM_LENGTH); - assertMatchesJSONSnapshot(selectors); - }); - it('should return a text selector for elements == maximum length', async () => { - const selectors = await getSelectorOfButtonWithLength(MAXIMUM_LENGTH); - assertMatchesJSONSnapshot(selectors); - }); - it('should not return a text selector for elements > maximum length', async () => { - const selectors = await getSelectorOfButtonWithLength(MAXIMUM_LENGTH + 1); - assert.isUndefined(selectors); - }); - it('should return a text selector correctly with same prefix elements', async () => { - let selectors = await getSelectorOfButtonWithLength( - SAME_PREFIX_TEXT_LENGTH, - ); - assertMatchesJSONSnapshot(selectors, {name: 'Smaller'}); - selectors = await getSelectorOfButtonWithLength( - SAME_PREFIX_TEXT_LENGTH + 1, - ); - assertMatchesJSONSnapshot(selectors, {name: 'Larger'}); - }); - it('should trim text selectors', async () => { - const {frontend} = getBrowserAndPages(); - const selectors = await frontend.evaluate(() => { - const selector = '#buttonWithNewLines'; - const target = document.querySelector(selector); - if (!target) { - throw new Error(`${selector} could not be found.`); - } - return window.DevToolsRecorder.recordingClientForTesting.getTextSelector( - target, - ); - }); - assertMatchesJSONSnapshot(selectors); - }); - }); -});