UNPKG

chrome-devtools-frontend

Version:
188 lines (160 loc) • 6.21 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 Protocol from '../../../generated/protocol.js'; import * as IssuesManager from '../../../models/issues_manager/issues_manager.js'; import { renderElementIntoDOM, } from '../../../testing/DOMHelpers.js'; import {describeWithLocale} from '../../../testing/EnvironmentHelpers.js'; import * as IconButton from '../icon_button/icon_button.js'; import * as RenderCoordinator from '../render_coordinator/render_coordinator.js'; import * as IssueCounter from './issue_counter.js'; const renderIssueLinkIcon = async(data: IssueCounter.IssueLinkIcon.IssueLinkIconData): Promise<{ component: IssueCounter.IssueLinkIcon.IssueLinkIcon, shadowRoot: ShadowRoot, }> => { const component = new IssueCounter.IssueLinkIcon.IssueLinkIcon(); component.data = data; renderElementIntoDOM(component); assert.isNotNull(component.shadowRoot); await RenderCoordinator.done(); return {component, shadowRoot: component.shadowRoot}; }; export const extractElements = (shadowRoot: ShadowRoot): { icon: IconButton.Icon.Icon, button: HTMLButtonElement, } => { const icon = shadowRoot.querySelector('devtools-icon'); assert.instanceOf(icon, IconButton.Icon.Icon); const button = shadowRoot.querySelector('button'); assert.instanceOf(button, HTMLButtonElement); return {icon, button}; }; interface MockIssueResolverEntry { resolve: (issue: IssuesManager.Issue.Issue|null) => void; promise: Promise<IssuesManager.Issue.Issue|null>; } class MockIssueResolver { #promiseMap = new Map<string, MockIssueResolverEntry>(); waitFor(issueId?: string) { if (!issueId) { if (this.#promiseMap.size !== 1) { throw new Error('more than one issue being awaited, specify a issue id'); } issueId = this.#promiseMap.keys().next().value; } issueId = issueId || ''; const entry = this.#promiseMap.get(issueId); if (entry) { return entry.promise; } const {resolve, promise} = Promise.withResolvers<IssuesManager.Issue.Issue|null>(); this.#promiseMap.set(issueId, {resolve, promise}); return promise; } resolve(result: IssuesManager.Issue.Issue|null, issueId?: string): void { if (!issueId && this.#promiseMap.size === 1) { issueId = this.#promiseMap.keys().next().value; } issueId = issueId || result?.getIssueId() || ''; const entry = this.#promiseMap.get(issueId); if (!entry) { throw new Error('resolve uninitialized'); } entry.resolve(result); this.#promiseMap.delete(issueId); } } describeWithLocale('IssueLinkIcon', () => { const issueId = 'issue1' as Protocol.Audits.IssueId; const mockIssue = { getKind() { return IssuesManager.Issue.IssueKind.PAGE_ERROR; }, getIssueId() { return issueId; }, getDescription() { return null; }, }; describe('with simple issues', () => { const failingIssueResolver = { async waitFor() { throw new Error('Couldn\'t resolve'); }, }; it('renders correctly without an issue', async () => { const {shadowRoot} = await renderIssueLinkIcon({ issueId, issueResolver: failingIssueResolver as unknown as IssuesManager.IssueResolver.IssueResolver, }); const {icon} = extractElements(shadowRoot); assert.strictEqual(icon.name, 'issue-questionmark-filled'); }); it('renders correctly with a "page error" issue', async () => { const {shadowRoot} = await renderIssueLinkIcon({ issue: mockIssue as unknown as IssuesManager.Issue.Issue, }); const {icon} = extractElements(shadowRoot); assert.strictEqual(icon.name, 'issue-cross-filled'); }); it('the style reacts to the presence of the issue', async () => { const {shadowRoot} = await renderIssueLinkIcon({ issue: mockIssue as unknown as IssuesManager.Issue.Issue, }); const {button} = extractElements(shadowRoot); assert.isTrue(button.classList.contains('link')); }); it('the style reacts to the absence of an issue', async () => { const {shadowRoot} = await renderIssueLinkIcon({ issueId, issueResolver: failingIssueResolver as unknown as IssuesManager.IssueResolver.IssueResolver, }); const {button} = extractElements(shadowRoot); assert.isFalse(button.classList.contains('link')); }); }); describe('transitions upon issue resolution', () => { it('to change the style correctly', async () => { const resolver = new MockIssueResolver(); const {shadowRoot} = await renderIssueLinkIcon({ issueId, issueResolver: resolver as unknown as IssuesManager.IssueResolver.IssueResolver, }); resolver.resolve(mockIssue as unknown as IssuesManager.Issue.Issue); await RenderCoordinator.done({waitForWork: true}); assert.isTrue(extractElements(shadowRoot).button.classList.contains('link')); }); it('handles multiple data assignments', async () => { const {shadowRoot, component} = await renderIssueLinkIcon({ issue: mockIssue as unknown as IssuesManager.Issue.Issue, }); const mockIssue2 = { getKind() { return IssuesManager.Issue.IssueKind.BREAKING_CHANGE; }, }; component.data = { issue: mockIssue2 as unknown as IssuesManager.Issue.Issue, }; await RenderCoordinator.done({waitForWork: true}); const {icon} = extractElements(shadowRoot); assert.strictEqual(icon.name, 'issue-exclamation-filled'); }); }); describe('handles clicks correctly', () => { it('if the button is clicked', async () => { const revealOverride = sinon.fake(Common.Revealer.reveal); const {shadowRoot} = await renderIssueLinkIcon({ issue: mockIssue as unknown as IssuesManager.Issue.Issue, revealOverride, }); const {button} = extractElements(shadowRoot); button.click(); sinon.assert.called(revealOverride); }); }); });