Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CI/test set up #9499

Merged
merged 15 commits into from Nov 25, 2019
6 changes: 4 additions & 2 deletions .circleci/config.yml
Expand Up @@ -38,9 +38,11 @@ commands:
command: yarn install --frozen-lockfile --check-files
- run:
name: Install correct Chrome Driver version
command: yarn add chromedriver@76 -W && git checkout yarn.lock package.json
command: node node_modules/chromedriver/install.js
environment:
CHROMEDRIVER_VERSION: '76.0.3809.68'
- run: google-chrome --version
- run: chromedriver --version
- run: yarn chromedriver --version
yarn_lint:
steps:
- run:
Expand Down
4 changes: 2 additions & 2 deletions azure-pipelines.yml
Expand Up @@ -39,9 +39,9 @@ steps:
displayName: 'Install dependencies'

- script: |
yarn add chromedriver@76 -W
node node_modules/chromedriver/install.js
displayName: 'Install correct Chrome Driver version'

- script: |
node run-tests.js -c 2 -g $(group)
node run-tests.js -g $(group)
displayName: 'Run tests'
4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -8,8 +8,8 @@
"lerna": "lerna",
"dev": "lerna run build --stream --parallel",
"dev2": "while true; do yarn --check-files && yarn dev; done",
"testonly": "jest",
"testall": "yarn run testonly -- --ci --reporters=default --reporters=jest-junit --forceExit --runInBand",
"testonly": "jest --runInBand",
"testall": "yarn run testonly -- --ci --reporters=default --reporters=jest-junit --forceExit",
"pretest": "yarn run lint",
"git-reset": "git reset --hard HEAD",
"git-clean": "git clean -d -x -e node_modules -e packages -f",
Expand Down
Expand Up @@ -49,6 +49,7 @@ describe('Empty Project', () => {

it('should show empty object warning during client transition', async () => {
const browser = await webdriver(appPort, '/static')
await waitFor(1000)
await browser.eval(`(function() {
window.gotWarn = false
const origWarn = console.warn
Expand All @@ -60,7 +61,7 @@ describe('Empty Project', () => {
}
window.next.router.replace('/another')
})()`)
await waitFor(300)
await waitFor(1000)
const gotWarn = await browser.eval(`window.gotWarn`)
expect(gotWarn).toBe(true)
await browser.close()
Expand Down
4 changes: 3 additions & 1 deletion test/integration/future/test/index.test.js
Expand Up @@ -36,7 +36,9 @@ describe('future.excludeDefaultMomentLocales', () => {
const browser = await webdriver(appPort, '/')
await waitFor(5000)
expect(await browser.elementByCss('h1').text()).toMatch(/current time/i)
expect(await browser.eval('moment.locales()')).toStrictEqual(['en'])
const locales = await browser.eval('moment.locales()')
expect(locales).toEqual(['en'])
expect(locales.length).toBe(1)
await browser.close()
})
})
86 changes: 61 additions & 25 deletions test/jest-environment.js
Expand Up @@ -4,6 +4,8 @@ const os = require('os')
const http = require('http')
const fetch = require('node-fetch')
const getPort = require('get-port')
const waitPort = require('wait-port')
const chromedriver = require('chromedriver')
const NodeEnvironment = require('jest-environment-node')
const {
BROWSER_NAME,
Expand All @@ -16,8 +18,14 @@ let browser
let initialWindow
let browserOptions = {
browserName: BROWSER_NAME || 'chrome',
...(process.env.HEADLESS === 'true'
? {
chromeOptions: { args: ['--headless'] },
}
: {}),
}
let deviceIP = 'localhost'
let driverPort = '9515'

const isIE = BROWSER_NAME === 'ie'
const isSafari = BROWSER_NAME === 'safari'
Expand Down Expand Up @@ -75,38 +83,52 @@ const newTabPg = `
class CustomEnvironment extends NodeEnvironment {
async createBrowser() {
// always create new browser session if not BrowserStack
if (!browser && isBrowserStack) {
if (!browser) {
if (!isBrowserStack) {
driverPort = await getPort()
chromedriver.start([`--port=${driverPort}`])

// https://github.com/giggio/node-chromedriver/issues/117
await waitPort({
port: driverPort,
timeout: 1000 * 60 * 2, // 2 Minutes
})
}

browser = wd.promiseChainRemote(
'hub-cloud.browserstack.com', // seleniumHost
80, // seleniumPort
BROWSERSTACK_USERNAME,
BROWSERSTACK_ACCESS_KEY
...(isBrowserStack
? [
'hub-cloud.browserstack.com', // seleniumHost
80, // seleniumPort
BROWSERSTACK_USERNAME,
BROWSERSTACK_ACCESS_KEY,
]
: [`http://localhost:${driverPort}`])
)

// Setup the browser instance
await browser.init(browserOptions)
initialWindow = await browser.windowHandle()
global.bsBrowser = browser
}

if (isBrowserStack) {
// disable browser.close and we handle it manually
browser.origClose = browser.close
browser.close = () => {}
// Since ie11 doesn't like dataURIs we have to spin up a
// server to handle the new tab page
this.server = http.createServer((req, res) => {
res.statusCode = 200
res.end(newTabPg)
})
this.newTabPort = await getPort()
await new Promise((resolve, reject) => {
this.server.listen(this.newTabPort, err => {
if (err) return reject(err)
resolve()
})
// disable browser.close and we handle it manually
browser.origClose = browser.close
browser.close = () => {}
// Since ie11 doesn't like dataURIs we have to spin up a
// server to handle the new tab page
this.server = http.createServer((req, res) => {
res.statusCode = 200
res.end(newTabPg)
})
this.newTabPort = await getPort()
await new Promise((resolve, reject) => {
this.server.listen(this.newTabPort, err => {
if (err) return reject(err)
resolve()
})
})

if (isBrowserStack) {
const networkIntfs = os.networkInterfaces()
// find deviceIP to use with BrowserStack
for (const intf of Object.keys(networkIntfs)) {
Expand All @@ -124,13 +146,14 @@ class CustomEnvironment extends NodeEnvironment {
}
}
}

this.global.browserName = browserOptions.browserName
this.global.isBrowserStack = isBrowserStack

// Mock current browser set up
this.global.bsWd = async (appPort, pathname) => {
this.global.sharedWD = async (appPort, pathname) => {
const url = `http://${deviceIP}:${appPort}${pathname}`
console.log(`\n> Loading browser with ${url}\n`)
if (isBrowserStack) await this.freshWindow()
await this.freshWindow()

return new Promise((resolve, reject) => {
let timedOut = false
Expand Down Expand Up @@ -199,6 +222,19 @@ class CustomEnvironment extends NodeEnvironment {
async teardown() {
await super.teardown()
if (this.server) this.server.close()
if (browser) {
// Close all remaining browser windows
try {
const windows = await browser.windowHandles()
for (const window of windows) {
if (!window) continue
await browser.window(window)
await browser.origClose()
await browser.quit()
}
} catch (_) {}
}
chromedriver.stop()
}
}

Expand Down
17 changes: 2 additions & 15 deletions test/jest-global-setup.js
@@ -1,4 +1,4 @@
let globalSetup
let globalSetup = () => {}

if (process.env.BROWSERSTACK) {
const { Local } = require('browserstack-local')
Expand All @@ -18,19 +18,6 @@ if (process.env.BROWSERSTACK) {
})
})
}
} else {
const chromedriver = require('chromedriver')
const waitPort = require('wait-port')

globalSetup = async function globalSetup() {
chromedriver.start()

// https://github.com/giggio/node-chromedriver/issues/117
await waitPort({
port: 9515,
timeout: 1000 * 60 * 2, // 2 Minutes
})
}
}

module.exports = () => globalSetup()
module.exports = globalSetup
17 changes: 1 addition & 16 deletions test/jest-global-teardown.js
@@ -1,24 +1,9 @@
let globalTeardown = () => {}
const browser = global.bsBrowser

if (process.env.BROWSERSTACK) {
globalTeardown = () => global.browserStackLocal.killAllProcesses(() => {})
}

module.exports = async () => {
if (browser) {
// Close all remaining browser windows
try {
const windows = await browser.windowHandles()
for (const window of windows) {
if (!window) continue
await browser.window(window)
await browser.origClose()
await browser.quit()
await globalTeardown()
}
} catch (_) {}
} else {
await globalTeardown()
}
await globalTeardown()
}
98 changes: 1 addition & 97 deletions test/lib/next-webdriver.js
@@ -1,97 +1 @@
import wd from 'wd'
import getPort from 'get-port'
import waitPort from 'wait-port'

const doHeadless = process.env.HEADLESS !== 'false'
let driverPort = 9515

let webdriver = async function(appPort, pathname) {
if (typeof appPort === 'undefined') {
throw new Error('appPort is undefined')
}

const url = `http://localhost:${appPort}${pathname}`
console.log(`> Start loading browser with url: ${url}`)

// Sometimes browser won't initialize due to some random issues.
// So, we need to timeout the initialization and retry again.
for (let lc = 0; lc < 5; lc++) {
try {
const browser = await getBrowser(url, 5000)
console.log(`> Complete loading browser with url: ${url}`)
return browser
} catch (ex) {
console.warn(`> Error when loading browser with url: ${url}`)

// Try restarting chromedriver max twice
if (lc < 2) {
const chromedriver = require('chromedriver')
console.log('Trying to restart chromedriver with random port')
driverPort = await getPort()
chromedriver.stop()
chromedriver.start([`--port=${driverPort}`])
// https://github.com/giggio/node-chromedriver/issues/117
await waitPort({
port: driverPort,
timeout: 1000 * 30, // 30 seconds
})
continue
}

if (ex.message === 'TIMEOUT') continue
throw ex
}
}

console.error(`> Tried 5 times. Cannot load the browser for url: ${url}`)
throw new Error(`Couldn't start the browser for url: ${url}`)
}

function getBrowser(url, timeout) {
const browser = wd.promiseChainRemote(`http://localhost:${driverPort}/`)

return new Promise((resolve, reject) => {
let timeouted = false
const timeoutHandler = setTimeout(() => {
timeouted = true
const error = new Error('TIMEOUT')
reject(error)
}, timeout)

browser
.init({
browserName: 'chrome',
...(doHeadless
? {
chromeOptions: { args: ['--headless'] },
}
: {}),
})
.get(url, err => {
if (timeouted) {
try {
browser.close(() => {
// Ignore errors
})
} catch (err) {
// Ignore
}
return
}

clearTimeout(timeoutHandler)

if (err) {
reject(err)
return
}

resolve(browser)
})
})
}

if (global.isBrowserStack) {
webdriver = (...args) => global.bsWd(...args)
}
export default webdriver
export default global.sharedWD