UNPKG

chrome-devtools-frontend

Version:
209 lines (179 loc) • 8.57 kB
// Copyright 2020 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 * as Common from '../../../core/common/common.js'; import type * as Platform from '../../../core/platform/platform.js'; import * as SDK from '../../../core/sdk/sdk.js'; import type * as Protocol from '../../../generated/protocol.js'; import { renderElementIntoDOM, } from '../../../testing/DOMHelpers.js'; import {createTarget} from '../../../testing/EnvironmentHelpers.js'; import {describeWithMockConnection} from '../../../testing/MockConnection.js'; import * as RenderCoordinator from '../../../ui/components/render_coordinator/render_coordinator.js'; import * as ElementsComponents from './components.js'; describeWithMockConnection('LayoutPane', () => { let target: SDK.Target.Target; let domModel: SDK.DOMModel.DOMModel; let overlayModel: SDK.OverlayModel.OverlayModel; let getNodesByStyle: sinon.SinonStub; beforeEach(() => { target = createTarget(); domModel = target.model(SDK.DOMModel.DOMModel) as SDK.DOMModel.DOMModel; assert.exists(domModel); getNodesByStyle = sinon.stub(domModel, 'getNodesByStyle').resolves([]); overlayModel = target.model(SDK.OverlayModel.OverlayModel) as SDK.OverlayModel.OverlayModel; assert.exists(overlayModel); }); async function renderComponent() { const component = new ElementsComponents.LayoutPane.LayoutPane(); renderElementIntoDOM(component); component.wasShown(); await RenderCoordinator.done({waitForWork: true}); return component; } function queryLabels(component: HTMLElement, selector: string) { assert.isNotNull(component.shadowRoot); return Array.from(component.shadowRoot.querySelectorAll(selector)).map(label => { const input = label.querySelector('[data-input]'); assert.instanceOf(input, HTMLElement); return { label: label.getAttribute('title'), input: input.tagName, }; }); } it('renders settings', async () => { Common.Settings.Settings.instance() .moduleSetting('show-grid-line-labels') .setTitle('Enum setting title' as Platform.UIString.LocalizedString); Common.Settings.Settings.instance() .moduleSetting('show-grid-track-sizes') .setTitle('Boolean setting title' as Platform.UIString.LocalizedString); const component = await renderComponent(); assert.deepEqual(queryLabels(component, '[data-enum-setting]'), [{label: 'Enum setting title', input: 'SELECT'}]); assert.deepEqual( queryLabels(component, '[data-boolean-setting]'), [{label: 'Boolean setting title', input: 'INPUT'}, {label: '', input: 'INPUT'}, {label: '', input: 'INPUT'}]); }); it('stores a setting when changed', async () => { const component = await renderComponent(); assert.isNotNull(component.shadowRoot); assert.isTrue(Common.Settings.Settings.instance().moduleSetting('show-grid-track-sizes').get()); const input = component.shadowRoot.querySelector('[data-boolean-setting] [data-input]'); assert.instanceOf(input, HTMLInputElement); input.click(); assert.isFalse(Common.Settings.Settings.instance().moduleSetting('show-grid-track-sizes').get()); }); function makeNode(id: Protocol.DOM.NodeId) { return { id, path: () => 'body > div', ancestorUserAgentShadowRoot: () => false, localName: () => 'div', getAttribute: () => '', scrollIntoView: () => {}, highlight: () => {}, domModel: () => domModel, } as unknown as SDK.DOMModel.DOMNode; } const ID_1 = 1 as Protocol.DOM.NodeId; const ID_2 = 2 as Protocol.DOM.NodeId; const ID_3 = 3 as Protocol.DOM.NodeId; it('renders grid elements', async () => { getNodesByStyle.withArgs([{name: 'display', value: 'grid'}, {name: 'display', value: 'inline-grid'}]).resolves([ ID_1, ID_2, ID_3, ]); sinon.stub(domModel, 'nodeForId') .withArgs(ID_1) .returns(makeNode(ID_1)) .withArgs(ID_2) .returns(makeNode(ID_2)) .withArgs(ID_3) .returns(makeNode(ID_2)); const component = await renderComponent(); assert.isNotNull(component.shadowRoot); assert.lengthOf(queryLabels(component, '[data-element]'), 3); }); it('renders flex elements', async () => { getNodesByStyle.withArgs([{name: 'display', value: 'flex'}, {name: 'display', value: 'inline-flex'}]).resolves([ ID_1, ID_2, ID_3, ]); sinon.stub(domModel, 'nodeForId') .withArgs(ID_1) .returns(makeNode(ID_1)) .withArgs(ID_2) .returns(makeNode(ID_2)) .withArgs(ID_3) .returns(makeNode(ID_3)); const component = await renderComponent(); assert.isNotNull(component.shadowRoot); assert.lengthOf(queryLabels(component, '[data-element]'), 3); }); it('send an event when an element overlay is toggled', async () => { getNodesByStyle.withArgs([{name: 'display', value: 'grid'}, {name: 'display', value: 'inline-grid'}]).resolves([ ID_1, ]); sinon.stub(domModel, 'nodeForId').withArgs(ID_1).returns(makeNode(ID_1)); const highlightGrid = sinon.spy(overlayModel, 'highlightGridInPersistentOverlay'); const component = await renderComponent(); assert.isNotNull(component.shadowRoot); const input = component.shadowRoot.querySelector('[data-element] [data-input]'); assert.instanceOf(input, HTMLInputElement); input.click(); assert.isTrue(highlightGrid.calledOnceWith(ID_1)); }); it('send an event when an element’s Show element button is pressed', async () => { getNodesByStyle.withArgs([{name: 'display', value: 'grid'}, {name: 'display', value: 'inline-grid'}]).resolves([ ID_1, ]); const node = makeNode(ID_1); sinon.stub(domModel, 'nodeForId').withArgs(ID_1).returns(node); const reveal = sinon.stub(Common.Revealer.RevealerRegistry.prototype, 'reveal').resolves(); const component = await renderComponent(); assert.isNotNull(component.shadowRoot); const button = component.shadowRoot.querySelector('.show-element'); assert.instanceOf(button, HTMLElement); button.click(); assert.isTrue(reveal.calledOnceWith(node, false)); }); it('expands/collapses <details> using ArrowLeft/ArrowRight keys', async () => { const component = await renderComponent(); assert.isNotNull(component.shadowRoot); const details = component.shadowRoot.querySelector('details'); assert.instanceOf(details, HTMLDetailsElement); const summary = details.querySelector('summary'); assert.instanceOf(summary, HTMLElement); assert(details.open, 'The first details were not expanded by default'); summary.dispatchEvent(new KeyboardEvent('keydown', {bubbles: true, cancelable: true, key: 'ArrowLeft'})); assert.isNotOk(details.open, 'The details were not collapsed after sending ArrowLeft'); summary.dispatchEvent(new KeyboardEvent('keydown', {bubbles: true, cancelable: true, key: 'ArrowRight'})); assert(details.open, 'The details were not expanded after sending ArrowRight'); }); const updatesUiOnEvent = <T extends keyof SDK.OverlayModel.EventTypes>( event: Platform.TypeScriptUtilities.NoUnion<T>, inScope: boolean) => async () => { SDK.TargetManager.TargetManager.instance().setScopeTarget(inScope ? target : null); const render = sinon.spy(ElementsComponents.LayoutPane.LayoutPane.prototype, 'render'); await renderComponent(); await RenderCoordinator.done(); render.resetHistory(); overlayModel.dispatchEventToListeners( event, ...[{nodeId: 42, enabled: true}] as unknown as Common.EventTarget.EventPayloadToRestParameters<SDK.OverlayModel.EventTypes, T>); await RenderCoordinator.done(); assert.strictEqual(render.called, inScope); }; it('updates UI on in scope grid overlay update event', updatesUiOnEvent(SDK.OverlayModel.Events.PERSISTENT_GRID_OVERLAY_STATE_CHANGED, true)); it('does not update UI on out of scope grid overlay update event', updatesUiOnEvent(SDK.OverlayModel.Events.PERSISTENT_GRID_OVERLAY_STATE_CHANGED, false)); it('updates UI on in scope flex overlay update event', updatesUiOnEvent(SDK.OverlayModel.Events.PERSISTENT_FLEX_CONTAINER_OVERLAY_STATE_CHANGED, true)); it('does not update UI on out of scope flex overlay update event', updatesUiOnEvent(SDK.OverlayModel.Events.PERSISTENT_FLEX_CONTAINER_OVERLAY_STATE_CHANGED, false)); });