blob: 31d4135476973261cb7515a484baaba323d6c58b [file] [edit]
// Copyright 2020 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 {
ACTIVE_STARTING_STYLE_ADORNER_SELECTOR,
editCSSProperty,
expandSelectedNodeRecursively,
focusElementsTree,
getComputedStylesForDomNode,
INACTIVE_GRID_ADORNER_SELECTOR,
INACTIVE_STARTING_STYLE_ADORNER_SELECTOR,
toggleAdornerSetting,
waitForAdornerOnSelectedNode,
waitForAdorners,
waitForAndClickTreeElementWithPartialText,
waitForElementsStyleSection,
waitForNoAdornersOnSelectedNode,
waitForPartialContentOfSelectedElementsNode,
waitForSpecificAdornerOnSelectedNode,
} from '../helpers/elements-helpers.js';
import type {DevToolsPage} from '../shared/frontend-helper.js';
const prepareElementsTab = async (devToolsPage: DevToolsPage) => {
await waitForElementsStyleSection(devToolsPage, null);
await expandSelectedNodeRecursively(devToolsPage);
};
describe('Adornment in the Elements Tab', function() {
// This test relies on the context menu which takes a while to appear, so we bump the timeout a bit.
if (this.timeout() > 0) {
this.timeout(20000);
}
it('displays a starting-style adorner for elements with starting styles', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-starting-style.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'starting-style', isActive: false},
{textContent: 'starting-style', isActive: false},
]);
});
it('displays a starting-style adorner for selected elements with starting styles',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-starting-style.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'no-starting-style');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'with-inner-starting-style');
await waitForAdornerOnSelectedNode(devToolsPage, 'starting-style');
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'with-outer-starting-style');
await waitForAdornerOnSelectedNode(devToolsPage, 'starting-style');
});
it('enforces starting styles when clicking the adorner', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-starting-style.html');
await prepareElementsTab(devToolsPage);
await devToolsPage.click(INACTIVE_STARTING_STYLE_ADORNER_SELECTOR);
await waitForAdorners(devToolsPage,
[
{textContent: 'view-source', isActive: false},
{textContent: 'starting-style', isActive: true},
{textContent: 'starting-style', isActive: false},
],
ACTIVE_STARTING_STYLE_ADORNER_SELECTOR);
const backgroundColorComputedStyle =
await getComputedStylesForDomNode(inspectedPage, '.with-inner-starting-style', 'backgroundColor');
assert.strictEqual(backgroundColorComputedStyle, 'rgb(0, 128, 0)');
});
it('displays grid and flex adorners', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'grid', isActive: false},
{textContent: 'subgrid', isActive: false},
{textContent: 'subgrid', isActive: false},
{textContent: 'subgrid', isActive: false},
{textContent: 'grid', isActive: false},
{textContent: 'flex', isActive: false},
{textContent: 'flex', isActive: false},
]);
});
it('displays grid-lanes adorners', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-grid-lanes.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'grid-lanes', isActive: false},
{textContent: 'grid-lanes', isActive: false},
]);
});
it('displays scroll-snap adorners', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll-snap.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'scroll-snap', isActive: false},
{textContent: 'scroll', isActive: false},
]);
});
it('displays media adorner for video and audio elements', async ({devToolsPage, inspectedPage}) => {
// Note that this test simulates several property value editing, with delay between each keystrokes.
// If this test become flaky in the future, it is likely that we will have to increase the timeout.
await inspectedPage.goToResource('elements/adornment-media.html');
await toggleAdornerSetting(devToolsPage, 'media');
await focusElementsTree(devToolsPage);
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'media', isActive: false},
{textContent: 'media', isActive: false},
]);
// Select the first video element.
await devToolsPage.pressKey('ArrowDown');
await waitForAdornerOnSelectedNode(devToolsPage, 'media');
// Select the second audio element.
await devToolsPage.pressKey('ArrowDown');
await waitForAdornerOnSelectedNode(devToolsPage, 'media');
});
it('opens sources panel with main document when view-source adorner is clicked',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-view-source.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
]);
await devToolsPage.click('devtools-adorner');
await devToolsPage.waitFor('div[aria-label="Sources panel"]');
await devToolsPage.waitFor('[aria-label="adornment-view-source.html"][aria-selected="true"]');
});
it('displays custom-element adorner and opens definition in sources panel when clicked',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-custom-element.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'custom-element', isActive: false},
{textContent: 'custom-element', isActive: false},
]);
const customElementAdorner = await devToolsPage.waitFor('devtools-adorner.custom-element');
await customElementAdorner.click();
await devToolsPage.waitFor('div[aria-label="Sources panel"]');
await devToolsPage.waitFor('[aria-label="adornment-custom-element.html"][aria-selected="true"]');
});
it('displays container query adorners', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-container-query.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'inline-size', isActive: false},
]);
});
it('does not display container adorner for normal container type', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-container-query.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'container-normal');
await waitForNoAdornersOnSelectedNode(devToolsPage);
});
it('can toggle adorners', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'grid', isActive: false},
{textContent: 'subgrid', isActive: false},
{textContent: 'subgrid', isActive: false},
{textContent: 'subgrid', isActive: false},
{textContent: 'grid', isActive: false},
{textContent: 'flex', isActive: false},
{textContent: 'flex', isActive: false},
]);
// Toggle both grid adorners on and try to select them with the active selector
await devToolsPage.click(INACTIVE_GRID_ADORNER_SELECTOR);
await devToolsPage.click(INACTIVE_GRID_ADORNER_SELECTOR);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'grid', isActive: true},
{textContent: 'subgrid', isActive: true},
{textContent: 'subgrid', isActive: false},
{textContent: 'subgrid', isActive: false},
{textContent: 'grid', isActive: false},
{textContent: 'flex', isActive: false},
{textContent: 'flex', isActive: false},
]);
});
it('does not display adorners on shadow roots when their parents are grid or flex containers',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-shadow.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'grid', isActive: false},
{textContent: 'flex', isActive: false},
]);
});
it('updates when display properties change', async ({devToolsPage, inspectedPage}) => {
// Note that this test simulates several property value editing, like a user would type, with delay between
// keystrokes. So if this test became flaky in the future, we'd likely have to increase the timeout.
await inspectedPage.goToResource('elements/adornment.html');
await prepareElementsTab(devToolsPage);
// Select the first element.
await devToolsPage.pressKey('ArrowDown');
await waitForAdornerOnSelectedNode(devToolsPage, 'grid');
await editCSSProperty(devToolsPage, '.grid', 'display', 'flex');
await waitForAdornerOnSelectedNode(devToolsPage, 'flex');
await editCSSProperty(devToolsPage, '.grid', 'display', 'inline-grid');
await waitForAdornerOnSelectedNode(devToolsPage, 'grid');
});
it('displays scroll adorner for an element with overflow:scroll and scrollable contents',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'scroller');
await waitForAdornerOnSelectedNode(devToolsPage, 'scroll');
});
it('displays scroll adorner for an element with `overflow: hidden` changed to `overflow: scroll`',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'overflow-hidden');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await editCSSProperty(devToolsPage, '#overflow-hidden', 'overflow', 'scroll');
await waitForAdornerOnSelectedNode(devToolsPage, 'scroll');
});
it('displays scroll adorner for an element with `overflow: visible` changed to `overflow: scroll`',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'overflow-visible');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await editCSSProperty(devToolsPage, '#overflow-visible', 'overflow', 'scroll');
await waitForAdornerOnSelectedNode(devToolsPage, 'scroll');
});
it('removes scroll adorner for an element with `overflow: scroll` changed to `overflow: visible`',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'overflow-scroll');
await waitForAdornerOnSelectedNode(devToolsPage, 'scroll');
await editCSSProperty(devToolsPage, '#overflow-scroll', 'overflow', 'visible');
await waitForNoAdornersOnSelectedNode(devToolsPage);
});
it('removes scroll adorner for an element whose content shrinks', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'content-shrinking');
await waitForAdornerOnSelectedNode(devToolsPage, 'scroll');
await inspectedPage.evaluate(() => {
document.getElementById('content-shrinking')?.classList.add('shrunk');
});
await waitForNoAdornersOnSelectedNode(devToolsPage);
});
it('displays scroll adorner for document node in an iframe', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'iframe');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, '"iframe"');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, 'document');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, '<html>');
await waitForSpecificAdornerOnSelectedNode(devToolsPage, 'devtools-adorner.scroll');
await devToolsPage.pressKey('ArrowDown');
await devToolsPage.pressKey('ArrowLeft');
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, '<body>');
await waitForNoAdornersOnSelectedNode(devToolsPage);
});
it('displays scroll adorner for the body node in an iframe', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'iframe-with-scrollable-body');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, '"iframe-with-scrollable-body"');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, 'document');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, 'DOCTYPE');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, '<html>');
await waitForAdornerOnSelectedNode(devToolsPage, 'view-source');
await devToolsPage.pressKey('ArrowDown');
await devToolsPage.pressKey('ArrowLeft');
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, '<body>');
await waitForAdornerOnSelectedNode(devToolsPage, 'scroll');
});
it('removes scroll adorner for an document element whose body shrinks', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-scroll.html');
await prepareElementsTab(devToolsPage);
await waitForAndClickTreeElementWithPartialText(devToolsPage, 'iframe-with-shrinking-body');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, '"iframe-with-shrinking-body"');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, 'document');
await waitForNoAdornersOnSelectedNode(devToolsPage);
await devToolsPage.pressKey('ArrowDown');
await waitForPartialContentOfSelectedElementsNode(devToolsPage, 'body-shrinking');
await waitForSpecificAdornerOnSelectedNode(devToolsPage, 'devtools-adorner.scroll');
await inspectedPage.evaluate(() => {
const frame = document.getElementById('iframe-with-shrinking-body') as HTMLIFrameElement;
const doc = frame.contentDocument;
if (doc) {
doc.getElementById('body-shrinking')?.classList.add('shrunk');
}
});
await waitForAdornerOnSelectedNode(devToolsPage, 'view-source');
});
it('displays popover adorners', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-popover.html');
await prepareElementsTab(devToolsPage);
await waitForAdorners(devToolsPage, [
{textContent: 'view-source', isActive: false},
{textContent: 'popover', isActive: false},
{textContent: 'popover', isActive: false},
]);
});
it('can toggle popover adorner', async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-popover.html');
await prepareElementsTab(devToolsPage);
const activePopoverSelector = '[aria-label="Stop keeping this popover open"]';
await waitForAdorners(devToolsPage,
[
{textContent: 'view-source', isActive: false},
{textContent: 'popover', isActive: false},
{textContent: 'popover', isActive: false},
],
activePopoverSelector);
let adorners = await devToolsPage.$$('[aria-label="Keep this popover open"]');
await adorners[0].click();
await waitForAdorners(devToolsPage,
[
{textContent: 'view-source', isActive: false},
{textContent: 'popover', isActive: true},
{textContent: 'top-layer (1)', isActive: false},
{textContent: 'popover', isActive: false},
{textContent: 'reveal', isActive: false},
{textContent: 'reveal', isActive: false},
],
activePopoverSelector);
adorners = await devToolsPage.$$('[aria-label="Stop keeping this popover open"]');
await adorners[0].click();
await waitForAdorners(devToolsPage,
[
{textContent: 'view-source', isActive: false},
{textContent: 'popover', isActive: false},
{textContent: 'popover', isActive: false},
],
activePopoverSelector);
});
it('popover adorner does not toggled off when another popover is force-opened',
async ({devToolsPage, inspectedPage}) => {
await inspectedPage.goToResource('elements/adornment-popover.html');
await prepareElementsTab(devToolsPage);
const activePopoverSelector = '[aria-label="Stop keeping this popover open"]';
await waitForAdorners(devToolsPage,
[
{textContent: 'view-source', isActive: false},
{textContent: 'popover', isActive: false},
{textContent: 'popover', isActive: false},
],
activePopoverSelector);
let adorners = await devToolsPage.$$('[aria-label="Keep this popover open"]');
await adorners[0].click();
await waitForAdorners(devToolsPage,
[
{textContent: 'view-source', isActive: false},
{textContent: 'popover', isActive: true},
{textContent: 'top-layer (1)', isActive: false},
{textContent: 'popover', isActive: false},
{textContent: 'reveal', isActive: false},
{textContent: 'reveal', isActive: false},
],
activePopoverSelector);
adorners = await devToolsPage.$$('[aria-label="Keep this popover open"]');
await adorners[0].click();
await waitForAdorners(devToolsPage,
[
{textContent: 'view-source', isActive: false},
{textContent: 'popover', isActive: true},
{textContent: 'top-layer (1)', isActive: false},
{textContent: 'popover', isActive: true},
{textContent: 'top-layer (2)', isActive: false},
{textContent: 'reveal', isActive: false},
{textContent: 'reveal', isActive: false},
{textContent: 'reveal', isActive: false},
{textContent: 'reveal', isActive: false},
],
activePopoverSelector);
});
});