From 5bc2814f37a1a554b998f079006772af4fb8d3ad Mon Sep 17 00:00:00 2001 From: Sebastian Morales Date: Fri, 1 Nov 2019 13:49:11 -0700 Subject: [PATCH 1/5] change permissions to use activeTab --- src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index a5e684832b..9f984b1053 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -23,7 +23,7 @@ }, "devtools_page": "Devtools/devtools.html", "web_accessible_resources": ["insights.html", "assessments/*", "injected/*", "background/*", "common/*", "DetailsView/*", "bundle/*"], - "permissions": ["storage", "webNavigation", "tabs", "notifications", "https://*/*", "http://*/*", "file://*/*"], + "permissions": ["storage", "webNavigation", "tabs", "notifications", "activeTab"], "commands": { "_execute_browser_action": { "suggested_key": { From 5f1e21d9f390b4ecbedc256d4c353e635feff112 Mon Sep 17 00:00:00 2001 From: Sebastian Morales Date: Mon, 4 Nov 2019 15:23:07 -0800 Subject: [PATCH 2/5] injecting permissions for e2e test to be able to inject js and css into the target page --- .../end-to-end/common/browser-factory.ts | 51 +++++++-- src/tests/end-to-end/common/browser.ts | 10 +- .../tests/details-view/headings.test.ts | 4 +- .../__snapshots__/adhoc-panel.test.ts.snap | 8 +- .../tests/popup/adhoc-panel.test.ts | 104 +++++++++++------- 5 files changed, 116 insertions(+), 61 deletions(-) diff --git a/src/tests/end-to-end/common/browser-factory.ts b/src/tests/end-to-end/common/browser-factory.ts index 3afeb912bd..145f5e045c 100644 --- a/src/tests/end-to-end/common/browser-factory.ts +++ b/src/tests/end-to-end/common/browser-factory.ts @@ -1,32 +1,54 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +import { generateUID } from 'common/uid-generator'; import * as fs from 'fs'; import * as path from 'path'; import * as Puppeteer from 'puppeteer'; import * as util from 'util'; -import { generateUID } from '../../../common/uid-generator'; import { Browser } from './browser'; import { popupPageElementIdentifiers } from './element-identifiers/popup-page-element-identifiers'; import { DEFAULT_BROWSER_LAUNCH_TIMEOUT_MS } from './timeouts'; export const chromeLogsPath = path.join(__dirname, '../../../../test-results/e2e/chrome-logs/'); -export function browserLogPath(browserInstanceId: string): string { - return path.join(chromeLogsPath, browserInstanceId); -} +export const browserLogPath = (browserInstanceId: string): string => path.join(chromeLogsPath, browserInstanceId); + +const fileExists = util.promisify(fs.exists); +const writeFile = util.promisify(fs.writeFile); +const readFile = util.promisify(fs.readFile); export interface ExtensionOptions { suppressFirstTimeDialog: boolean; + addLocalhostToPermissions?: boolean; } export async function launchBrowser(extensionOptions: ExtensionOptions): Promise { const browserInstanceId = generateUID(); - const puppeteerBrowser = await launchNewBrowser(browserInstanceId); - const browser = new Browser(browserInstanceId, puppeteerBrowser); + + // only unpacked extension paths are supported + const devExtensionPath = `${(global as any).rootDir}/drop/extension/dev/product`; + const manifestPath = getManifestPath(devExtensionPath); + + let onClose: () => Promise; + + if (extensionOptions.addLocalhostToPermissions) { + const originalManifest = await readFile(manifestPath); + + const permissiveManifest = addLocalhostPermissionsToManifest(originalManifest.toString()); + + await writeFile(manifestPath, permissiveManifest); + + onClose = async () => await writeFile(manifestPath, originalManifest.toString()); + } + + const puppeteerBrowser = await launchNewBrowser(browserInstanceId, devExtensionPath); + + const browser = new Browser(browserInstanceId, puppeteerBrowser, onClose); if (extensionOptions.suppressFirstTimeDialog) { await suppressFirstTimeUsagePrompt(browser); } + return browser; } @@ -40,12 +62,12 @@ async function suppressFirstTimeUsagePrompt(browser: Browser): Promise { await popupPage.close(); } -function fileExists(filePath: string): Promise { - return new Promise(resolve => fs.exists(filePath, resolve)); +function getManifestPath(extensionPath: string): string { + return `${extensionPath}/manifest.json`; } async function verifyExtensionIsBuilt(extensionPath: string): Promise { - const manifestPath = `${extensionPath}/manifest.json`; + const manifestPath = getManifestPath(extensionPath); if (!(await fileExists(manifestPath))) { throw new Error( `Cannot launch extension-enabled browser instance because extension has not been built.\n` + @@ -55,10 +77,15 @@ async function verifyExtensionIsBuilt(extensionPath: string): Promise { } } -async function launchNewBrowser(browserInstanceId: string): Promise { - // only unpacked extension paths are supported - const extensionPath = `${(global as any).rootDir}/drop/extension/dev/product`; +function addLocalhostPermissionsToManifest(originalManifest: string): string { + const manifest = JSON.parse(originalManifest); + + manifest['permissions'].push('http://localhost:9050/*'); + + return JSON.stringify(manifest, null, 2); +} +async function launchNewBrowser(browserInstanceId: string, extensionPath: string): Promise { // It's important that we verify this before calling Puppeteer.launch because its behavior if the // extension can't be loaded is "the Chromium instance hangs with an alert and everything on Puppeteer's // end shows up as a generic timeout error with no meaningful logging". diff --git a/src/tests/end-to-end/common/browser.ts b/src/tests/end-to-end/common/browser.ts index 52026fb666..ff5daa3449 100644 --- a/src/tests/end-to-end/common/browser.ts +++ b/src/tests/end-to-end/common/browser.ts @@ -14,11 +14,19 @@ export class Browser { private memoizedBackgroundPage: BackgroundPage; private pages: Array = []; - constructor(private readonly browserInstanceId: string, private readonly underlyingBrowser: Puppeteer.Browser) { + constructor( + private readonly browserInstanceId: string, + private readonly underlyingBrowser: Puppeteer.Browser, + private readonly onClose?: () => Promise, + ) { underlyingBrowser.on('disconnected', onBrowserDisconnected); } public async close(): Promise { + if (this.onClose) { + await this.onClose(); + } + this.underlyingBrowser.removeListener('disconnected', onBrowserDisconnected); await this.underlyingBrowser.close(); } diff --git a/src/tests/end-to-end/tests/details-view/headings.test.ts b/src/tests/end-to-end/tests/details-view/headings.test.ts index a2044042bc..eafa53385d 100644 --- a/src/tests/end-to-end/tests/details-view/headings.test.ts +++ b/src/tests/end-to-end/tests/details-view/headings.test.ts @@ -15,7 +15,7 @@ describe('Headings Page', () => { let headingsPage: DetailsViewPage; beforeAll(async () => { - browser = await launchBrowser({ suppressFirstTimeDialog: true }); + browser = await launchBrowser({ suppressFirstTimeDialog: true, addLocalhostToPermissions: true }); targetPage = await browser.newTargetPage(); headingsPage = await openHeadingsPage(browser, targetPage); }); @@ -39,7 +39,7 @@ describe('Headings Page', () => { let headingsPage: DetailsViewPage; beforeAll(async () => { - browser = await launchBrowser({ suppressFirstTimeDialog: true }); + browser = await launchBrowser({ suppressFirstTimeDialog: true, addLocalhostToPermissions: true }); targetPage = await browser.newTargetPage(); headingsPage = await openHeadingsPage(browser, targetPage); await headingsPage.enableHighContrast(); diff --git a/src/tests/end-to-end/tests/popup/__snapshots__/adhoc-panel.test.ts.snap b/src/tests/end-to-end/tests/popup/__snapshots__/adhoc-panel.test.ts.snap index 2c7eb1bf9c..f779b33b81 100644 --- a/src/tests/end-to-end/tests/popup/__snapshots__/adhoc-panel.test.ts.snap +++ b/src/tests/end-to-end/tests/popup/__snapshots__/adhoc-panel.test.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Ad hoc tools should display the pinned target page visualizations when enabling the "Automated checks" toggle 1`] = ` +exports[`Ad hoc tools ad hoc toggles should display the pinned target page visualizations when enabling the "Automated checks" toggle 1`] = `
`; -exports[`Ad hoc tools should display the pinned target page visualizations when enabling the "Color" toggle 1`] = ` +exports[`Ad hoc tools ad hoc toggles should display the pinned target page visualizations when enabling the "Color" toggle 1`] = `
`; -exports[`Ad hoc tools should display the pinned target page visualizations when enabling the "Headings" toggle 1`] = ` +exports[`Ad hoc tools ad hoc toggles should display the pinned target page visualizations when enabling the "Headings" toggle 1`] = `
`; -exports[`Ad hoc tools should display the pinned target page visualizations when enabling the "Landmarks" toggle 1`] = ` +exports[`Ad hoc tools ad hoc toggles should display the pinned target page visualizations when enabling the "Landmarks" toggle 1`] = `
{ let targetPage: TargetPage; let popupPage: PopupPage; - beforeEach(async () => { - browser = await launchBrowser({ suppressFirstTimeDialog: true }); - targetPage = await browser.newTargetPage(); - popupPage = await browser.newPopupPage(targetPage); - await popupPage.bringToFront(); - }); - afterEach(async () => { if (browser) { await browser.close(); @@ -26,53 +19,80 @@ describe('Ad hoc tools', () => { } }); - it('should have launchpad link that takes us to adhoc panel & is sticky', async () => { - await popupPage.gotoAdhocPanel(); - - // verify adhoc panel state is sticky - targetPage = await browser.newTargetPage(); - popupPage = await browser.newPopupPage(targetPage); - await popupPage.verifyAdhocPanelLoaded(); - }); + describe('navigation', () => { + beforeEach(async () => { + browser = await launchBrowser({ suppressFirstTimeDialog: true }); + targetPage = await browser.newTargetPage(); + popupPage = await browser.newPopupPage(targetPage); + await popupPage.bringToFront(); + }); - it('should take back to Launch pad on clicking "Back to Launch pad" link & is sticky', async () => { - await popupPage.clickSelectorXPath(popupPageElementIdentifiers.adhocLaunchPadLinkXPath); - await popupPage.clickSelector(popupPageElementIdentifiers.backToLaunchPadLink); + it('should have launchpad link that takes us to adhoc panel & is sticky', async () => { + await popupPage.gotoAdhocPanel(); - await popupPage.verifyLaunchPadLoaded(); + // verify ad hoc panel state is sticky + targetPage = await browser.newTargetPage(); + popupPage = await browser.newPopupPage(targetPage); + await popupPage.verifyAdhocPanelLoaded(); + }); - // verify adhoc panel state is sticky - targetPage = await browser.newTargetPage(); - popupPage = await browser.newPopupPage(targetPage); - await popupPage.verifyLaunchPadLoaded(); - }); + it('should take back to Launch pad on clicking "Back to Launch pad" link & is sticky', async () => { + await popupPage.clickSelectorXPath(popupPageElementIdentifiers.adhocLaunchPadLinkXPath); + await popupPage.clickSelector(popupPageElementIdentifiers.backToLaunchPadLink); - it('should pass accessibility validation', async () => { - await popupPage.gotoAdhocPanel(); + await popupPage.verifyLaunchPadLoaded(); - const results = await scanForAccessibilityIssues(popupPage, '*'); - expect(results).toHaveLength(0); + // verify ad hoc panel state is sticky + targetPage = await browser.newTargetPage(); + popupPage = await browser.newPopupPage(targetPage); + await popupPage.verifyLaunchPadLoaded(); + }); }); - it('should pass accessibility validation in high contrast', async () => { - const detailsViewPage = await browser.newDetailsViewPage(targetPage); - await detailsViewPage.enableHighContrast(); + describe('ad hoc toggles', () => { + beforeEach(async () => { + browser = await launchBrowser({ suppressFirstTimeDialog: true, addLocalhostToPermissions: true }); + targetPage = await browser.newTargetPage(); + popupPage = await browser.newPopupPage(targetPage); + await popupPage.bringToFront(); + }); - await popupPage.bringToFront(); - await popupPage.gotoAdhocPanel(); + it.each(['Automated checks', 'Landmarks', 'Headings', 'Color'])( + 'should display the pinned target page visualizations when enabling the "%s" toggle', + async (toggleAriaLabel: string) => { + await popupPage.gotoAdhocPanel(); - const results = await scanForAccessibilityIssues(popupPage, '*'); - expect(results).toHaveLength(0); + await popupPage.enableToggleByAriaLabel(toggleAriaLabel); + + expect(await targetPage.getShadowRootHtmlSnapshot()).toMatchSnapshot(); + }, + ); }); - it.each(['Automated checks', 'Landmarks', 'Headings', 'Color'])( - 'should display the pinned target page visualizations when enabling the "%s" toggle', - async (toggleAriaLabel: string) => { + describe('a11y scan', () => { + beforeEach(async () => { + browser = await launchBrowser({ suppressFirstTimeDialog: true, addLocalhostToPermissions: true }); + targetPage = await browser.newTargetPage(); + popupPage = await browser.newPopupPage(targetPage); + await popupPage.bringToFront(); + }); + + it('should pass accessibility validation', async () => { await popupPage.gotoAdhocPanel(); - await popupPage.enableToggleByAriaLabel(toggleAriaLabel); + const results = await scanForAccessibilityIssues(popupPage, '*'); + expect(results).toHaveLength(0); + }); + + it('should pass accessibility validation in high contrast', async () => { + const detailsViewPage = await browser.newDetailsViewPage(targetPage); + await detailsViewPage.enableHighContrast(); - expect(await targetPage.getShadowRootHtmlSnapshot()).toMatchSnapshot(); - }, - ); + await popupPage.bringToFront(); + await popupPage.gotoAdhocPanel(); + + const results = await scanForAccessibilityIssues(popupPage, '*'); + expect(results).toHaveLength(0); + }); + }); }); From 6128525a0c2cd88042ac7a962a48dfc8bb192282 Mon Sep 17 00:00:00 2001 From: Sebastian Morales Date: Mon, 4 Nov 2019 16:02:19 -0800 Subject: [PATCH 3/5] fix tabstop test --- src/tests/end-to-end/tests/target-page/tabstop.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/end-to-end/tests/target-page/tabstop.test.ts b/src/tests/end-to-end/tests/target-page/tabstop.test.ts index 29bfb7e1ac..965ec48d8e 100644 --- a/src/tests/end-to-end/tests/target-page/tabstop.test.ts +++ b/src/tests/end-to-end/tests/target-page/tabstop.test.ts @@ -14,7 +14,7 @@ describe('tabstop tests', () => { let popupPage: PopupPage; beforeEach(async () => { - browser = await launchBrowser({ suppressFirstTimeDialog: true }); + browser = await launchBrowser({ suppressFirstTimeDialog: true, addLocalhostToPermissions: true }); targetPage = await browser.newTargetPage({ testResourcePath: 'native-widgets/input-type-radio.html' }); }); From bfc1360ef42678e4cceb21f125093c9a30ae8ea3 Mon Sep 17 00:00:00 2001 From: Sebastian Morales Date: Mon, 4 Nov 2019 16:35:21 -0800 Subject: [PATCH 4/5] fix visualization-bixes test --- .../end-to-end/tests/target-page/visualization-boxes.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/end-to-end/tests/target-page/visualization-boxes.test.ts b/src/tests/end-to-end/tests/target-page/visualization-boxes.test.ts index abe1ce8463..ba76749340 100644 --- a/src/tests/end-to-end/tests/target-page/visualization-boxes.test.ts +++ b/src/tests/end-to-end/tests/target-page/visualization-boxes.test.ts @@ -13,7 +13,7 @@ describe('Target Page visualization boxes', () => { let popupPage: PopupPage; beforeEach(async () => { - browser = await launchBrowser({ suppressFirstTimeDialog: true }); + browser = await launchBrowser({ suppressFirstTimeDialog: true, addLocalhostToPermissions: true }); targetPage = await browser.newTargetPage(); popupPage = await browser.newPopupPage(targetPage); await popupPage.gotoAdhocPanel(); From bf1ce682257046936ad454592efe3313550739f6 Mon Sep 17 00:00:00 2001 From: Sebastian Morales Date: Mon, 4 Nov 2019 16:58:37 -0800 Subject: [PATCH 5/5] fix issue-dialog test --- src/tests/end-to-end/tests/target-page/issue-dialog.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/end-to-end/tests/target-page/issue-dialog.test.ts b/src/tests/end-to-end/tests/target-page/issue-dialog.test.ts index 826bf37805..f59f20e85f 100644 --- a/src/tests/end-to-end/tests/target-page/issue-dialog.test.ts +++ b/src/tests/end-to-end/tests/target-page/issue-dialog.test.ts @@ -13,7 +13,7 @@ describe('Target Page issue dialog', () => { let popupPage: PopupPage; beforeAll(async () => { - browser = await launchBrowser({ suppressFirstTimeDialog: true }); + browser = await launchBrowser({ suppressFirstTimeDialog: true, addLocalhostToPermissions: true }); targetPage = await browser.newTargetPage(); popupPage = await browser.newPopupPage(targetPage); });