提交 7d0919a7 编写于 作者: J JJ Kasper 提交者: Tim Neutkens

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
上级 2c975b1c
......@@ -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",
......
/* 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))
......
export default () => (
<p>Hello World 1</p>
)
module.exports = {
onDemandEntries: {
// Make sure entries are not getting disposed.
maxInactiveAge: 1000 * 60 * 60
}
}
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
\ No newline at end of file
export default ThrowUndefined
/* 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()
......
......@@ -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"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册