blob: b88c5a97f46bbb71142ca589e697f4567fe9f520 [file]
// Copyright 2026 The Chromium Authors
// 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 {findSubMenuEntryItem, waitForSoftContextMenu} from '../helpers/context-menu-helpers.js';
import {expandSelectedNodeRecursively} from '../helpers/elements-helpers.js';
interface TextEditorElement extends Element {
editor: {
state: {
doc: {length: number},
selection: {main: {from: number, to: number}},
},
viewport: {from: number, to: number},
dispatch(spec: {
changes?: {from: number, insert: string},
selection?: {anchor: number},
scrollIntoView?: boolean,
}): void,
};
}
describe('The Elements tab', function() {
// Opening two context menus and the editor in sequence can exceed the
// default budget on a busy bot.
if (this.timeout() !== 0) {
this.timeout(20_000);
}
it('selects the whole document from the Edit as HTML context menu', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/switch-panels-while-editing-as-html.html');
await expandSelectedNodeRecursively(devToolsPage);
const elementsContentPanel = await devToolsPage.waitFor('#elements-content');
const selectedNode = await devToolsPage.waitForElementWithTextContent('Inspected Node', elementsContentPanel);
await selectedNode.click({button: 'right'});
const editAsHTMLOption = await findSubMenuEntryItem('Edit as HTML', devToolsPage);
await editAsHTMLOption.click();
const editor = await devToolsPage.waitFor('.elements-disclosure devtools-text-editor');
await devToolsPage.waitForNone('.soft-context-menu');
// Grow the document past the editor's height cap and scroll back to the
// top, so the lines at the bottom fall outside the render viewport.
await editor.evaluate(element => {
const view = (element as TextEditorElement).editor;
const lines = Array.from({length: 500}, (_, i) => `<div>line ${i}</div>`).join('\n');
view.dispatch({
changes: {from: view.state.doc.length, insert: '\n' + lines},
selection: {anchor: 0},
scrollIntoView: true,
});
});
await devToolsPage.waitForFunction(() => editor.evaluate(element => {
const view = (element as TextEditorElement).editor;
return view.viewport.to < view.state.doc.length;
}),
undefined, 'Waiting for the editor content to be virtualized');
// Right-click the first rendered line rather than .cm-content, whose
// center sits below the editor's clipped scroller by now.
await devToolsPage.click('.cm-line', {root: editor, clickOptions: {button: 'right'}});
const menu = await waitForSoftContextMenu(devToolsPage);
await devToolsPage.click('[aria-label="Select all"]', {root: menu});
await devToolsPage.waitForNone('.soft-context-menu');
const selection = await editor.evaluate(element => {
const {state} = (element as TextEditorElement).editor;
return {from: state.selection.main.from, to: state.selection.main.to, length: state.doc.length};
});
assert.isAbove(selection.length, 0, 'editor should contain the node markup');
assert.strictEqual(selection.from, 0, 'selection should start at the beginning of the document');
assert.strictEqual(selection.to, selection.length, 'selection should reach the end of the document');
const editorContent = await devToolsPage.$('.cm-content', editor);
assert.isNotNull(editorContent, 'editor should remain open');
});
});