Support e2e tests in Firefox

This commit is contained in:
Felix Becker 2019-06-20 13:51:16 +02:00
parent 38144071d8
commit 69fab829fb
5 changed files with 1830 additions and 183 deletions

View File

@ -1,8 +1,13 @@
import getFreePort from 'get-port'
import { startCase } from 'lodash'
import { appendFile, exists, mkdir, readFile } from 'mz/fs'
import * as path from 'path'
import puppeteer from 'puppeteer'
import puppeteerFirefox from 'puppeteer-firefox'
import webExt from 'web-ext'
import { saveScreenshotsUponFailuresAndClosePage } from '../../../shared/src/util/screenshotReporter'
const chromeExtensionPath = path.resolve(__dirname, '..', '..', 'build/chrome')
const BROWSER = process.env.E2E_BROWSER || 'chrome'
async function getTokenWithSelector(
page: puppeteer.Page,
@ -33,7 +38,29 @@ async function clickElement(page: puppeteer.Page, element: puppeteer.ElementHand
await element.click()
}
describe('Sourcegraph Chrome extension', () => {
// Copied from node_modules/puppeteer-firefox/misc/install-preferences.js
async function getFirefoxCfgPath(): Promise<string> {
const firefoxFolder = path.dirname(puppeteerFirefox.executablePath())
let configPath: string
if (process.platform === 'darwin') {
configPath = path.join(firefoxFolder, '..', 'Resources')
} else if (process.platform === 'linux') {
if (!(await exists(path.join(firefoxFolder, 'browser', 'defaults')))) {
await mkdir(path.join(firefoxFolder, 'browser', 'defaults'))
}
if (!(await exists(path.join(firefoxFolder, 'browser', 'defaults', 'preferences')))) {
await mkdir(path.join(firefoxFolder, 'browser', 'defaults', 'preferences'))
}
configPath = firefoxFolder
} else if (process.platform === 'win32') {
configPath = firefoxFolder
} else {
throw new Error('Unsupported platform: ' + process.platform)
}
return path.join(configPath, 'puppeteer.cfg')
}
describe(`Sourcegraph ${startCase(BROWSER)} extension`, () => {
let browser: puppeteer.Browser
let page: puppeteer.Page
@ -42,21 +69,42 @@ describe('Sourcegraph Chrome extension', () => {
async (): Promise<void> => {
jest.setTimeout(90 * 1000)
let args: string[] = [
`--disable-extensions-except=${chromeExtensionPath}`,
`--load-extension=${chromeExtensionPath}`,
]
if (BROWSER === 'chrome') {
const chromeExtensionPath = path.resolve(__dirname, '..', '..', 'build', 'chrome')
let args: string[] = [
`--disable-extensions-except=${chromeExtensionPath}`,
`--load-extension=${chromeExtensionPath}`,
]
if (process.getuid() === 0) {
// TODO don't run as root in CI
console.warn('Running as root, disabling sandbox')
args = [...args, '--no-sandbox', '--disable-setuid-sandbox']
}
browser = await puppeteer.launch({ args })
} else {
// Make sure CSP is disabled in FF preferences,
// because Puppeteer uses new Function() to evaluate code
// which is not allowed by the github.com CSP.
const cfgPath = await getFirefoxCfgPath()
const disableCspPreference = '\npref("security.csp.enable", false);\n'
if (!(await readFile(cfgPath, 'utf-8')).includes(disableCspPreference)) {
await appendFile(cfgPath, disableCspPreference)
}
if (process.getuid() === 0) {
// TODO don't run as root in CI
console.warn('Running as root, disabling sandbox')
args = [...args, '--no-sandbox', '--disable-setuid-sandbox']
const cdpPort = await getFreePort()
const firefoxExtensionPath = path.resolve(__dirname, '..', '..', 'build', 'firefox')
// webExt.util.logger.consoleStream.makeVerbose()
await webExt.cmd.run(
{
sourceDir: firefoxExtensionPath,
firefox: puppeteerFirefox.executablePath(),
args: [`-juggler=${cdpPort}`, '-headless'],
},
{ shouldExitProgram: false }
)
const browserWSEndpoint = `ws://127.0.0.1:${cdpPort}`
browser = await puppeteerFirefox.connect({ browserWSEndpoint })
}
browser = await puppeteer.launch({
headless: false,
args,
})
}
)
@ -67,8 +115,8 @@ describe('Sourcegraph Chrome extension', () => {
// Take a screenshot when a test fails.
saveScreenshotsUponFailuresAndClosePage(
path.resolve(__dirname, '..', '..', '..', '..'),
path.resolve(__dirname, '..', '..', '..', '..', 'puppeteer'),
path.resolve(__dirname, '..', '..', '..'),
path.resolve(__dirname, '..', '..', '..', 'puppeteer'),
() => page
)
@ -110,11 +158,11 @@ describe('Sourcegraph Chrome extension', () => {
}
for (const diffType of ['unified', 'split']) {
for (const side of ['base', 'head']) {
for (const side of ['base', 'head'] as const) {
test(`provides tooltips for diff files (${diffType}, ${side})`, async () => {
await page.goto(`https://github.com/gorilla/mux/pull/328/files?diff=${diffType}`)
const token = tokens[side as 'base' | 'head']
const token = tokens[side]
const element = await getTokenWithSelector(page, token.text, token.selector)
// Scrolls the element into view so that code view is in view.

View File

@ -0,0 +1,4 @@
declare module 'puppeteer-firefox' {
import puppeteer from 'puppeteer'
export default puppeteer
}

17
browser/src/types/web-ext/index.d.ts vendored Normal file
View File

@ -0,0 +1,17 @@
declare module 'web-ext' {
export namespace cmd {
export interface RunParams {
sourceDir: string
firefox: string
args?: string[]
}
export interface RunOptions {
shouldExitProgram?: boolean
}
/**
* Launches Firefox with an extension loaded.
*/
export function run(params: RunParams, options: RunOptions): Promise<void>
}
}

View File

@ -146,6 +146,7 @@
"eslint-plugin-react-hooks": "^1.6.0",
"execa": "^1.0.0",
"fancy-log": "^1.3.3",
"get-port": "^5.0.0",
"gql2ts": "^1.10.1",
"graphql": "^14.3.1",
"graphql-schema-linter": "^0.2.0",
@ -167,6 +168,7 @@
"postcss-loader": "^3.0.0",
"prettier": "^1.18.2",
"puppeteer": "1.17.0",
"puppeteer-firefox": "^0.5.0",
"react-docgen-typescript-webpack-plugin": "^1.1.0",
"react-hot-loader": "^4.8.4",
"react-test-renderer": "^16.8.6",
@ -187,6 +189,7 @@
"typedoc": "^0.14.2",
"typescript": "^3.5.1",
"utc-version": "^2.0.0",
"web-ext": "npm:@sourcegraph/web-ext@^3.0.0-fork.1",
"webpack": "^4.33.0",
"webpack-dev-server": "^3.7.1",
"worker-loader": "^2.0.0",

1903
yarn.lock

File diff suppressed because it is too large Load Diff