Break up basic test suite (#6730)
* Break out client-navigation and rendering test from basic test * Try with parallelism dialed back to 3 * Update jest-junit for more compatible timings in CircleCI * Bump to test timings * Use filepath for suitename in jest-junit * Store reports as artifacts * Try using classname for timings * Bump * Remove reports from artifacts
This commit is contained in:
parent
2c975b1cdc
commit
7d0919a784
61 changed files with 908 additions and 824 deletions
|
@ -16,7 +16,7 @@ jobs:
|
|||
root: ~/repo
|
||||
paths: ['.']
|
||||
test:
|
||||
parallelism: 6
|
||||
parallelism: 3
|
||||
docker:
|
||||
- image: circleci/node:8-browsers
|
||||
working_directory: ~/repo
|
||||
|
@ -25,9 +25,10 @@ jobs:
|
|||
at: .
|
||||
- run:
|
||||
name: Tests
|
||||
command: yarn testall $(circleci tests glob "test/**/*.test.*" | circleci tests split --split-by=timings)
|
||||
command: yarn testall $(circleci tests glob "test/**/*.test.*" | circleci tests split --split-by=timings --timings-type=classname)
|
||||
environment:
|
||||
JEST_JUNIT_OUTPUT: 'reports/junit/js-test-results.xml'
|
||||
JEST_JUNIT_CLASSNAME: '{filepath}'
|
||||
- store_test_results:
|
||||
path: ~/repo/reports
|
||||
deploy:
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
"examples/with-ioc/**",
|
||||
"examples/with-kea/**",
|
||||
"examples/with-mobx/**",
|
||||
"test/integration/basic/pages/throw-undefined.js"
|
||||
"test/integration/client-navigation/pages/throw-undefined.js"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -82,7 +82,7 @@
|
|||
"get-port": "3.2.0",
|
||||
"isomorphic-unfetch": "3.0.0",
|
||||
"jest-cli": "23.6.0",
|
||||
"jest-junit": "^5.0.0",
|
||||
"jest-junit": "6.3.0",
|
||||
"lerna": "^3.4.0",
|
||||
"lint-staged": "4.2.3",
|
||||
"mkdirp": "0.5.1",
|
||||
|
|
|
@ -1,776 +0,0 @@
|
|||
/* eslint-env jest */
|
||||
|
||||
import webdriver from 'next-webdriver'
|
||||
import { waitFor, getReactErrorOverlayContent } from 'next-test-utils'
|
||||
|
||||
export default (context) => {
|
||||
describe('Client Navigation', () => {
|
||||
describe('with <Link/>', () => {
|
||||
it('should navigate the page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const text = await browser
|
||||
.elementByCss('#about-link').click()
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should navigate via the client side', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
const counterText = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#about-link').click()
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('#home-link').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.elementByCss('#counter').text()
|
||||
|
||||
expect(counterText).toBe('Counter: 1')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('With url property', () => {
|
||||
it('Should keep immutable pathname, asPath and query', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/url-prop-change')
|
||||
await browser.elementByCss('#add-query').click()
|
||||
const urlResult = await browser.elementByCss('#url-result').text()
|
||||
const previousUrlResult = await browser.elementByCss('#previous-url-result').text()
|
||||
|
||||
expect(JSON.parse(urlResult)).toMatchObject({ 'query': { 'added': 'yes' }, 'pathname': '/nav/url-prop-change', 'asPath': '/nav/url-prop-change?added=yes' })
|
||||
expect(JSON.parse(previousUrlResult)).toMatchObject({ 'query': {}, 'pathname': '/nav/url-prop-change', 'asPath': '/nav/url-prop-change' })
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with <a/> tag inside the <Link />', () => {
|
||||
it('should navigate the page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/about')
|
||||
const text = await browser
|
||||
.elementByCss('#home-link').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the home.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should not navigate if the <a/> tag has a target', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
const counterText = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#target-link').click()
|
||||
.elementByCss('#counter').text()
|
||||
|
||||
expect(counterText).toBe('Counter: 1')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with unexpected <a/> nested tag', () => {
|
||||
it('should not redirect if passHref prop is not defined in Link', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/pass-href-prop')
|
||||
const text = await browser
|
||||
.elementByCss('#without-href').click()
|
||||
.waitForElementByCss('.nav-pass-href-prop')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the passHref prop page.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should redirect if passHref prop is defined in Link', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/pass-href-prop')
|
||||
const text = await browser
|
||||
.elementByCss('#with-href').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the home.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with empty getInitialProps()', () => {
|
||||
it('should render an error', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav')
|
||||
await browser.elementByCss('#empty-props').click()
|
||||
|
||||
await waitFor(3000)
|
||||
|
||||
expect(await getReactErrorOverlayContent(browser)).toMatch(
|
||||
/should resolve to an object\. But found "null" instead\./
|
||||
)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('with the same page but different querystring', () => {
|
||||
it('should navigate the page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/querystring?id=1')
|
||||
const text = await browser
|
||||
.elementByCss('#next-id-link').click()
|
||||
.waitForElementByCss('.nav-id-2')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('2')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should remove querystring', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/querystring?id=1')
|
||||
const text = await browser
|
||||
.elementByCss('#main-page').click()
|
||||
.waitForElementByCss('.nav-id-0')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('0')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with the current url', () => {
|
||||
it('should reload the page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/self-reload')
|
||||
const defaultCount = await browser.elementByCss('p').text()
|
||||
expect(defaultCount).toBe('COUNT: 0')
|
||||
|
||||
const countAfterClicked = await browser
|
||||
.elementByCss('#self-reload-link').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(countAfterClicked).toBe('COUNT: 1')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should always replace the state', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
const countAfterClicked = await browser
|
||||
.elementByCss('#self-reload-link').click()
|
||||
.waitForElementByCss('#self-reload-page')
|
||||
.elementByCss('#self-reload-link').click()
|
||||
.elementByCss('#self-reload-link').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
// counts (page change + two clicks)
|
||||
expect(countAfterClicked).toBe('COUNT: 3')
|
||||
|
||||
// Since we replace the state, back button would simply go us back to /nav
|
||||
await browser
|
||||
.back()
|
||||
.waitForElementByCss('.nav-home')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with onClick action', () => {
|
||||
it('should reload the page and perform additional action', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/on-click')
|
||||
const defaultCountQuery = await browser.elementByCss('#query-count').text()
|
||||
const defaultCountState = await browser.elementByCss('#state-count').text()
|
||||
expect(defaultCountQuery).toBe('QUERY COUNT: 0')
|
||||
expect(defaultCountState).toBe('STATE COUNT: 0')
|
||||
|
||||
await browser.elementByCss('#on-click-link').click()
|
||||
|
||||
const countQueryAfterClicked = await browser.elementByCss('#query-count').text()
|
||||
const countStateAfterClicked = await browser.elementByCss('#state-count').text()
|
||||
expect(countQueryAfterClicked).toBe('QUERY COUNT: 1')
|
||||
expect(countStateAfterClicked).toBe('STATE COUNT: 1')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should not reload if default was prevented', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/on-click')
|
||||
const defaultCountQuery = await browser.elementByCss('#query-count').text()
|
||||
const defaultCountState = await browser.elementByCss('#state-count').text()
|
||||
expect(defaultCountQuery).toBe('QUERY COUNT: 0')
|
||||
expect(defaultCountState).toBe('STATE COUNT: 0')
|
||||
|
||||
await browser.elementByCss('#on-click-link-prevent-default').click()
|
||||
|
||||
const countQueryAfterClicked = await browser.elementByCss('#query-count').text()
|
||||
const countStateAfterClicked = await browser.elementByCss('#state-count').text()
|
||||
expect(countQueryAfterClicked).toBe('QUERY COUNT: 0')
|
||||
expect(countStateAfterClicked).toBe('STATE COUNT: 1')
|
||||
|
||||
await browser.elementByCss('#on-click-link').click()
|
||||
|
||||
const countQueryAfterClickedAgain = await browser.elementByCss('#query-count').text()
|
||||
const countStateAfterClickedAgain = await browser.elementByCss('#state-count').text()
|
||||
expect(countQueryAfterClickedAgain).toBe('QUERY COUNT: 1')
|
||||
expect(countStateAfterClickedAgain).toBe('STATE COUNT: 2')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should always replace the state and perform additional action', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
await browser.elementByCss('#on-click-link').click().waitForElementByCss('#on-click-page')
|
||||
|
||||
const defaultCountQuery = await browser.elementByCss('#query-count').text()
|
||||
expect(defaultCountQuery).toBe('QUERY COUNT: 1')
|
||||
|
||||
await browser.elementByCss('#on-click-link').click()
|
||||
const countQueryAfterClicked = await browser.elementByCss('#query-count').text()
|
||||
const countStateAfterClicked = await browser.elementByCss('#state-count').text()
|
||||
expect(countQueryAfterClicked).toBe('QUERY COUNT: 2')
|
||||
expect(countStateAfterClicked).toBe('STATE COUNT: 1')
|
||||
|
||||
// Since we replace the state, back button would simply go us back to /nav
|
||||
await browser.back().waitForElementByCss('.nav-home')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('with hash changes', () => {
|
||||
describe('when hash change via Link', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-link').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should scroll to the specified position on the same page', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
// Scrolls to item 400 on the page
|
||||
const scrollPosition = await browser
|
||||
.elementByCss('#scroll-to-item-400').click()
|
||||
.eval('window.pageYOffset')
|
||||
|
||||
expect(scrollPosition).toBe(7258)
|
||||
|
||||
// Scrolls back to top when scrolling to `#` with no value.
|
||||
const scrollPositionAfterEmptyHash = await browser
|
||||
.elementByCss('#via-empty-hash').click()
|
||||
.eval('window.pageYOffset')
|
||||
|
||||
expect(scrollPositionAfterEmptyHash).toBe(0)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should scroll to the specified position on the same page with a name property', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
// Scrolls to item 400 with name="name-item-400" on the page
|
||||
const scrollPosition = await browser
|
||||
.elementByCss('#scroll-to-name-item-400').click()
|
||||
.eval('window.pageYOffset')
|
||||
|
||||
console.log(scrollPosition)
|
||||
|
||||
expect(scrollPosition).toBe(16258)
|
||||
|
||||
// Scrolls back to top when scrolling to `#` with no value.
|
||||
const scrollPositionAfterEmptyHash = await browser
|
||||
.elementByCss('#via-empty-hash').click()
|
||||
.eval('window.pageYOffset')
|
||||
|
||||
expect(scrollPositionAfterEmptyHash).toBe(0)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should scroll to the specified position to a new page', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
// Scrolls to item 400 on the page
|
||||
await browser
|
||||
.elementByCss('#scroll-to-hash').click()
|
||||
.waitForElementByCss('#hash-changes-page')
|
||||
|
||||
const scrollPosition = await browser.eval('window.pageYOffset')
|
||||
expect(scrollPosition).toBe(7258)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('when hash change via A tag', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-a').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when hash get removed', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-a').click()
|
||||
.elementByCss('#page-url').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 1')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when hash set to empty', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-a').click()
|
||||
.elementByCss('#via-empty-hash').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when hash changed to a different hash', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-a').click()
|
||||
.elementByCss('#via-link').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with shallow routing', () => {
|
||||
it('should update the url without running getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||
const counter = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 2')
|
||||
|
||||
const getInitialPropsRunCount = await browser
|
||||
.elementByCss('#get-initial-props-run-count').text()
|
||||
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 1')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should handle the back button and should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||
let counter = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 2')
|
||||
|
||||
counter = await browser
|
||||
.back()
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 1')
|
||||
|
||||
const getInitialPropsRunCount = await browser
|
||||
.elementByCss('#get-initial-props-run-count').text()
|
||||
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 1')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should run getInitialProps always when rending the page to the screen', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#home-link').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.back()
|
||||
.waitForElementByCss('.shallow-routing')
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 2')
|
||||
|
||||
const getInitialPropsRunCount = await browser
|
||||
.elementByCss('#get-initial-props-run-count').text()
|
||||
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 2')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with URL objects', () => {
|
||||
it('should work with <Link/>', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const text = await browser
|
||||
.elementByCss('#query-string-link').click()
|
||||
.waitForElementByCss('.nav-querystring')
|
||||
.elementByCss('p').text()
|
||||
expect(text).toBe('10')
|
||||
|
||||
expect(await browser.url())
|
||||
.toBe(`http://localhost:${context.appPort}/nav/querystring/10#10`)
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with "Router.push"', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const text = await browser
|
||||
.elementByCss('#query-string-button').click()
|
||||
.waitForElementByCss('.nav-querystring')
|
||||
.elementByCss('p').text()
|
||||
expect(text).toBe('10')
|
||||
|
||||
expect(await browser.url())
|
||||
.toBe(`http://localhost:${context.appPort}/nav/querystring/10#10`)
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with the "replace" prop', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
let stackLength = await browser
|
||||
.eval('window.history.length')
|
||||
|
||||
expect(stackLength).toBe(2)
|
||||
|
||||
// Navigation to /about using a replace link should maintain the url stack length
|
||||
const text = await browser
|
||||
.elementByCss('#about-replace-link').click()
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
|
||||
stackLength = await browser
|
||||
.eval('window.history.length')
|
||||
|
||||
expect(stackLength).toBe(2)
|
||||
|
||||
// Going back to the home with a regular link will augment the history count
|
||||
await browser
|
||||
.elementByCss('#home-link').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
|
||||
stackLength = await browser
|
||||
.eval('window.history.length')
|
||||
|
||||
expect(stackLength).toBe(3)
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with getInitialProp redirect', () => {
|
||||
it('should redirect the page via client side', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const text = await browser
|
||||
.elementByCss('#redirect-link').click()
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should redirect the page when loading', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/redirect')
|
||||
const text = await browser
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with different types of urls', () => {
|
||||
it('should work with normal page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/with-cdm')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('ComponentDidMount executed on client.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with dir/ page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nested-cdm')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('ComponentDidMount executed on client.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with /index page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/index')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('ComponentDidMount executed on client.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with / page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('ComponentDidMount executed on client.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with the HOC based router', () => {
|
||||
it('should navigate as expected', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/with-hoc')
|
||||
|
||||
const pathname = await browser.elementByCss('#pathname').text()
|
||||
expect(pathname).toBe('Current path: /nav/with-hoc')
|
||||
|
||||
const asPath = await browser.elementByCss('#asPath').text()
|
||||
expect(asPath).toBe('Current asPath: /nav/with-hoc')
|
||||
|
||||
const text = await browser
|
||||
.elementByCss('.nav-with-hoc a').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the home.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with asPath', () => {
|
||||
describe('inside getInitialProps', () => {
|
||||
it('should show the correct asPath with a Link with as prop', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const asPath = await browser
|
||||
.elementByCss('#as-path-link').click()
|
||||
.waitForElementByCss('.as-path-content')
|
||||
.elementByCss('.as-path-content').text()
|
||||
|
||||
expect(asPath).toBe('/as/path')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should show the correct asPath with a Link without the as prop', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const asPath = await browser
|
||||
.elementByCss('#as-path-link-no-as').click()
|
||||
.waitForElementByCss('.as-path-content')
|
||||
.elementByCss('.as-path-content').text()
|
||||
|
||||
expect(asPath).toBe('/nav/as-path')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with next/router', () => {
|
||||
it('should show the correct asPath', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const asPath = await browser
|
||||
.elementByCss('#as-path-using-router-link').click()
|
||||
.waitForElementByCss('.as-path-content')
|
||||
.elementByCss('.as-path-content').text()
|
||||
|
||||
expect(asPath).toBe('/nav/as-path-using-router')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with next/link', () => {
|
||||
it('should use pushState with same href and different asPath', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/as-path-pushstate')
|
||||
await browser.elementByCss('#hello').click().waitForElementByCss('#something-hello')
|
||||
const queryOne = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryOne.something).toBe('hello')
|
||||
await browser.elementByCss('#same-query').click().waitForElementByCss('#something-same-query')
|
||||
const queryTwo = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryTwo.something).toBe('hello')
|
||||
await browser.back().waitForElementByCss('#something-hello')
|
||||
const queryThree = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryThree.something).toBe('hello')
|
||||
await browser.elementByCss('#else').click().waitForElementByCss('#something-else')
|
||||
await browser.elementByCss('#hello2').click().waitForElementByCss('#nav-as-path-pushstate')
|
||||
await browser.back().waitForElementByCss('#something-else')
|
||||
const queryFour = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryFour.something).toBe(undefined)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should detect asPath query changes correctly', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/as-path-query')
|
||||
await browser.elementByCss('#hello').click().waitForElementByCss('#something-hello-something-hello')
|
||||
const queryOne = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryOne.something).toBe('hello')
|
||||
await browser.elementByCss('#hello2').click().waitForElementByCss('#something-hello-something-else')
|
||||
const queryTwo = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryTwo.something).toBe('else')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('runtime errors', () => {
|
||||
it('should show react-error-overlay when a client side error is thrown inside a component', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/error-inside-browser-page')
|
||||
await waitFor(3000)
|
||||
const text = await getReactErrorOverlayContent(browser)
|
||||
expect(text).toMatch(/An Expected error occurred/)
|
||||
expect(text).toMatch(/pages\/error-inside-browser-page\.js:5/)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should show react-error-overlay when a client side error is thrown outside a component', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/error-in-the-browser-global-scope')
|
||||
await waitFor(3000)
|
||||
const text = await getReactErrorOverlayContent(browser)
|
||||
expect(text).toMatch(/An Expected error occurred/)
|
||||
expect(text).toMatch(/error-in-the-browser-global-scope\.js:2/)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('with 404 pages', () => {
|
||||
it('should 404 on not existent page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/non-existent')
|
||||
expect(await browser.elementByCss('h1').text()).toBe('404')
|
||||
expect(await browser.elementByCss('h2').text()).toBe('This page could not be found.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should 404 for <page>/', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/about/')
|
||||
expect(await browser.elementByCss('h1').text()).toBe('404')
|
||||
expect(await browser.elementByCss('h2').text()).toBe('This page could not be found.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should should not contain a page script in a 404 page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/non-existent')
|
||||
const scripts = await browser.elementsByCss('script[src]')
|
||||
for (const script of scripts) {
|
||||
const src = await script.getAttribute('src')
|
||||
expect(src.includes('/non-existent')).toBeFalsy()
|
||||
}
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('updating head while client routing', () => {
|
||||
it('should update head during client routing', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/head-1')
|
||||
expect(await browser.elementByCss('meta[name="description"]').getAttribute('content')).toBe('Head One')
|
||||
await browser.elementByCss('#to-head-2').click().waitForElementByCss('#head-2')
|
||||
expect(await browser.elementByCss('meta[name="description"]').getAttribute('content')).toBe('Head Two')
|
||||
await browser.elementByCss('#to-head-1').click().waitForElementByCss('#head-1')
|
||||
expect(await browser.elementByCss('meta[name="description"]').getAttribute('content')).toBe('Head One')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should not error on module.exports + polyfills', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/read-only-object-error')
|
||||
expect(await browser.elementByCss('body').text()).toBe('this is just a placeholder component')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should work on nested /index/index.js', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nested-index/index')
|
||||
expect(await browser.elementByCss('p').text()).toBe('This is an index.js nested in an index/ folder.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
}
|
|
@ -3,15 +3,12 @@
|
|||
import { join } from 'path'
|
||||
import {
|
||||
renderViaHTTP,
|
||||
fetchViaHTTP,
|
||||
findPort,
|
||||
launchApp,
|
||||
killApp
|
||||
} from 'next-test-utils'
|
||||
|
||||
// test suits
|
||||
import rendering from './rendering'
|
||||
import clientNavigation from './client-navigation'
|
||||
import hmr from './hmr'
|
||||
import errorRecovery from './error-recovery'
|
||||
import dynamic from './dynamic'
|
||||
|
@ -27,37 +24,8 @@ describe('Basic Features', () => {
|
|||
|
||||
// pre-build all pages at the start
|
||||
await Promise.all([
|
||||
renderViaHTTP(context.appPort, '/async-props'),
|
||||
renderViaHTTP(context.appPort, '/default-head'),
|
||||
renderViaHTTP(context.appPort, '/empty-get-initial-props'),
|
||||
renderViaHTTP(context.appPort, '/error'),
|
||||
renderViaHTTP(context.appPort, '/finish-response'),
|
||||
renderViaHTTP(context.appPort, '/head'),
|
||||
renderViaHTTP(context.appPort, '/json'),
|
||||
renderViaHTTP(context.appPort, '/link'),
|
||||
renderViaHTTP(context.appPort, '/stateless'),
|
||||
renderViaHTTP(context.appPort, '/fragment-syntax'),
|
||||
renderViaHTTP(context.appPort, '/custom-extension'),
|
||||
renderViaHTTP(context.appPort, '/styled-jsx'),
|
||||
renderViaHTTP(context.appPort, '/with-cdm'),
|
||||
renderViaHTTP(context.appPort, '/url-prop'),
|
||||
renderViaHTTP(context.appPort, '/url-prop-override'),
|
||||
renderViaHTTP(context.appPort, '/process-env'),
|
||||
|
||||
renderViaHTTP(context.appPort, '/nav'),
|
||||
renderViaHTTP(context.appPort, '/nav/about'),
|
||||
renderViaHTTP(context.appPort, '/nav/on-click'),
|
||||
renderViaHTTP(context.appPort, '/nav/querystring'),
|
||||
renderViaHTTP(context.appPort, '/nav/self-reload'),
|
||||
renderViaHTTP(context.appPort, '/nav/hash-changes'),
|
||||
renderViaHTTP(context.appPort, '/nav/shallow-routing'),
|
||||
renderViaHTTP(context.appPort, '/nav/redirect'),
|
||||
renderViaHTTP(context.appPort, '/nav/as-path'),
|
||||
renderViaHTTP(context.appPort, '/nav/as-path-using-router'),
|
||||
renderViaHTTP(context.appPort, '/nav/url-prop-change'),
|
||||
|
||||
renderViaHTTP(context.appPort, '/nested-cdm/index'),
|
||||
|
||||
renderViaHTTP(context.appPort, '/hmr/about'),
|
||||
renderViaHTTP(context.appPort, '/hmr/style'),
|
||||
renderViaHTTP(context.appPort, '/hmr/contact'),
|
||||
|
@ -66,8 +34,6 @@ describe('Basic Features', () => {
|
|||
})
|
||||
afterAll(() => killApp(context.server))
|
||||
|
||||
rendering(context, 'Rendering via HTTP', (p, q) => renderViaHTTP(context.appPort, p, q), (p, q) => fetchViaHTTP(context.appPort, p, q))
|
||||
clientNavigation(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||
dynamic(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||
hmr(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||
errorRecovery(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||
|
|
3
test/integration/client-navigation/components/hello1.js
Normal file
3
test/integration/client-navigation/components/hello1.js
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default () => (
|
||||
<p>Hello World 1</p>
|
||||
)
|
6
test/integration/client-navigation/next.config.js
Normal file
6
test/integration/client-navigation/next.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
onDemandEntries: {
|
||||
// Make sure entries are not getting disposed.
|
||||
maxInactiveAge: 1000 * 60 * 60
|
||||
}
|
||||
}
|
5
test/integration/client-navigation/pages/dynamic/ssr.js
Normal file
5
test/integration/client-navigation/pages/dynamic/ssr.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
import dynamic from 'next/dynamic'
|
||||
|
||||
const Hello = dynamic(import('../../components/hello1'))
|
||||
|
||||
export default Hello
|
|
@ -8,4 +8,4 @@ ThrowUndefined.getInitialProps = () => {
|
|||
throw undefined
|
||||
}
|
||||
|
||||
export default ThrowUndefined
|
||||
export default ThrowUndefined
|
830
test/integration/client-navigation/test/index.test.js
Normal file
830
test/integration/client-navigation/test/index.test.js
Normal file
|
@ -0,0 +1,830 @@
|
|||
/* eslint-env jest */
|
||||
/* global jasmine */
|
||||
import { join } from 'path'
|
||||
import webdriver from 'next-webdriver'
|
||||
import renderingSuite from './rendering'
|
||||
import {
|
||||
waitFor,
|
||||
findPort,
|
||||
killApp,
|
||||
launchApp,
|
||||
fetchViaHTTP,
|
||||
renderViaHTTP,
|
||||
getReactErrorOverlayContent
|
||||
} from 'next-test-utils'
|
||||
|
||||
const context = {}
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
|
||||
|
||||
describe('Client Navigation', () => {
|
||||
beforeAll(async () => {
|
||||
context.appPort = await findPort()
|
||||
context.server = await launchApp(join(__dirname, '../'), context.appPort)
|
||||
|
||||
const prerender = [
|
||||
'/async-props',
|
||||
'/default-head',
|
||||
'/empty-get-initial-props',
|
||||
'/error',
|
||||
'/finish-response',
|
||||
'/head',
|
||||
'/json',
|
||||
'/link',
|
||||
'/stateless',
|
||||
'/fragment-syntax',
|
||||
'/custom-extension',
|
||||
'/styled-jsx',
|
||||
'/with-cdm',
|
||||
'/url-prop',
|
||||
'/url-prop-override',
|
||||
|
||||
'/dynamic/ssr',
|
||||
|
||||
'/nav',
|
||||
'/nav/about',
|
||||
'/nav/on-click',
|
||||
'/nav/querystring',
|
||||
'/nav/self-reload',
|
||||
'/nav/hash-changes',
|
||||
'/nav/shallow-routing',
|
||||
'/nav/redirect',
|
||||
'/nav/as-path',
|
||||
'/nav/as-path-using-router',
|
||||
'/nav/url-prop-change',
|
||||
|
||||
'/nested-cdm/index'
|
||||
]
|
||||
await Promise.all(prerender.map(route => renderViaHTTP(context.appPort, route)))
|
||||
})
|
||||
afterAll(() => killApp(context.server))
|
||||
|
||||
describe('with <Link/>', () => {
|
||||
it('should navigate the page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const text = await browser
|
||||
.elementByCss('#about-link').click()
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should navigate via the client side', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
const counterText = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#about-link').click()
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('#home-link').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.elementByCss('#counter').text()
|
||||
|
||||
expect(counterText).toBe('Counter: 1')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('With url property', () => {
|
||||
it('Should keep immutable pathname, asPath and query', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/url-prop-change')
|
||||
await browser.elementByCss('#add-query').click()
|
||||
const urlResult = await browser.elementByCss('#url-result').text()
|
||||
const previousUrlResult = await browser.elementByCss('#previous-url-result').text()
|
||||
|
||||
expect(JSON.parse(urlResult)).toMatchObject({ 'query': { 'added': 'yes' }, 'pathname': '/nav/url-prop-change', 'asPath': '/nav/url-prop-change?added=yes' })
|
||||
expect(JSON.parse(previousUrlResult)).toMatchObject({ 'query': {}, 'pathname': '/nav/url-prop-change', 'asPath': '/nav/url-prop-change' })
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with <a/> tag inside the <Link />', () => {
|
||||
it('should navigate the page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/about')
|
||||
const text = await browser
|
||||
.elementByCss('#home-link').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the home.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should not navigate if the <a/> tag has a target', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
const counterText = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#target-link').click()
|
||||
.elementByCss('#counter').text()
|
||||
|
||||
expect(counterText).toBe('Counter: 1')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with unexpected <a/> nested tag', () => {
|
||||
it('should not redirect if passHref prop is not defined in Link', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/pass-href-prop')
|
||||
const text = await browser
|
||||
.elementByCss('#without-href').click()
|
||||
.waitForElementByCss('.nav-pass-href-prop')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the passHref prop page.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should redirect if passHref prop is defined in Link', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/pass-href-prop')
|
||||
const text = await browser
|
||||
.elementByCss('#with-href').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the home.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with empty getInitialProps()', () => {
|
||||
it('should render an error', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav')
|
||||
await browser.elementByCss('#empty-props').click()
|
||||
|
||||
await waitFor(3000)
|
||||
|
||||
expect(await getReactErrorOverlayContent(browser)).toMatch(
|
||||
/should resolve to an object\. But found "null" instead\./
|
||||
)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('with the same page but different querystring', () => {
|
||||
it('should navigate the page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/querystring?id=1')
|
||||
const text = await browser
|
||||
.elementByCss('#next-id-link').click()
|
||||
.waitForElementByCss('.nav-id-2')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('2')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should remove querystring', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/querystring?id=1')
|
||||
const text = await browser
|
||||
.elementByCss('#main-page').click()
|
||||
.waitForElementByCss('.nav-id-0')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('0')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with the current url', () => {
|
||||
it('should reload the page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/self-reload')
|
||||
const defaultCount = await browser.elementByCss('p').text()
|
||||
expect(defaultCount).toBe('COUNT: 0')
|
||||
|
||||
const countAfterClicked = await browser
|
||||
.elementByCss('#self-reload-link').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(countAfterClicked).toBe('COUNT: 1')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should always replace the state', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
const countAfterClicked = await browser
|
||||
.elementByCss('#self-reload-link').click()
|
||||
.waitForElementByCss('#self-reload-page')
|
||||
.elementByCss('#self-reload-link').click()
|
||||
.elementByCss('#self-reload-link').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
// counts (page change + two clicks)
|
||||
expect(countAfterClicked).toBe('COUNT: 3')
|
||||
|
||||
// Since we replace the state, back button would simply go us back to /nav
|
||||
await browser
|
||||
.back()
|
||||
.waitForElementByCss('.nav-home')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with onClick action', () => {
|
||||
it('should reload the page and perform additional action', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/on-click')
|
||||
const defaultCountQuery = await browser.elementByCss('#query-count').text()
|
||||
const defaultCountState = await browser.elementByCss('#state-count').text()
|
||||
expect(defaultCountQuery).toBe('QUERY COUNT: 0')
|
||||
expect(defaultCountState).toBe('STATE COUNT: 0')
|
||||
|
||||
await browser.elementByCss('#on-click-link').click()
|
||||
|
||||
const countQueryAfterClicked = await browser.elementByCss('#query-count').text()
|
||||
const countStateAfterClicked = await browser.elementByCss('#state-count').text()
|
||||
expect(countQueryAfterClicked).toBe('QUERY COUNT: 1')
|
||||
expect(countStateAfterClicked).toBe('STATE COUNT: 1')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should not reload if default was prevented', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/on-click')
|
||||
const defaultCountQuery = await browser.elementByCss('#query-count').text()
|
||||
const defaultCountState = await browser.elementByCss('#state-count').text()
|
||||
expect(defaultCountQuery).toBe('QUERY COUNT: 0')
|
||||
expect(defaultCountState).toBe('STATE COUNT: 0')
|
||||
|
||||
await browser.elementByCss('#on-click-link-prevent-default').click()
|
||||
|
||||
const countQueryAfterClicked = await browser.elementByCss('#query-count').text()
|
||||
const countStateAfterClicked = await browser.elementByCss('#state-count').text()
|
||||
expect(countQueryAfterClicked).toBe('QUERY COUNT: 0')
|
||||
expect(countStateAfterClicked).toBe('STATE COUNT: 1')
|
||||
|
||||
await browser.elementByCss('#on-click-link').click()
|
||||
|
||||
const countQueryAfterClickedAgain = await browser.elementByCss('#query-count').text()
|
||||
const countStateAfterClickedAgain = await browser.elementByCss('#state-count').text()
|
||||
expect(countQueryAfterClickedAgain).toBe('QUERY COUNT: 1')
|
||||
expect(countStateAfterClickedAgain).toBe('STATE COUNT: 2')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should always replace the state and perform additional action', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
await browser.elementByCss('#on-click-link').click().waitForElementByCss('#on-click-page')
|
||||
|
||||
const defaultCountQuery = await browser.elementByCss('#query-count').text()
|
||||
expect(defaultCountQuery).toBe('QUERY COUNT: 1')
|
||||
|
||||
await browser.elementByCss('#on-click-link').click()
|
||||
const countQueryAfterClicked = await browser.elementByCss('#query-count').text()
|
||||
const countStateAfterClicked = await browser.elementByCss('#state-count').text()
|
||||
expect(countQueryAfterClicked).toBe('QUERY COUNT: 2')
|
||||
expect(countStateAfterClicked).toBe('STATE COUNT: 1')
|
||||
|
||||
// Since we replace the state, back button would simply go us back to /nav
|
||||
await browser.back().waitForElementByCss('.nav-home')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('with hash changes', () => {
|
||||
describe('when hash change via Link', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-link').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should scroll to the specified position on the same page', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
// Scrolls to item 400 on the page
|
||||
const scrollPosition = await browser
|
||||
.elementByCss('#scroll-to-item-400').click()
|
||||
.eval('window.pageYOffset')
|
||||
|
||||
expect(scrollPosition).toBe(7258)
|
||||
|
||||
// Scrolls back to top when scrolling to `#` with no value.
|
||||
const scrollPositionAfterEmptyHash = await browser
|
||||
.elementByCss('#via-empty-hash').click()
|
||||
.eval('window.pageYOffset')
|
||||
|
||||
expect(scrollPositionAfterEmptyHash).toBe(0)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should scroll to the specified position on the same page with a name property', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
// Scrolls to item 400 with name="name-item-400" on the page
|
||||
const scrollPosition = await browser
|
||||
.elementByCss('#scroll-to-name-item-400').click()
|
||||
.eval('window.pageYOffset')
|
||||
|
||||
console.log(scrollPosition)
|
||||
|
||||
expect(scrollPosition).toBe(16258)
|
||||
|
||||
// Scrolls back to top when scrolling to `#` with no value.
|
||||
const scrollPositionAfterEmptyHash = await browser
|
||||
.elementByCss('#via-empty-hash').click()
|
||||
.eval('window.pageYOffset')
|
||||
|
||||
expect(scrollPositionAfterEmptyHash).toBe(0)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should scroll to the specified position to a new page', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
// Scrolls to item 400 on the page
|
||||
await browser
|
||||
.elementByCss('#scroll-to-hash').click()
|
||||
.waitForElementByCss('#hash-changes-page')
|
||||
|
||||
const scrollPosition = await browser.eval('window.pageYOffset')
|
||||
expect(scrollPosition).toBe(7258)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('when hash change via A tag', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-a').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when hash get removed', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-a').click()
|
||||
.elementByCss('#page-url').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 1')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when hash set to empty', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-a').click()
|
||||
.elementByCss('#via-empty-hash').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('when hash changed to a different hash', () => {
|
||||
it('should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/hash-changes')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#via-a').click()
|
||||
.elementByCss('#via-link').click()
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(counter).toBe('COUNT: 0')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('with shallow routing', () => {
|
||||
it('should update the url without running getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||
const counter = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 2')
|
||||
|
||||
const getInitialPropsRunCount = await browser
|
||||
.elementByCss('#get-initial-props-run-count').text()
|
||||
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 1')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should handle the back button and should not run getInitialProps', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||
let counter = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 2')
|
||||
|
||||
counter = await browser
|
||||
.back()
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 1')
|
||||
|
||||
const getInitialPropsRunCount = await browser
|
||||
.elementByCss('#get-initial-props-run-count').text()
|
||||
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 1')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should run getInitialProps always when rending the page to the screen', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/shallow-routing')
|
||||
|
||||
const counter = await browser
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#increase').click()
|
||||
.elementByCss('#home-link').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.back()
|
||||
.waitForElementByCss('.shallow-routing')
|
||||
.elementByCss('#counter').text()
|
||||
expect(counter).toBe('Counter: 2')
|
||||
|
||||
const getInitialPropsRunCount = await browser
|
||||
.elementByCss('#get-initial-props-run-count').text()
|
||||
expect(getInitialPropsRunCount).toBe('getInitialProps run count: 2')
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with URL objects', () => {
|
||||
it('should work with <Link/>', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const text = await browser
|
||||
.elementByCss('#query-string-link').click()
|
||||
.waitForElementByCss('.nav-querystring')
|
||||
.elementByCss('p').text()
|
||||
expect(text).toBe('10')
|
||||
|
||||
expect(await browser.url())
|
||||
.toBe(`http://localhost:${context.appPort}/nav/querystring/10#10`)
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with "Router.push"', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const text = await browser
|
||||
.elementByCss('#query-string-button').click()
|
||||
.waitForElementByCss('.nav-querystring')
|
||||
.elementByCss('p').text()
|
||||
expect(text).toBe('10')
|
||||
|
||||
expect(await browser.url())
|
||||
.toBe(`http://localhost:${context.appPort}/nav/querystring/10#10`)
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with the "replace" prop', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
|
||||
let stackLength = await browser
|
||||
.eval('window.history.length')
|
||||
|
||||
expect(stackLength).toBe(2)
|
||||
|
||||
// Navigation to /about using a replace link should maintain the url stack length
|
||||
const text = await browser
|
||||
.elementByCss('#about-replace-link').click()
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
|
||||
stackLength = await browser
|
||||
.eval('window.history.length')
|
||||
|
||||
expect(stackLength).toBe(2)
|
||||
|
||||
// Going back to the home with a regular link will augment the history count
|
||||
await browser
|
||||
.elementByCss('#home-link').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
|
||||
stackLength = await browser
|
||||
.eval('window.history.length')
|
||||
|
||||
expect(stackLength).toBe(3)
|
||||
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with getInitialProp redirect', () => {
|
||||
it('should redirect the page via client side', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const text = await browser
|
||||
.elementByCss('#redirect-link').click()
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should redirect the page when loading', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/redirect')
|
||||
const text = await browser
|
||||
.waitForElementByCss('.nav-about')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the about page.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with different types of urls', () => {
|
||||
it('should work with normal page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/with-cdm')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('ComponentDidMount executed on client.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with dir/ page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nested-cdm')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('ComponentDidMount executed on client.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with /index page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/index')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('ComponentDidMount executed on client.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should work with / page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/')
|
||||
const text = await browser.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('ComponentDidMount executed on client.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with the HOC based router', () => {
|
||||
it('should navigate as expected', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/with-hoc')
|
||||
|
||||
const pathname = await browser.elementByCss('#pathname').text()
|
||||
expect(pathname).toBe('Current path: /nav/with-hoc')
|
||||
|
||||
const asPath = await browser.elementByCss('#asPath').text()
|
||||
expect(asPath).toBe('Current asPath: /nav/with-hoc')
|
||||
|
||||
const text = await browser
|
||||
.elementByCss('.nav-with-hoc a').click()
|
||||
.waitForElementByCss('.nav-home')
|
||||
.elementByCss('p').text()
|
||||
|
||||
expect(text).toBe('This is the home.')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with asPath', () => {
|
||||
describe('inside getInitialProps', () => {
|
||||
it('should show the correct asPath with a Link with as prop', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const asPath = await browser
|
||||
.elementByCss('#as-path-link').click()
|
||||
.waitForElementByCss('.as-path-content')
|
||||
.elementByCss('.as-path-content').text()
|
||||
|
||||
expect(asPath).toBe('/as/path')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should show the correct asPath with a Link without the as prop', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const asPath = await browser
|
||||
.elementByCss('#as-path-link-no-as').click()
|
||||
.waitForElementByCss('.as-path-content')
|
||||
.elementByCss('.as-path-content').text()
|
||||
|
||||
expect(asPath).toBe('/nav/as-path')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with next/router', () => {
|
||||
it('should show the correct asPath', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav')
|
||||
const asPath = await browser
|
||||
.elementByCss('#as-path-using-router-link').click()
|
||||
.waitForElementByCss('.as-path-content')
|
||||
.elementByCss('.as-path-content').text()
|
||||
|
||||
expect(asPath).toBe('/nav/as-path-using-router')
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('with next/link', () => {
|
||||
it('should use pushState with same href and different asPath', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/as-path-pushstate')
|
||||
await browser.elementByCss('#hello').click().waitForElementByCss('#something-hello')
|
||||
const queryOne = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryOne.something).toBe('hello')
|
||||
await browser.elementByCss('#same-query').click().waitForElementByCss('#something-same-query')
|
||||
const queryTwo = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryTwo.something).toBe('hello')
|
||||
await browser.back().waitForElementByCss('#something-hello')
|
||||
const queryThree = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryThree.something).toBe('hello')
|
||||
await browser.elementByCss('#else').click().waitForElementByCss('#something-else')
|
||||
await browser.elementByCss('#hello2').click().waitForElementByCss('#nav-as-path-pushstate')
|
||||
await browser.back().waitForElementByCss('#something-else')
|
||||
const queryFour = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryFour.something).toBe(undefined)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should detect asPath query changes correctly', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/as-path-query')
|
||||
await browser.elementByCss('#hello').click().waitForElementByCss('#something-hello-something-hello')
|
||||
const queryOne = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryOne.something).toBe('hello')
|
||||
await browser.elementByCss('#hello2').click().waitForElementByCss('#something-hello-something-else')
|
||||
const queryTwo = JSON.parse(await browser.elementByCss('#router-query').text())
|
||||
expect(queryTwo.something).toBe('else')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('runtime errors', () => {
|
||||
it('should show react-error-overlay when a client side error is thrown inside a component', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/error-inside-browser-page')
|
||||
await waitFor(3000)
|
||||
const text = await getReactErrorOverlayContent(browser)
|
||||
expect(text).toMatch(/An Expected error occurred/)
|
||||
expect(text).toMatch(/pages\/error-inside-browser-page\.js:5/)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should show react-error-overlay when a client side error is thrown outside a component', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/error-in-the-browser-global-scope')
|
||||
await waitFor(3000)
|
||||
const text = await getReactErrorOverlayContent(browser)
|
||||
expect(text).toMatch(/An Expected error occurred/)
|
||||
expect(text).toMatch(/error-in-the-browser-global-scope\.js:2/)
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe('with 404 pages', () => {
|
||||
it('should 404 on not existent page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/non-existent')
|
||||
expect(await browser.elementByCss('h1').text()).toBe('404')
|
||||
expect(await browser.elementByCss('h2').text()).toBe('This page could not be found.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should 404 for <page>/', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nav/about/')
|
||||
expect(await browser.elementByCss('h1').text()).toBe('404')
|
||||
expect(await browser.elementByCss('h2').text()).toBe('This page could not be found.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
it('should should not contain a page script in a 404 page', async () => {
|
||||
const browser = await webdriver(context.appPort, '/non-existent')
|
||||
const scripts = await browser.elementsByCss('script[src]')
|
||||
for (const script of scripts) {
|
||||
const src = await script.getAttribute('src')
|
||||
expect(src.includes('/non-existent')).toBeFalsy()
|
||||
}
|
||||
await browser.close()
|
||||
})
|
||||
})
|
||||
|
||||
describe('updating head while client routing', () => {
|
||||
it('should update head during client routing', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/nav/head-1')
|
||||
expect(await browser.elementByCss('meta[name="description"]').getAttribute('content')).toBe('Head One')
|
||||
await browser.elementByCss('#to-head-2').click().waitForElementByCss('#head-2')
|
||||
expect(await browser.elementByCss('meta[name="description"]').getAttribute('content')).toBe('Head Two')
|
||||
await browser.elementByCss('#to-head-1').click().waitForElementByCss('#head-1')
|
||||
expect(await browser.elementByCss('meta[name="description"]').getAttribute('content')).toBe('Head One')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it('should not error on module.exports + polyfills', async () => {
|
||||
let browser
|
||||
try {
|
||||
browser = await webdriver(context.appPort, '/read-only-object-error')
|
||||
expect(await browser.elementByCss('body').text()).toBe('this is just a placeholder component')
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
it('should work on nested /index/index.js', async () => {
|
||||
const browser = await webdriver(context.appPort, '/nested-index/index')
|
||||
expect(await browser.elementByCss('p').text()).toBe('This is an index.js nested in an index/ folder.')
|
||||
await browser.close()
|
||||
})
|
||||
|
||||
renderingSuite((p, q) => renderViaHTTP(context.appPort, p, q), (p, q) => fetchViaHTTP(context.appPort, p, q))
|
||||
})
|
|
@ -4,13 +4,13 @@ import cheerio from 'cheerio'
|
|||
import { BUILD_MANIFEST, REACT_LOADABLE_MANIFEST } from 'next-server/constants'
|
||||
import { join } from 'path'
|
||||
|
||||
export default function ({ app }, suiteName, render, fetch) {
|
||||
export default function (render, fetch) {
|
||||
async function get$ (path, query) {
|
||||
const html = await render(path, query)
|
||||
return cheerio.load(html)
|
||||
}
|
||||
|
||||
describe(suiteName, () => {
|
||||
describe('Rendering via HTTP', () => {
|
||||
test('renders a stateless component', async () => {
|
||||
const html = await render('/stateless')
|
||||
expect(html.includes('<meta charSet="utf-8" class="next-head"/>')).toBeTruthy()
|
63
yarn.lock
63
yarn.lock
|
@ -777,6 +777,14 @@
|
|||
lodash "^4.17.11"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@jest/types@^24.5.0":
|
||||
version "24.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.5.0.tgz#feee214a4d0167b0ca447284e95a57aa10b3ee95"
|
||||
integrity sha512-kN7RFzNMf2R8UDadPOl6ReyI+MT8xfqRuAnuVL+i4gwjv/zubdDK+EDeLHYwq1j0CSSR2W/MmgaRlMZJzXdmVA==
|
||||
dependencies:
|
||||
"@types/istanbul-lib-coverage" "^1.1.0"
|
||||
"@types/yargs" "^12.0.9"
|
||||
|
||||
"@lerna/add@3.13.0":
|
||||
version "3.13.0"
|
||||
resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.13.0.tgz#e971a17c1f85cba40f22c816a2bb9d855b62d07d"
|
||||
|
@ -1584,6 +1592,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/fresh/-/fresh-0.5.0.tgz#4d09231027d69c4369cfb01a9af5ef083d0d285f"
|
||||
integrity sha512-eGPzuyc6wZM3sSHJdF7NM2jW6B/xsB014Rqg/iDa6xY02mlfy1w/TE2sYhR8vbHxkzJOXiGo6NuIk3xk35vsgQ==
|
||||
|
||||
"@types/istanbul-lib-coverage@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz#2cc2ca41051498382b43157c8227fea60363f94a"
|
||||
integrity sha512-ohkhb9LehJy+PA40rDtGAji61NCgdtKLAlFoYp4cnuuQEswwdK3vz9SOIkkyc3wrk8dzjphQApNs56yyXLStaQ==
|
||||
|
||||
"@types/loader-utils@1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/loader-utils/-/loader-utils-1.1.3.tgz#82b9163f2ead596c68a8c03e450fbd6e089df401"
|
||||
|
@ -1739,6 +1752,11 @@
|
|||
"@types/uglify-js" "*"
|
||||
source-map "^0.6.0"
|
||||
|
||||
"@types/yargs@^12.0.9":
|
||||
version "12.0.10"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.10.tgz#17a8ec65cd8e88f51b418ceb271af18d3137df67"
|
||||
integrity sha512-WsVzTPshvCSbHThUduGGxbmnwcpkgSctHGHTqzWyFg4lYAuV5qXlyFPOsP3OWqCINfmg/8VXP+zJaa4OxEsBQQ==
|
||||
|
||||
"@webassemblyjs/ast@1.7.11":
|
||||
version "1.7.11"
|
||||
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace"
|
||||
|
@ -7129,6 +7147,11 @@ jest-get-type@^22.1.0:
|
|||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4"
|
||||
integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==
|
||||
|
||||
jest-get-type@^24.3.0:
|
||||
version "24.3.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.3.0.tgz#582cfd1a4f91b5cdad1d43d2932f816d543c65da"
|
||||
integrity sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow==
|
||||
|
||||
jest-haste-map@^23.6.0:
|
||||
version "23.6.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.6.0.tgz#2e3eb997814ca696d62afdb3f2529f5bbc935e16"
|
||||
|
@ -7161,13 +7184,12 @@ jest-jasmine2@^23.6.0:
|
|||
jest-util "^23.4.0"
|
||||
pretty-format "^23.6.0"
|
||||
|
||||
jest-junit@^5.0.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-5.2.0.tgz#980401db7aa69999cf117c6d740a8135c22ae379"
|
||||
integrity sha512-Mdg0Qpdh1Xm/FA1B/mcLlmEmlr3XzH5pZg7MvcAwZhjHijPRd1z/UwYwkwNHmCV7o4ZOWCf77nLu7ZkhHHrtJg==
|
||||
jest-junit@6.3.0:
|
||||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-6.3.0.tgz#99e64ebc54eddcb21238f0cc49f5820c89a8c785"
|
||||
integrity sha512-3PH9UkpaomX6CUzqjlnk0m4yBCW/eroxV6v61OM6LkCQFO848P3YUhfIzu8ypZSBKB3vvCbB4WaLTKT0BrIf8A==
|
||||
dependencies:
|
||||
jest-config "^23.6.0"
|
||||
jest-validate "^23.0.1"
|
||||
jest-validate "^24.0.0"
|
||||
mkdirp "^0.5.1"
|
||||
strip-ansi "^4.0.0"
|
||||
xml "^1.0.1"
|
||||
|
@ -7317,7 +7339,7 @@ jest-validate@^21.1.0:
|
|||
leven "^2.1.0"
|
||||
pretty-format "^21.2.1"
|
||||
|
||||
jest-validate@^23.0.1, jest-validate@^23.6.0:
|
||||
jest-validate@^23.6.0:
|
||||
version "23.6.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.6.0.tgz#36761f99d1ed33fcd425b4e4c5595d62b6597474"
|
||||
integrity sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==
|
||||
|
@ -7327,6 +7349,18 @@ jest-validate@^23.0.1, jest-validate@^23.6.0:
|
|||
leven "^2.1.0"
|
||||
pretty-format "^23.6.0"
|
||||
|
||||
jest-validate@^24.0.0:
|
||||
version "24.5.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.5.0.tgz#62fd93d81214c070bb2d7a55f329a79d8057c7de"
|
||||
integrity sha512-gg0dYszxjgK2o11unSIJhkOFZqNRQbWOAB2/LOUdsd2LfD9oXiMeuee8XsT0iRy5EvSccBgB4h/9HRbIo3MHgQ==
|
||||
dependencies:
|
||||
"@jest/types" "^24.5.0"
|
||||
camelcase "^5.0.0"
|
||||
chalk "^2.0.1"
|
||||
jest-get-type "^24.3.0"
|
||||
leven "^2.1.0"
|
||||
pretty-format "^24.5.0"
|
||||
|
||||
jest-watcher@^23.4.0:
|
||||
version "23.4.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.4.0.tgz#d2e28ce74f8dad6c6afc922b92cabef6ed05c91c"
|
||||
|
@ -9909,6 +9943,16 @@ pretty-format@^23.6.0:
|
|||
ansi-regex "^3.0.0"
|
||||
ansi-styles "^3.2.0"
|
||||
|
||||
pretty-format@^24.5.0:
|
||||
version "24.5.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.5.0.tgz#cc69a0281a62cd7242633fc135d6930cd889822d"
|
||||
integrity sha512-/3RuSghukCf8Riu5Ncve0iI+BzVkbRU5EeUoArKARZobREycuH5O4waxvaNIloEXdb0qwgmEAed5vTpX1HNROQ==
|
||||
dependencies:
|
||||
"@jest/types" "^24.5.0"
|
||||
ansi-regex "^4.0.0"
|
||||
ansi-styles "^3.2.0"
|
||||
react-is "^16.8.4"
|
||||
|
||||
private@^0.1.6, private@^0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
|
||||
|
@ -10218,6 +10262,11 @@ react-is@^16.3.2, react-is@^16.8.1:
|
|||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.3.tgz#4ad8b029c2a718fc0cfc746c8d4e1b7221e5387d"
|
||||
integrity sha512-Y4rC1ZJmsxxkkPuMLwvKvlL1Zfpbcu+Bf4ZigkHup3v9EfdYhAlWAaVyA19olXq2o2mGn0w+dFKvk3pVVlYcIA==
|
||||
|
||||
react-is@^16.8.4:
|
||||
version "16.8.4"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.4.tgz#90f336a68c3a29a096a3d648ab80e87ec61482a2"
|
||||
integrity sha512-PVadd+WaUDOAciICm/J1waJaSvgq+4rHE/K70j0PFqKhkTBsPv/82UGQJNXAngz1fOQLLxI6z1sEDmJDQhCTAA==
|
||||
|
||||
react@16.8.0:
|
||||
version "16.8.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.8.0.tgz#8533f0e4af818f448a276eae71681d09e8dd970a"
|
||||
|
|
Loading…
Reference in a new issue