UNPKG

chrome-devtools-frontend

Version:
361 lines (308 loc) • 16.2 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 SDK from '../../core/sdk/sdk.js'; import * as Protocol from '../../generated/protocol.js'; import {createFakeSetting, createTarget} from '../../testing/EnvironmentHelpers.js'; import {describeWithMockConnection, dispatchEvent} from '../../testing/MockConnection.js'; import {activate, getMainFrame, navigate} from '../../testing/ResourceTreeHelpers.js'; import { mkInspectorCspIssue, StubIssue, ThirdPartyStubIssue, } from '../../testing/StubIssue.js'; import * as IssuesManager from '../issues_manager/issues_manager.js'; describeWithMockConnection('IssuesManager', () => { let target: SDK.Target.Target; let model: SDK.IssuesModel.IssuesModel; beforeEach(() => { target = createTarget(); const maybeModel = target.model(SDK.IssuesModel.IssuesModel); assert.exists(maybeModel); model = maybeModel; }); it('collects issues from an issues model', () => { const issuesManager = new IssuesManager.IssuesManager.IssuesManager(); const dispatchedIssues: IssuesManager.Issue.Issue[] = []; issuesManager.addEventListener( IssuesManager.IssuesManager.Events.ISSUE_ADDED, event => dispatchedIssues.push(event.data.issue)); model.dispatchEventToListeners( SDK.IssuesModel.Events.ISSUE_ADDED, {issuesModel: model, inspectorIssue: mkInspectorCspIssue('url1')}); model.dispatchEventToListeners( SDK.IssuesModel.Events.ISSUE_ADDED, {issuesModel: model, inspectorIssue: mkInspectorCspIssue('url2')}); const expected = ['ContentSecurityPolicyIssue::kURLViolation', 'ContentSecurityPolicyIssue::kURLViolation']; assert.deepEqual(dispatchedIssues.map(i => i.code()), expected); const issueCodes = Array.from(issuesManager.issues()).map(r => r.code()); assert.deepEqual(issueCodes, expected); }); function getBlockedUrl(issue: IssuesManager.Issue.Issue): string|undefined { const cspIssue = issue as IssuesManager.ContentSecurityPolicyIssue.ContentSecurityPolicyIssue; return cspIssue.details().blockedURL; } function assertOutOfScopeIssuesAreFiltered(): {issuesManager: IssuesManager.IssuesManager.IssuesManager, prerenderTarget: SDK.Target.Target} { const issuesManager = new IssuesManager.IssuesManager.IssuesManager(); const dispatchedIssues: IssuesManager.Issue.Issue[] = []; issuesManager.addEventListener( IssuesManager.IssuesManager.Events.ISSUE_ADDED, event => dispatchedIssues.push(event.data.issue)); model.dispatchEventToListeners( SDK.IssuesModel.Events.ISSUE_ADDED, {issuesModel: model, inspectorIssue: mkInspectorCspIssue('url1')}); const prerenderTarget = createTarget({subtype: 'prerender'}); const prerenderModel = prerenderTarget.model(SDK.IssuesModel.IssuesModel); assert.exists(prerenderModel); prerenderModel.dispatchEventToListeners( SDK.IssuesModel.Events.ISSUE_ADDED, {issuesModel: prerenderModel, inspectorIssue: mkInspectorCspIssue('url2')}); const expected = ['url1']; assert.deepEqual(dispatchedIssues.map(getBlockedUrl), expected); assert.deepEqual(Array.from(issuesManager.issues()).map(getBlockedUrl), expected); return {issuesManager, prerenderTarget}; } it('updates filtered issues when switching scope', () => { const {issuesManager, prerenderTarget} = assertOutOfScopeIssuesAreFiltered(); SDK.TargetManager.TargetManager.instance().setScopeTarget(prerenderTarget); assert.deepEqual(Array.from(issuesManager.issues()).map(getBlockedUrl), ['url2']); }); it('keeps issues of prerendered page upon activation', () => { const {issuesManager, prerenderTarget} = assertOutOfScopeIssuesAreFiltered(); SDK.TargetManager.TargetManager.instance().setScopeTarget(prerenderTarget); activate(prerenderTarget); assert.deepEqual(Array.from(issuesManager.issues()).map(getBlockedUrl), ['url2']); }); const updatesOnPrimaryPageChange = (primary: boolean) => () => { const issuesManager = new IssuesManager.IssuesManager.IssuesManager(); model.dispatchEventToListeners( SDK.IssuesModel.Events.ISSUE_ADDED, {issuesModel: model, inspectorIssue: mkInspectorCspIssue('url1')}); assert.strictEqual(issuesManager.numberOfIssues(), 1); navigate(getMainFrame(primary ? target : createTarget({subtype: 'prerender'}))); assert.strictEqual(issuesManager.numberOfIssues(), primary ? 0 : 1); }; it('clears issues after primary page navigation', updatesOnPrimaryPageChange(true)); it('does not clear issues after non-primary page navigation', updatesOnPrimaryPageChange(false)); it('filters third-party issues when the third-party issues setting is false, includes them otherwise', () => { const issues = [ new ThirdPartyStubIssue('AllowedStubIssue1', false), new ThirdPartyStubIssue('StubIssue2', true), new ThirdPartyStubIssue('AllowedStubIssue3', false), new ThirdPartyStubIssue('StubIssue4', true), ]; const showThirdPartyIssuesSetting = createFakeSetting('third party flag', false); const issuesManager = new IssuesManager.IssuesManager.IssuesManager(showThirdPartyIssuesSetting); const firedIssueAddedEventCodes: string[] = []; issuesManager.addEventListener( IssuesManager.IssuesManager.Events.ISSUE_ADDED, event => firedIssueAddedEventCodes.push(event.data.issue.code())); for (const issue of issues) { issuesManager.addIssue(model, issue); } let issueCodes = Array.from(issuesManager.issues()).map(i => i.code()); assert.deepEqual(issueCodes, ['AllowedStubIssue1', 'AllowedStubIssue3']); assert.deepEqual(firedIssueAddedEventCodes, ['AllowedStubIssue1', 'AllowedStubIssue3']); showThirdPartyIssuesSetting.set(true); issueCodes = Array.from(issuesManager.issues()).map(i => i.code()); assert.deepEqual(issueCodes, ['AllowedStubIssue1', 'StubIssue2', 'AllowedStubIssue3', 'StubIssue4']); }); it('reports issue counts by kind', () => { const issue1 = new StubIssue('StubIssue1', ['id1'], [], IssuesManager.Issue.IssueKind.IMPROVEMENT); const issue2 = new StubIssue('StubIssue1', ['id2'], [], IssuesManager.Issue.IssueKind.IMPROVEMENT); const issue3 = new StubIssue('StubIssue1', ['id3'], [], IssuesManager.Issue.IssueKind.BREAKING_CHANGE); const issuesManager = new IssuesManager.IssuesManager.IssuesManager(); issuesManager.addIssue(model, issue1); issuesManager.addIssue(model, issue2); issuesManager.addIssue(model, issue3); assert.deepEqual(issuesManager.numberOfIssues(), 3); assert.deepEqual(issuesManager.numberOfIssues(IssuesManager.Issue.IssueKind.IMPROVEMENT), 2); assert.deepEqual(issuesManager.numberOfIssues(IssuesManager.Issue.IssueKind.BREAKING_CHANGE), 1); assert.deepEqual(issuesManager.numberOfIssues(IssuesManager.Issue.IssueKind.PAGE_ERROR), 0); }); describe('instance', () => { it('throws an Error if its not the first instance created with "ensureFirst" set', () => { IssuesManager.IssuesManager.IssuesManager.instance(); assert.throws(() => IssuesManager.IssuesManager.IssuesManager.instance({forceNew: true, ensureFirst: true})); assert.throws(() => IssuesManager.IssuesManager.IssuesManager.instance({forceNew: false, ensureFirst: true})); }); }); it('hides issues added after setting has been initialised', () => { const issues = [ new StubIssue('HiddenStubIssue1', [], []), new StubIssue('HiddenStubIssue2', [], []), new StubIssue('UnhiddenStubIssue1', [], []), new StubIssue('UnhiddenStubIssue2', [], []), ]; const hideIssueByCodeSetting = createFakeSetting('hide by code', ({} as IssuesManager.IssuesManager.HideIssueMenuSetting)); const showThirdPartyIssuesSetting = createFakeSetting('third party flag', true); const issuesManager = new IssuesManager.IssuesManager.IssuesManager(showThirdPartyIssuesSetting, hideIssueByCodeSetting); const hiddenIssues: string[] = []; issuesManager.addEventListener(IssuesManager.IssuesManager.Events.ISSUE_ADDED, event => { if (event.data.issue.isHidden()) { hiddenIssues.push(event.data.issue.code()); } }); // This Setting can either have been initialised in a previous Devtools session and retained // through to a new session. // OR // These settings have been updated by clicking on "hide issue" and cause the updateHiddenIssues // method to be called. These issues are being added to the IssuesManager after this has happened. hideIssueByCodeSetting.set({ HiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, HiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, }); for (const issue of issues) { issuesManager.addIssue(model, issue); } assert.deepEqual(hiddenIssues, ['HiddenStubIssue1', 'HiddenStubIssue2']); }); it('hides issues present in IssuesManager when setting is updated', () => { const issues = [ new StubIssue('HiddenStubIssue1', [], []), new StubIssue('HiddenStubIssue2', [], []), new StubIssue('UnhiddenStubIssue1', [], []), new StubIssue('UnhiddenStubIssue2', [], []), ]; const hideIssueByCodeSetting = createFakeSetting('hide by code', ({} as IssuesManager.IssuesManager.HideIssueMenuSetting)); const showThirdPartyIssuesSetting = createFakeSetting('third party flag', true); const issuesManager = new IssuesManager.IssuesManager.IssuesManager(showThirdPartyIssuesSetting, hideIssueByCodeSetting); let hiddenIssues: string[] = []; issuesManager.addEventListener(IssuesManager.IssuesManager.Events.FULL_UPDATE_REQUIRED, () => { hiddenIssues = []; for (const issue of issuesManager.issues()) { if (issue.isHidden()) { hiddenIssues.push(issue.code()); } } }); for (const issue of issues) { issuesManager.addIssue(model, issue); } // Setting is updated by clicking on "hide issue". hideIssueByCodeSetting.set({ HiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, }); assert.deepEqual(hiddenIssues, ['HiddenStubIssue1']); hideIssueByCodeSetting.set({ HiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, HiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, }); assert.deepEqual(hiddenIssues, ['HiddenStubIssue1', 'HiddenStubIssue2']); }); it('unhides issues present in IssuesManager when setting is updated', () => { const issues = [ new StubIssue('HiddenStubIssue1', [], []), new StubIssue('HiddenStubIssue2', [], []), new StubIssue('UnhiddenStubIssue1', [], []), new StubIssue('UnhiddenStubIssue2', [], []), ]; const hideIssueByCodeSetting = createFakeSetting('hide by code', ({} as IssuesManager.IssuesManager.HideIssueMenuSetting)); const showThirdPartyIssuesSetting = createFakeSetting('third party flag', true); const issuesManager = new IssuesManager.IssuesManager.IssuesManager(showThirdPartyIssuesSetting, hideIssueByCodeSetting); for (const issue of issues) { issuesManager.addIssue(model, issue); } hideIssueByCodeSetting.set({ HiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, HiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, UnhiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, UnhiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, }); let unhiddenIssues: string[] = []; issuesManager.addEventListener(IssuesManager.IssuesManager.Events.FULL_UPDATE_REQUIRED, () => { unhiddenIssues = []; for (const issue of issuesManager.issues()) { if (!issue.isHidden()) { unhiddenIssues.push(issue.code()); } } }); // Setting updated by clicking on "unhide issue" hideIssueByCodeSetting.set({ HiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, HiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, UnhiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.UNHIDDEN, UnhiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, }); assert.deepEqual(unhiddenIssues, ['UnhiddenStubIssue1']); hideIssueByCodeSetting.set({ HiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, HiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, UnhiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.UNHIDDEN, UnhiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.UNHIDDEN, }); assert.deepEqual(unhiddenIssues, ['UnhiddenStubIssue1', 'UnhiddenStubIssue2']); }); it('unhides all issues correctly', () => { const issues = [ new StubIssue('HiddenStubIssue1', [], []), new StubIssue('HiddenStubIssue2', [], []), new StubIssue('UnhiddenStubIssue1', [], []), new StubIssue('UnhiddenStubIssue2', [], []), ]; const hideIssueByCodeSetting = createFakeSetting('hide by code', ({} as IssuesManager.IssuesManager.HideIssueMenuSetting)); const showThirdPartyIssuesSetting = createFakeSetting('third party flag', true); const issuesManager = new IssuesManager.IssuesManager.IssuesManager(showThirdPartyIssuesSetting, hideIssueByCodeSetting); for (const issue of issues) { issuesManager.addIssue(model, issue); } hideIssueByCodeSetting.set({ HiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, HiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, UnhiddenStubIssue1: IssuesManager.IssuesManager.IssueStatus.HIDDEN, UnhiddenStubIssue2: IssuesManager.IssuesManager.IssueStatus.HIDDEN, }); let unhiddenIssues: string[] = []; issuesManager.addEventListener(IssuesManager.IssuesManager.Events.FULL_UPDATE_REQUIRED, () => { unhiddenIssues = []; for (const issue of issuesManager.issues()) { if (!issue.isHidden()) { unhiddenIssues.push(issue.code()); } } }); issuesManager.unhideAllIssues(); assert.deepEqual( unhiddenIssues, ['HiddenStubIssue1', 'HiddenStubIssue2', 'UnhiddenStubIssue1', 'UnhiddenStubIssue2']); }); it('send update event on scope change', async () => { const issuesManager = new IssuesManager.IssuesManager.IssuesManager(); const updateRequired = issuesManager.once(IssuesManager.IssuesManager.Events.FULL_UPDATE_REQUIRED); const anotherTarget = createTarget(); SDK.TargetManager.TargetManager.instance().setScopeTarget(anotherTarget); await updateRequired; }); it('clears BounceTrackingIssue only on user-initiated navigation', () => { const issuesManager = new IssuesManager.IssuesManager.IssuesManager(); const issue = { code: Protocol.Audits.InspectorIssueCode.BounceTrackingIssue, details: { bounceTrackingIssueDetails: { trackingSites: ['example_1.test'], }, }, }; model.dispatchEventToListeners(SDK.IssuesModel.Events.ISSUE_ADDED, {issuesModel: model, inspectorIssue: issue}); assert.strictEqual(issuesManager.numberOfIssues(), 1); dispatchEvent(target, 'Network.requestWillBeSent', { requestId: 'requestId1', loaderId: 'loaderId1', request: {url: 'http://example.com'}, hasUserGesture: false, } as unknown as Protocol.Network.RequestWillBeSentEvent); const frame = getMainFrame(target); navigate(frame, {loaderId: 'loaderId1' as Protocol.Network.LoaderId}); assert.strictEqual(issuesManager.numberOfIssues(), 1); dispatchEvent(target, 'Network.requestWillBeSent', { requestId: 'requestId2', loaderId: 'loaderId2', request: {url: 'http://example.com/page'}, hasUserGesture: true, } as unknown as Protocol.Network.RequestWillBeSentEvent); navigate(frame, {loaderId: 'loaderId2' as Protocol.Network.LoaderId}); assert.strictEqual(issuesManager.numberOfIssues(), 0); }); });