Update tests for BrowserStack (#6810)
Update tests to setup webdriver stuff in `jest-environment` and re-use one browser session instead of spawning one for each webdriver call to prevent creating too many BrowserStack sessions.
This commit is contained in:
parent
9c2f690c0a
commit
533018f7d0
32 changed files with 542 additions and 243 deletions
|
@ -31,6 +31,16 @@ jobs:
|
|||
JEST_JUNIT_CLASSNAME: '{filepath}'
|
||||
- store_test_results:
|
||||
path: ~/repo/reports
|
||||
test-production:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
working_directory: ~/repo
|
||||
steps:
|
||||
- attach_workspace:
|
||||
at: .
|
||||
- run:
|
||||
name: Production Tests
|
||||
command: '[[ ! -z $BROWSERSTACK_USERNAME ]] && yarn testall test/integration/production/ || echo "Not running for PR"'
|
||||
deploy:
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
|
@ -55,6 +65,9 @@ workflows:
|
|||
- test:
|
||||
requires:
|
||||
- build
|
||||
- test-production:
|
||||
requires:
|
||||
- build
|
||||
- deploy:
|
||||
requires:
|
||||
- test
|
||||
|
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -18,7 +18,7 @@ pids
|
|||
coverage
|
||||
|
||||
# test output
|
||||
test/**/out
|
||||
test/**/out*
|
||||
.DS_Store
|
||||
|
||||
# Editors
|
||||
|
@ -26,3 +26,4 @@ test/**/out
|
|||
|
||||
# example output
|
||||
examples/**/out
|
||||
|
||||
|
|
|
@ -4,10 +4,10 @@ module.exports = {
|
|||
testMatch: ['**/*.test.js'],
|
||||
verbose: true,
|
||||
bail: true,
|
||||
testEnvironment: 'node',
|
||||
rootDir: 'test',
|
||||
modulePaths: ['<rootDir>/lib'],
|
||||
globalSetup: '<rootDir>/jest-global-setup.js',
|
||||
globalTeardown: '<rootDir>/jest-global-teardown.js',
|
||||
testEnvironment: '<rootDir>/jest-environment.js',
|
||||
coverageReporters: ['text', 'lcov', 'cobertura']
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-eslint": "9.0.0",
|
||||
"babel-jest": "23.6.0",
|
||||
"browserstack-local": "1.3.7",
|
||||
"cheerio": "0.22.0",
|
||||
"chromedriver": "2.46.0",
|
||||
"clone": "2.1.1",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
/* global jasmine, webdriver */
|
||||
import { join } from 'path'
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import {
|
||||
|
@ -14,7 +14,6 @@ import {
|
|||
launchApp,
|
||||
killApp
|
||||
} from 'next-test-utils'
|
||||
import webdriver from 'next-webdriver'
|
||||
import cheerio from 'cheerio'
|
||||
import amphtmlValidator from 'amphtml-validator'
|
||||
const appDir = join(__dirname, '../')
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global jasmine, webdriver */
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-env jest */
|
||||
/* global webdriver */
|
||||
|
||||
import webdriver from 'next-webdriver'
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { check } from 'next-test-utils'
|
||||
|
|
|
@ -1,20 +1,23 @@
|
|||
/* eslint-env jest */
|
||||
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
|
||||
export default (context, render) => {
|
||||
describe('With CSP enabled', () => {
|
||||
it('should load inline script by hash', async () => {
|
||||
const browser = await webdriver(context.appPort, '/?withCSP=hash')
|
||||
const errLog = await browser.log('browser')
|
||||
expect(errLog.filter((e) => e.source === 'security')).toEqual([])
|
||||
if (browser.log) {
|
||||
const errLog = await browser.log('browser')
|
||||
expect(errLog.filter((e) => e.source === 'security')).toEqual([])
|
||||
}
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should load inline script by nonce', async () => {
|
||||
const browser = await webdriver(context.appPort, '/?withCSP=nonce')
|
||||
const errLog = await browser.log('browser')
|
||||
expect(errLog.filter((e) => e.source === 'security')).toEqual([])
|
||||
if (browser.log) {
|
||||
const errLog = await browser.log('browser')
|
||||
expect(errLog.filter((e) => e.source === 'security')).toEqual([])
|
||||
}
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
import cheerio from 'cheerio'
|
||||
import { waitFor, check } from 'next-test-utils'
|
||||
|
||||
|
@ -45,11 +45,13 @@ export default (context, render) => {
|
|||
await check(() => browser.elementByCss('body').text(), /Nested 2/)
|
||||
await check(() => browser.elementByCss('body').text(), /Browser hydrated/)
|
||||
|
||||
const logs = await browser.log('browser')
|
||||
if (browser.log) {
|
||||
const logs = await browser.log('browser')
|
||||
|
||||
logs.forEach(logItem => {
|
||||
expect(logItem.message).not.toMatch(/Expected server HTML to contain/)
|
||||
})
|
||||
logs.forEach(logItem => {
|
||||
expect(logItem.message).not.toMatch(/Expected server HTML to contain/)
|
||||
})
|
||||
}
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
import { join } from 'path'
|
||||
import { check, File, waitFor, getReactErrorOverlayContent, getBrowserBodyText } from 'next-test-utils'
|
||||
|
||||
|
@ -40,9 +40,8 @@ export default (context, renderViaHTTP) => {
|
|||
it('should have installed the react-overlay-editor editor handler', async () => {
|
||||
let browser
|
||||
const aboutPage = new File(join(__dirname, '../', 'pages', 'hmr', 'about.js'))
|
||||
aboutPage.replace('</div>', 'div')
|
||||
|
||||
try {
|
||||
aboutPage.replace('</div>', 'div')
|
||||
browser = await webdriver(context.appPort, '/hmr/about')
|
||||
|
||||
// react-error-overlay uses the following inline style if an editorHandler is installed
|
||||
|
@ -76,8 +75,10 @@ export default (context, renderViaHTTP) => {
|
|||
const aboutPage = new File(join(__dirname, '../', 'pages', 'hmr', 'about.js'))
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/hmr/about')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
expect(text).toBe('This is the about page.')
|
||||
await check(
|
||||
() => getBrowserBodyText(browser),
|
||||
/This is the about page/
|
||||
)
|
||||
|
||||
aboutPage.replace('</div>', 'div')
|
||||
|
||||
|
@ -147,9 +148,10 @@ export default (context, renderViaHTTP) => {
|
|||
const aboutPage = new File(join(__dirname, '../', 'pages', 'hmr', 'about.js'))
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/hmr/about')
|
||||
const text = await browser
|
||||
.elementByCss('p').text()
|
||||
expect(text).toBe('This is the about page.')
|
||||
await check(
|
||||
() => getBrowserBodyText(browser),
|
||||
/This is the about page/
|
||||
)
|
||||
|
||||
aboutPage.replace('export', 'aa=20;\nexport')
|
||||
|
||||
|
@ -174,9 +176,10 @@ export default (context, renderViaHTTP) => {
|
|||
const aboutPage = new File(join(__dirname, '../', 'pages', 'hmr', 'about.js'))
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/hmr/about')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
await check(
|
||||
() => getBrowserBodyText(browser),
|
||||
/This is the about page/
|
||||
)
|
||||
|
||||
aboutPage.replace('return', 'throw new Error("an-expected-error");\nreturn')
|
||||
|
||||
|
@ -210,8 +213,10 @@ export default (context, renderViaHTTP) => {
|
|||
const aboutPage = new File(join(__dirname, '../', 'pages', 'hmr', 'about.js'))
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/hmr/about')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
expect(text).toBe('This is the about page.')
|
||||
await check(
|
||||
() => getBrowserBodyText(browser),
|
||||
/This is the about page/
|
||||
)
|
||||
|
||||
aboutPage.replace('export default', 'export default {};\nexport const fn =')
|
||||
|
||||
|
@ -249,8 +254,10 @@ export default (context, renderViaHTTP) => {
|
|||
const aboutPage = new File(join(__dirname, '../', 'pages', 'hmr', 'about.js'))
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/hmr/about')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
expect(text).toBe('This is the about page.')
|
||||
await check(
|
||||
() => getBrowserBodyText(browser),
|
||||
/This is the about page/
|
||||
)
|
||||
|
||||
aboutPage.replace('export default', 'export default () => /search/;\nexport const fn =')
|
||||
|
||||
|
@ -288,8 +295,10 @@ export default (context, renderViaHTTP) => {
|
|||
const aboutPage = new File(join(__dirname, '../', 'pages', 'hmr', 'about.js'))
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/hmr/about')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
expect(text).toBe('This is the about page.')
|
||||
await check(
|
||||
() => getBrowserBodyText(browser),
|
||||
/This is the about page/
|
||||
)
|
||||
|
||||
aboutPage.replace('export default', 'export default undefined;\nexport const fn =')
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
import { readFileSync, writeFileSync, renameSync, existsSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { waitFor, check, getBrowserBodyText } from 'next-test-utils'
|
||||
|
@ -135,15 +135,15 @@ export default (context, renderViaHTTP) => {
|
|||
// Change the page
|
||||
writeFileSync(pagePath, editedContent, 'utf8')
|
||||
|
||||
// wait for 5 seconds
|
||||
await waitFor(5000)
|
||||
|
||||
try {
|
||||
// Check whether the this page has reloaded or not.
|
||||
const editedPTag = await browser.elementByCss('.hmr-style-page p')
|
||||
const editedFontSize = await editedPTag.getComputedCss('font-size')
|
||||
|
||||
expect(editedFontSize).toBe('200px')
|
||||
await check(
|
||||
async () => {
|
||||
const editedPTag = await browser.elementByCss('.hmr-style-page p')
|
||||
return editedPTag.getComputedCss('font-size')
|
||||
},
|
||||
/200px/
|
||||
)
|
||||
} finally {
|
||||
// Finally is used so that we revert the content back to the original regardless of the test outcome
|
||||
// restore the about page content.
|
||||
|
@ -173,14 +173,14 @@ export default (context, renderViaHTTP) => {
|
|||
// Change the page
|
||||
writeFileSync(pagePath, editedContent, 'utf8')
|
||||
|
||||
// wait for 5 seconds
|
||||
await waitFor(5000)
|
||||
|
||||
// Check whether the this page has reloaded or not.
|
||||
const editedPTag = await browser.elementByCss('.hmr-style-page p')
|
||||
const editedFontSize = await editedPTag.getComputedCss('font-size')
|
||||
|
||||
expect(editedFontSize).toBe('200px')
|
||||
await check(
|
||||
async () => {
|
||||
const editedPTag = await browser.elementByCss('.hmr-style-page p')
|
||||
return editedPTag.getComputedCss('font-size')
|
||||
},
|
||||
/200px/
|
||||
)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
|
||||
export default (context) => {
|
||||
describe('process.env', () => {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
|
||||
export default (context) => {
|
||||
describe('Client Navigation 404', () => {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
/* global jasmine, webdriver */
|
||||
import { join } from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
import renderingSuite from './rendering'
|
||||
import {
|
||||
waitFor,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-env jest */
|
||||
/* global webdriver */
|
||||
|
||||
import webdriver from 'next-webdriver'
|
||||
import { waitFor } from 'next-test-utils' /* check, File */
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
/* global jasmine, webdriver */
|
||||
import { join } from 'path'
|
||||
import getPort from 'get-port'
|
||||
import clone from 'clone'
|
||||
import webdriver from 'next-webdriver'
|
||||
import {
|
||||
initNextServerScript,
|
||||
killApp,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
import { check, getBrowserBodyText } from 'next-test-utils'
|
||||
|
||||
export default function (context) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
import { renderViaHTTP, getBrowserBodyText, check } from 'next-test-utils'
|
||||
import cheerio from 'cheerio'
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
/* global jasmine, webdriver */
|
||||
import { join, resolve } from 'path'
|
||||
import { existsSync } from 'fs'
|
||||
import webdriver from 'next-webdriver'
|
||||
import AbortController from 'abort-controller'
|
||||
import {
|
||||
renderViaHTTP,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
import { readFileSync, writeFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import { waitFor } from 'next-test-utils'
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
/* global jasmine, webdriver */
|
||||
import { join } from 'path'
|
||||
import {
|
||||
nextServer,
|
||||
|
@ -8,7 +8,6 @@ import {
|
|||
stopApp,
|
||||
runNextCommand
|
||||
} from 'next-test-utils'
|
||||
import webdriver from 'next-webdriver'
|
||||
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
import cheerio from 'cheerio'
|
||||
import { waitFor, check } from 'next-test-utils'
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
/* global jasmine, webdriver, browserName */
|
||||
import { readFileSync } from 'fs'
|
||||
import { join } from 'path'
|
||||
import {
|
||||
|
@ -10,7 +10,6 @@ import {
|
|||
renderViaHTTP,
|
||||
waitFor
|
||||
} from 'next-test-utils'
|
||||
import webdriver from 'next-webdriver'
|
||||
import fetch from 'node-fetch'
|
||||
import dynamicImportTests from './dynamic'
|
||||
import processEnv from './process-env'
|
||||
|
@ -237,75 +236,78 @@ describe('Production Usage', () => {
|
|||
await browser.close()
|
||||
})
|
||||
|
||||
it('should add preload tags when Link prefetch prop is used', async () => {
|
||||
const browser = await webdriver(appPort, '/prefetch')
|
||||
const elements = await browser.elementsByCss('link[rel=preload]')
|
||||
expect(elements.length).toBe(9)
|
||||
await Promise.all(
|
||||
elements.map(async (element) => {
|
||||
const rel = await element.getAttribute('rel')
|
||||
const as = await element.getAttribute('as')
|
||||
expect(rel).toBe('preload')
|
||||
expect(as).toBe('script')
|
||||
})
|
||||
)
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
// This is a workaround to fix https://github.com/zeit/next.js/issues/5860
|
||||
// TODO: remove this workaround when https://bugs.webkit.org/show_bug.cgi?id=187726 is fixed.
|
||||
it('It does not add a timestamp to link tags with preload attribute', async () => {
|
||||
const browser = await webdriver(appPort, '/prefetch')
|
||||
const links = await browser.elementsByCss('link[rel=preload]')
|
||||
await Promise.all(
|
||||
links.map(async (element) => {
|
||||
const href = await element.getAttribute('href')
|
||||
expect(href).not.toMatch(/\?ts=/)
|
||||
})
|
||||
)
|
||||
const scripts = await browser.elementsByCss('script[src]')
|
||||
await Promise.all(
|
||||
scripts.map(async (element) => {
|
||||
const src = await element.getAttribute('src')
|
||||
expect(src).not.toMatch(/\?ts=/)
|
||||
})
|
||||
)
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should reload the page on page script error with prefetch', async () => {
|
||||
const browser = await webdriver(appPort, '/counter')
|
||||
const counter = await browser
|
||||
.elementByCss('#increase').click().click()
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 2')
|
||||
|
||||
// Let the browser to prefetch the page and error it on the console.
|
||||
await waitFor(3000)
|
||||
const browserLogs = await browser.log('browser')
|
||||
let foundLog = false
|
||||
browserLogs.forEach((log) => {
|
||||
if (log.message.match(/\/no-such-page\.js - Failed to load resource/)) {
|
||||
foundLog = true
|
||||
}
|
||||
if (browserName === 'chrome') {
|
||||
it('should add preload tags when Link prefetch prop is used', async () => {
|
||||
const browser = await webdriver(appPort, '/prefetch')
|
||||
const elements = await browser.elementsByCss('link[rel=preload]')
|
||||
expect(elements.length).toBe(9)
|
||||
await Promise.all(
|
||||
elements.map(async (element) => {
|
||||
const rel = await element.getAttribute('rel')
|
||||
const as = await element.getAttribute('as')
|
||||
expect(rel).toBe('preload')
|
||||
expect(as).toBe('script')
|
||||
})
|
||||
)
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
expect(foundLog).toBe(true)
|
||||
// This is a workaround to fix https://github.com/zeit/next.js/issues/5860
|
||||
// TODO: remove this workaround when https://bugs.webkit.org/show_bug.cgi?id=187726 is fixed.
|
||||
it('It does not add a timestamp to link tags with preload attribute', async () => {
|
||||
const browser = await webdriver(appPort, '/prefetch')
|
||||
const links = await browser.elementsByCss('link[rel=preload]')
|
||||
await Promise.all(
|
||||
links.map(async (element) => {
|
||||
const href = await element.getAttribute('href')
|
||||
expect(href).not.toMatch(/\?ts=/)
|
||||
})
|
||||
)
|
||||
const scripts = await browser.elementsByCss('script[src]')
|
||||
await Promise.all(
|
||||
scripts.map(async (element) => {
|
||||
const src = await element.getAttribute('src')
|
||||
expect(src).not.toMatch(/\?ts=/)
|
||||
})
|
||||
)
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
// When we go to the 404 page, it'll do a hard reload.
|
||||
// So, it's possible for the front proxy to load a page from another zone.
|
||||
// Since the page is reloaded, when we go back to the counter page again,
|
||||
// previous counter value should be gone.
|
||||
const counterAfter404Page = await browser
|
||||
.elementByCss('#no-such-page-prefetch').click()
|
||||
.waitForElementByCss('h1')
|
||||
.back()
|
||||
.waitForElementByCss('#counter-page')
|
||||
.elementByCss('#counter').text()
|
||||
expect(counterAfter404Page).toBe('Counter: 0')
|
||||
it('should reload the page on page script error with prefetch', async () => {
|
||||
const browser = await webdriver(appPort, '/counter')
|
||||
if (!browser.log) return
|
||||
const counter = await browser
|
||||
.elementByCss('#increase').click().click()
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 2')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
// Let the browser to prefetch the page and error it on the console.
|
||||
await waitFor(3000)
|
||||
const browserLogs = await browser.log('browser')
|
||||
let foundLog = false
|
||||
browserLogs.forEach((log) => {
|
||||
if (log.message.match(/\/no-such-page\.js - Failed to load resource/)) {
|
||||
foundLog = true
|
||||
}
|
||||
})
|
||||
|
||||
expect(foundLog).toBe(true)
|
||||
|
||||
// When we go to the 404 page, it'll do a hard reload.
|
||||
// So, it's possible for the front proxy to load a page from another zone.
|
||||
// Since the page is reloaded, when we go back to the counter page again,
|
||||
// previous counter value should be gone.
|
||||
const counterAfter404Page = await browser
|
||||
.elementByCss('#no-such-page-prefetch').click()
|
||||
.waitForElementByCss('h1')
|
||||
.back()
|
||||
.waitForElementByCss('#counter-page')
|
||||
.elementByCss('#counter').text()
|
||||
expect(counterAfter404Page).toBe('Counter: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it('should not expose the compiled page file in development', async () => {
|
||||
|
@ -342,5 +344,5 @@ describe('Production Usage', () => {
|
|||
dynamicImportTests(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||
|
||||
processEnv(context)
|
||||
security(context)
|
||||
if (browserName === 'chrome') security(context)
|
||||
})
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
import webdriver from 'next-webdriver'
|
||||
/* global webdriver */
|
||||
import { readFile } from 'fs'
|
||||
import { promisify } from 'util'
|
||||
import { join } from 'path'
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/* eslint-env jest */
|
||||
/* global webdriver */
|
||||
import { readFileSync } from 'fs'
|
||||
import { join, resolve as resolvePath } from 'path'
|
||||
import { renderViaHTTP, getBrowserBodyText, waitFor } from 'next-test-utils'
|
||||
import webdriver from 'next-webdriver'
|
||||
import { recursiveReadDir } from 'next/dist/lib/recursive-readdir'
|
||||
import { homedir } from 'os'
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine, test */
|
||||
/* global jasmine, test, webdriver */
|
||||
import { join } from 'path'
|
||||
import { existsSync } from 'fs'
|
||||
import {
|
||||
|
@ -8,7 +8,6 @@ import {
|
|||
renderViaHTTP
|
||||
} from 'next-test-utils'
|
||||
import startServer from '../server'
|
||||
import webdriver from 'next-webdriver'
|
||||
import fetch from 'node-fetch'
|
||||
|
||||
const appDir = join(__dirname, '../')
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
/* global jasmine, webdriver */
|
||||
import { join } from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
import {
|
||||
getReactErrorOverlayContent,
|
||||
nextServer,
|
||||
|
|
210
test/jest-environment.js
Normal file
210
test/jest-environment.js
Normal file
|
@ -0,0 +1,210 @@
|
|||
// my-custom-environment
|
||||
const wd = require('wd')
|
||||
const os = require('os')
|
||||
const http = require('http')
|
||||
const fetch = require('node-fetch')
|
||||
const getPort = require('get-port')
|
||||
const NodeEnvironment = require('jest-environment-node')
|
||||
const {
|
||||
HEADLESS,
|
||||
BROWSER_NAME,
|
||||
BROWSERSTACK,
|
||||
BROWSERSTACK_USERNAME,
|
||||
BROWSERSTACK_ACCESS_KEY
|
||||
} = process.env
|
||||
|
||||
let browser
|
||||
let initialWindow
|
||||
let driverPort = 9515
|
||||
let browserOptions = {
|
||||
browserName: BROWSER_NAME || 'chrome'
|
||||
}
|
||||
let deviceIP = 'localhost'
|
||||
|
||||
const isIE = BROWSER_NAME === 'ie'
|
||||
const isSafari = BROWSER_NAME === 'safari'
|
||||
const isFirefox = BROWSER_NAME === 'firefox'
|
||||
// 30 seconds for BrowserStack 5 seconds for local
|
||||
const isBrowserStack = BROWSERSTACK && BROWSERSTACK_USERNAME && BROWSERSTACK_ACCESS_KEY
|
||||
const browserTimeout = (isBrowserStack ? 30 : 5) * 1000
|
||||
|
||||
if (isBrowserStack) {
|
||||
const safariOpts = {
|
||||
'os': 'OS X',
|
||||
'os_version': 'Mojave',
|
||||
'browser': 'Safari'
|
||||
}
|
||||
const ieOpts = {
|
||||
'os': 'Windows',
|
||||
'os_version': '10',
|
||||
'browser': 'IE'
|
||||
}
|
||||
const firefoxOpts = {
|
||||
'os': 'Windows',
|
||||
'os_version': '10',
|
||||
'browser': 'Firefox'
|
||||
}
|
||||
const sharedOpts = {
|
||||
'browserstack.local': true,
|
||||
'browserstack.video': false
|
||||
}
|
||||
|
||||
browserOptions = {
|
||||
...browserOptions,
|
||||
...sharedOpts,
|
||||
|
||||
...(isIE ? ieOpts : {}),
|
||||
...(isSafari ? safariOpts : {}),
|
||||
...(isFirefox ? firefoxOpts : {})
|
||||
}
|
||||
} else if (HEADLESS !== 'false') {
|
||||
browserOptions.chromeOptions = { args: ['--headless'] }
|
||||
}
|
||||
|
||||
const newTabPg = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>new tab</title>
|
||||
</head>
|
||||
<body>
|
||||
<a href="about:blank" target="_blank" id="new">Click me</a>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
|
||||
class CustomEnvironment extends NodeEnvironment {
|
||||
async createBrowser (fromWebdriver = false) {
|
||||
// always create new browser session if not BrowserStack
|
||||
if ((!browser && isBrowserStack) || fromWebdriver) {
|
||||
browser = isBrowserStack
|
||||
? wd.promiseChainRemote(
|
||||
'hub-cloud.browserstack.com', // seleniumHost
|
||||
80, // seleniumPort
|
||||
BROWSERSTACK_USERNAME,
|
||||
BROWSERSTACK_ACCESS_KEY
|
||||
)
|
||||
: wd.promiseChainRemote(`http://localhost:${driverPort}/`)
|
||||
|
||||
// Setup the browser instance
|
||||
await browser.init(browserOptions)
|
||||
if (isBrowserStack) initialWindow = await browser.windowHandle()
|
||||
global.browser = 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()
|
||||
})
|
||||
})
|
||||
|
||||
const networkIntfs = os.networkInterfaces()
|
||||
// find deviceIP to use with BrowserStack
|
||||
for (const intf of Object.keys(networkIntfs)) {
|
||||
const addresses = networkIntfs[intf]
|
||||
|
||||
for (const { internal, address, family } of addresses) {
|
||||
if (family !== 'IPv4' || internal) continue
|
||||
try {
|
||||
const res = await fetch(`http://${address}:${this.newTabPort}`)
|
||||
if (res.ok) {
|
||||
deviceIP = address
|
||||
break
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.global.browserName = BROWSER_NAME
|
||||
|
||||
// Mock current browser set up
|
||||
this.global.webdriver = async (appPort, pathname) => {
|
||||
if (!isBrowserStack) await this.createBrowser(true)
|
||||
const url = `http://${deviceIP}:${appPort}${pathname}`
|
||||
|
||||
console.log(`\n> Loading browser with ${url}\n`)
|
||||
if (isBrowserStack) await this.freshWindow()
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let timedOut = false
|
||||
const timeoutHandler = setTimeout(() => {
|
||||
timedOut = true
|
||||
reject(new Error(`Loading browser with ${url} timed out`))
|
||||
}, browserTimeout)
|
||||
|
||||
browser.get(url, err => {
|
||||
if (err) return reject(err)
|
||||
clearTimeout(timeoutHandler)
|
||||
if (!timedOut) resolve(browser)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async freshWindow (tries = 0) {
|
||||
if (tries > 3) throw new Error('failed to get fresh browser window')
|
||||
// Since we need a fresh start for each window
|
||||
// we have to force a new tab which can be disposed
|
||||
const startWindows = await browser.windowHandles()
|
||||
|
||||
// let's close all windows that aren't the initial window
|
||||
for (const window of startWindows) {
|
||||
if (!window || window === initialWindow) continue
|
||||
try {
|
||||
await browser.window(window)
|
||||
await browser.origClose()
|
||||
} catch (_) { /* should already be closed */ }
|
||||
}
|
||||
// focus initial window
|
||||
await browser.window(initialWindow)
|
||||
const newTabUrl = `http://${deviceIP}:${this.newTabPort}/`
|
||||
|
||||
// load html to open new tab
|
||||
if (await browser.url() !== newTabUrl) {
|
||||
await browser.get(newTabUrl)
|
||||
}
|
||||
// click new tab link
|
||||
await browser.elementByCss('#new').click()
|
||||
|
||||
// focus fresh window
|
||||
const newWindows = await browser.windowHandles()
|
||||
try {
|
||||
await browser.window(
|
||||
newWindows.find(win => {
|
||||
if (win &&
|
||||
win !== initialWindow &&
|
||||
startWindows.indexOf(win) < 0
|
||||
) {
|
||||
return win
|
||||
}
|
||||
})
|
||||
)
|
||||
} catch (err) {
|
||||
await this.freshWindow(tries + 1)
|
||||
}
|
||||
}
|
||||
|
||||
async setup () {
|
||||
await super.setup()
|
||||
await this.createBrowser()
|
||||
}
|
||||
|
||||
async teardown () {
|
||||
await super.teardown()
|
||||
if (this.server) this.server.close()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = CustomEnvironment
|
|
@ -1,14 +1,36 @@
|
|||
'use strict'
|
||||
let globalSetup
|
||||
|
||||
const chromedriver = require('chromedriver')
|
||||
const waitPort = require('wait-port')
|
||||
if (process.env.BROWSERSTACK) {
|
||||
const { Local } = require('browserstack-local')
|
||||
const browserStackLocal = new Local()
|
||||
const localBrowserStackOpts = {
|
||||
key: process.env.BROWSERSTACK_ACCESS_KEY
|
||||
}
|
||||
global.browserStackLocal = browserStackLocal
|
||||
|
||||
module.exports = async function globalSetup () {
|
||||
chromedriver.start()
|
||||
globalSetup = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
browserStackLocal.start(localBrowserStackOpts, err => {
|
||||
if (err) return reject(err)
|
||||
console.log('Started BrowserStackLocal', browserStackLocal.isRunning())
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
} else {
|
||||
const chromedriver = require('chromedriver')
|
||||
const waitPort = require('wait-port')
|
||||
|
||||
// https://github.com/giggio/node-chromedriver/issues/117
|
||||
await waitPort({
|
||||
port: 9515,
|
||||
timeout: 1000 * 60 * 2 // 2 Minutes
|
||||
})
|
||||
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()
|
||||
|
|
|
@ -1,7 +1,27 @@
|
|||
'use strict'
|
||||
|
||||
const chromedriver = require('chromedriver')
|
||||
let globalTeardown
|
||||
const browser = global.browser
|
||||
|
||||
module.exports = async function globalSetup () {
|
||||
chromedriver.stop()
|
||||
if (process.env.BROWSERSTACK) {
|
||||
globalTeardown = () => global.browserStackLocal.killAllProcesses(() => {})
|
||||
} else {
|
||||
const chromedriver = require('chromedriver')
|
||||
globalTeardown = () => chromedriver.stop()
|
||||
}
|
||||
|
||||
module.exports = async () => {
|
||||
await globalTeardown()
|
||||
|
||||
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()
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,88 +0,0 @@
|
|||
import wd from 'wd'
|
||||
import getPort from 'get-port'
|
||||
import waitPort from 'wait-port'
|
||||
|
||||
const doHeadless = process.env.HEADLESS !== 'false'
|
||||
let driverPort = 9515
|
||||
|
||||
export default 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)
|
||||
})
|
||||
})
|
||||
}
|
119
yarn.lock
119
yarn.lock
|
@ -2896,6 +2896,17 @@ browserslist@^4.0.0, browserslist@^4.1.0:
|
|||
electron-to-chromium "^1.3.103"
|
||||
node-releases "^1.1.3"
|
||||
|
||||
browserstack-local@1.3.7:
|
||||
version "1.3.7"
|
||||
resolved "https://registry.yarnpkg.com/browserstack-local/-/browserstack-local-1.3.7.tgz#cac9fc958eaa0a352e8f1ca1dc91bb141ba5da6f"
|
||||
integrity sha512-ilZlmiy7XYJxsztYan7XueHVr3Ix9EVh/mCiYN1G53wRPEW/hg1KMsseM6UExzVbexEqFEfwjkBLeFlSqxh+bQ==
|
||||
dependencies:
|
||||
https-proxy-agent "^2.2.1"
|
||||
is-running "^2.0.0"
|
||||
ps-tree "=1.1.1"
|
||||
sinon "^1.17.6"
|
||||
temp-fs "^0.9.9"
|
||||
|
||||
bser@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
|
||||
|
@ -4547,7 +4558,7 @@ duplexer3@^0.1.4:
|
|||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
|
||||
|
||||
duplexer@^0.1.1:
|
||||
duplexer@^0.1.1, duplexer@~0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
|
||||
integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
|
||||
|
@ -4933,6 +4944,19 @@ etag@1.8.1, etag@~1.8.1:
|
|||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||
|
||||
event-stream@=3.3.4:
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571"
|
||||
integrity sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE=
|
||||
dependencies:
|
||||
duplexer "~0.1.1"
|
||||
from "~0"
|
||||
map-stream "~0.1.0"
|
||||
pause-stream "0.0.11"
|
||||
split "0.3"
|
||||
stream-combiner "~0.0.4"
|
||||
through "~2.3.1"
|
||||
|
||||
event-target-shim@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||
|
@ -5504,6 +5528,13 @@ form-data@~2.3.1, form-data@~2.3.2:
|
|||
combined-stream "^1.0.6"
|
||||
mime-types "^2.1.12"
|
||||
|
||||
formatio@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/formatio/-/formatio-1.1.1.tgz#5ed3ccd636551097383465d996199100e86161e9"
|
||||
integrity sha1-XtPM1jZVEJc4NGXZlhmRAOhhYek=
|
||||
dependencies:
|
||||
samsam "~1.1"
|
||||
|
||||
forwarded@~0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
|
||||
|
@ -5529,6 +5560,11 @@ from2@^2.1.0:
|
|||
inherits "^2.0.1"
|
||||
readable-stream "^2.0.0"
|
||||
|
||||
from@~0:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe"
|
||||
integrity sha1-g8YK/Fi5xWmXAH7Rp2izqzA6RP4=
|
||||
|
||||
fs-constants@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
|
||||
|
@ -6798,6 +6834,11 @@ is-retry-allowed@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
|
||||
integrity sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=
|
||||
|
||||
is-running@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-running/-/is-running-2.1.0.tgz#30a73ff5cc3854e4fc25490809e9f5abf8de09e0"
|
||||
integrity sha1-MKc/9cw4VOT8JUkICen1q/jeCeA=
|
||||
|
||||
is-ssh@^1.3.0:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/is-ssh/-/is-ssh-1.3.1.tgz#f349a8cadd24e65298037a522cf7520f2e81a0f3"
|
||||
|
@ -7994,6 +8035,11 @@ log-update@^1.0.2:
|
|||
ansi-escapes "^1.0.0"
|
||||
cli-cursor "^1.0.2"
|
||||
|
||||
lolex@1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.3.2.tgz#7c3da62ffcb30f0f5a80a2566ca24e45d8a01f31"
|
||||
integrity sha1-fD2mL/yzDw9agKJWbKJORdigHzE=
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
|
@ -8099,6 +8145,11 @@ map-obj@^2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9"
|
||||
integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk=
|
||||
|
||||
map-stream@~0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
|
||||
integrity sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ=
|
||||
|
||||
map-visit@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
|
||||
|
@ -9448,6 +9499,13 @@ path-type@^3.0.0:
|
|||
dependencies:
|
||||
pify "^3.0.0"
|
||||
|
||||
pause-stream@0.0.11:
|
||||
version "0.0.11"
|
||||
resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
|
||||
integrity sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=
|
||||
dependencies:
|
||||
through "~2.3"
|
||||
|
||||
pbkdf2@^3.0.3:
|
||||
version "3.0.17"
|
||||
resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6"
|
||||
|
@ -10053,6 +10111,13 @@ prr@~1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
|
||||
integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY=
|
||||
|
||||
ps-tree@=1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.1.tgz#5f1ba35455b8c25eeb718d04c37de1555d96d3db"
|
||||
integrity sha512-kef7fYYSKVqQffmzTMsVcUD1ObNJMp8sNSmHGlGKsZQyL/ht9MZKk86u0Rd1NhpTOAuhqwKCLLpktwkqz+MF8A==
|
||||
dependencies:
|
||||
event-stream "=3.3.4"
|
||||
|
||||
pseudomap@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||
|
@ -10904,6 +10969,13 @@ rimraf@2.6.2:
|
|||
dependencies:
|
||||
glob "^7.0.5"
|
||||
|
||||
rimraf@~2.5.2:
|
||||
version "2.5.4"
|
||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.5.4.tgz#96800093cbf1a0c86bd95b4625467535c29dfa04"
|
||||
integrity sha1-loAAk8vxoMhr2VtGJUZ1NcKd+gQ=
|
||||
dependencies:
|
||||
glob "^7.0.5"
|
||||
|
||||
ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c"
|
||||
|
@ -10972,6 +11044,16 @@ safe-regex@^1.1.0:
|
|||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
samsam@1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567"
|
||||
integrity sha1-vsEf3IOp/aBjQBIQ5AF2wwJNFWc=
|
||||
|
||||
samsam@~1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621"
|
||||
integrity sha1-n1CHQZtNCR8jJXHn+lLpCw9VJiE=
|
||||
|
||||
sane@^2.0.0:
|
||||
version "2.5.2"
|
||||
resolved "https://registry.yarnpkg.com/sane/-/sane-2.5.2.tgz#b4dc1861c21b427e929507a3e751e2a2cb8ab3fa"
|
||||
|
@ -11220,6 +11302,16 @@ simple-swizzle@^0.2.2:
|
|||
dependencies:
|
||||
is-arrayish "^0.3.1"
|
||||
|
||||
sinon@^1.17.6:
|
||||
version "1.17.7"
|
||||
resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf"
|
||||
integrity sha1-RUKk9JugxFwF6y6d2dID4rjv4L8=
|
||||
dependencies:
|
||||
formatio "1.1.1"
|
||||
lolex "1.3.2"
|
||||
samsam "1.1.2"
|
||||
util ">=0.10.3 <1"
|
||||
|
||||
sisteransi@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce"
|
||||
|
@ -11433,6 +11525,13 @@ split2@^2.0.0:
|
|||
dependencies:
|
||||
through2 "^2.0.2"
|
||||
|
||||
split@0.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
|
||||
integrity sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8=
|
||||
dependencies:
|
||||
through "2"
|
||||
|
||||
split@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/split/-/split-1.0.1.tgz#605bd9be303aa59fb35f9229fbea0ddec9ea07d9"
|
||||
|
@ -11555,6 +11654,13 @@ stream-browserify@^2.0.1:
|
|||
inherits "~2.0.1"
|
||||
readable-stream "^2.0.2"
|
||||
|
||||
stream-combiner@~0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14"
|
||||
integrity sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ=
|
||||
dependencies:
|
||||
duplexer "~0.1.1"
|
||||
|
||||
stream-each@^1.1.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae"
|
||||
|
@ -11901,6 +12007,13 @@ temp-dir@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
|
||||
integrity sha1-CnwOom06Oa+n4OvqnB/AvE2qAR0=
|
||||
|
||||
temp-fs@^0.9.9:
|
||||
version "0.9.9"
|
||||
resolved "https://registry.yarnpkg.com/temp-fs/-/temp-fs-0.9.9.tgz#8071730437870720e9431532fe2814364f8803d7"
|
||||
integrity sha1-gHFzBDeHByDpQxUy/igUNk+IA9c=
|
||||
dependencies:
|
||||
rimraf "~2.5.2"
|
||||
|
||||
temp-write@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-3.4.0.tgz#8cff630fb7e9da05f047c74ce4ce4d685457d492"
|
||||
|
@ -11985,7 +12098,7 @@ through2@~0.4.1:
|
|||
readable-stream "~1.0.17"
|
||||
xtend "~2.1.1"
|
||||
|
||||
through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6:
|
||||
through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3, through@~2.3.1:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||
|
@ -12561,7 +12674,7 @@ util@0.10.3:
|
|||
dependencies:
|
||||
inherits "2.0.1"
|
||||
|
||||
util@^0.11.0:
|
||||
"util@>=0.10.3 <1", util@^0.11.0:
|
||||
version "0.11.1"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61"
|
||||
integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==
|
||||
|
|
Loading…
Reference in a new issue