From 7d0919a7848e34f6d68ee97fcc61f7487472e666 Mon Sep 17 00:00:00 2001
From: JJ Kasper <22380829+ijjk@users.noreply.github.com>
Date: Wed, 20 Mar 2019 05:01:32 -0500
Subject: [PATCH] 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
---
.circleci/config.yml | 5 +-
package.json | 4 +-
.../basic/test/client-navigation.js | 776 ----------------
test/integration/basic/test/index.test.js | 34 -
.../components/hello.jsx | 0
.../client-navigation/components/hello1.js | 3 +
.../components/world.jsx | 0
.../lib/async-function.js | 0
.../{basic => client-navigation}/lib/cdm.js | 0
.../lib/data.json | 0
.../client-navigation/next.config.js | 6 +
.../pages/async-props.js | 0
.../pages/circular-json-error.js | 0
.../pages/custom-encoding.js | 0
.../pages/custom-extension.jsx | 0
.../pages/default-head.js | 0
.../client-navigation/pages/dynamic/ssr.js | 5 +
.../pages/empty-get-initial-props.js | 0
.../error-in-the-browser-global-scope.js | 0
.../pages/error-in-the-global-scope.js | 0
.../pages/error-inside-browser-page.js | 0
.../pages/error-inside-page.js | 0
.../pages/exports.js | 0
.../pages/forwardRef-component.js | 0
.../pages/fragment-syntax.js | 0
.../pages/head.js | 0
.../pages/index.js | 0
.../pages/instance-get-initial-props.js | 0
.../pages/json.js | 0
.../pages/link.js | 0
.../pages/memo-component.js | 0
.../pages/nav/about.js | 0
.../pages/nav/as-path-pushstate.js | 0
.../pages/nav/as-path-query.js | 0
.../pages/nav/as-path-using-router.js | 0
.../pages/nav/as-path.js | 0
.../pages/nav/hash-changes.js | 0
.../pages/nav/head-1.js | 0
.../pages/nav/head-2.js | 0
.../pages/nav/index.js | 0
.../pages/nav/on-click.js | 0
.../pages/nav/pass-href-prop.js | 0
.../pages/nav/querystring.js | 0
.../pages/nav/redirect.js | 0
.../pages/nav/self-reload.js | 0
.../pages/nav/shallow-routing.js | 0
.../pages/nav/url-prop-change.js | 0
.../pages/nav/with-hoc.js | 0
.../pages/nested-cdm/index.js | 0
.../pages/nested-index/index/index.js | 0
.../pages/no-default-export.js | 0
.../pages/read-only-object-error.js | 0
.../pages/stateless.js | 0
.../pages/styled-jsx.js | 0
.../pages/throw-undefined.js | 2 +-
.../pages/url-prop-override.js | 0
.../pages/url-prop.js | 0
.../pages/with-cdm.js | 0
.../client-navigation/test/index.test.js | 830 ++++++++++++++++++
.../test/rendering.js | 4 +-
yarn.lock | 63 +-
61 files changed, 908 insertions(+), 824 deletions(-)
delete mode 100644 test/integration/basic/test/client-navigation.js
rename test/integration/{basic => client-navigation}/components/hello.jsx (100%)
create mode 100644 test/integration/client-navigation/components/hello1.js
rename test/integration/{basic => client-navigation}/components/world.jsx (100%)
rename test/integration/{basic => client-navigation}/lib/async-function.js (100%)
rename test/integration/{basic => client-navigation}/lib/cdm.js (100%)
rename test/integration/{basic => client-navigation}/lib/data.json (100%)
create mode 100644 test/integration/client-navigation/next.config.js
rename test/integration/{basic => client-navigation}/pages/async-props.js (100%)
rename test/integration/{basic => client-navigation}/pages/circular-json-error.js (100%)
rename test/integration/{basic => client-navigation}/pages/custom-encoding.js (100%)
rename test/integration/{basic => client-navigation}/pages/custom-extension.jsx (100%)
rename test/integration/{basic => client-navigation}/pages/default-head.js (100%)
create mode 100644 test/integration/client-navigation/pages/dynamic/ssr.js
rename test/integration/{basic => client-navigation}/pages/empty-get-initial-props.js (100%)
rename test/integration/{basic => client-navigation}/pages/error-in-the-browser-global-scope.js (100%)
rename test/integration/{basic => client-navigation}/pages/error-in-the-global-scope.js (100%)
rename test/integration/{basic => client-navigation}/pages/error-inside-browser-page.js (100%)
rename test/integration/{basic => client-navigation}/pages/error-inside-page.js (100%)
rename test/integration/{basic => client-navigation}/pages/exports.js (100%)
rename test/integration/{basic => client-navigation}/pages/forwardRef-component.js (100%)
rename test/integration/{basic => client-navigation}/pages/fragment-syntax.js (100%)
rename test/integration/{basic => client-navigation}/pages/head.js (100%)
rename test/integration/{basic => client-navigation}/pages/index.js (100%)
rename test/integration/{basic => client-navigation}/pages/instance-get-initial-props.js (100%)
rename test/integration/{basic => client-navigation}/pages/json.js (100%)
rename test/integration/{basic => client-navigation}/pages/link.js (100%)
rename test/integration/{basic => client-navigation}/pages/memo-component.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/about.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/as-path-pushstate.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/as-path-query.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/as-path-using-router.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/as-path.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/hash-changes.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/head-1.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/head-2.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/index.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/on-click.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/pass-href-prop.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/querystring.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/redirect.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/self-reload.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/shallow-routing.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/url-prop-change.js (100%)
rename test/integration/{basic => client-navigation}/pages/nav/with-hoc.js (100%)
rename test/integration/{basic => client-navigation}/pages/nested-cdm/index.js (100%)
rename test/integration/{basic => client-navigation}/pages/nested-index/index/index.js (100%)
rename test/integration/{basic => client-navigation}/pages/no-default-export.js (100%)
rename test/integration/{basic => client-navigation}/pages/read-only-object-error.js (100%)
rename test/integration/{basic => client-navigation}/pages/stateless.js (100%)
rename test/integration/{basic => client-navigation}/pages/styled-jsx.js (100%)
rename test/integration/{basic => client-navigation}/pages/throw-undefined.js (82%)
rename test/integration/{basic => client-navigation}/pages/url-prop-override.js (100%)
rename test/integration/{basic => client-navigation}/pages/url-prop.js (100%)
rename test/integration/{basic => client-navigation}/pages/with-cdm.js (100%)
create mode 100644 test/integration/client-navigation/test/index.test.js
rename test/integration/{basic => client-navigation}/test/rendering.js (99%)
diff --git a/.circleci/config.yml b/.circleci/config.yml
index a1c557a9da..1522ea8f21 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -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:
diff --git a/package.json b/package.json
index b10e7c8367..b70007a883 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/test/integration/basic/test/client-navigation.js b/test/integration/basic/test/client-navigation.js
deleted file mode 100644
index c0584d449e..0000000000
--- a/test/integration/basic/test/client-navigation.js
+++ /dev/null
@@ -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 ', () => {
- 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 tag inside the ', () => {
- 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 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 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 ', 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 /', 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()
- })
- })
-}
diff --git a/test/integration/basic/test/index.test.js b/test/integration/basic/test/index.test.js
index 0c01551989..7db1cd4cec 100644
--- a/test/integration/basic/test/index.test.js
+++ b/test/integration/basic/test/index.test.js
@@ -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))
diff --git a/test/integration/basic/components/hello.jsx b/test/integration/client-navigation/components/hello.jsx
similarity index 100%
rename from test/integration/basic/components/hello.jsx
rename to test/integration/client-navigation/components/hello.jsx
diff --git a/test/integration/client-navigation/components/hello1.js b/test/integration/client-navigation/components/hello1.js
new file mode 100644
index 0000000000..a896f64f5c
--- /dev/null
+++ b/test/integration/client-navigation/components/hello1.js
@@ -0,0 +1,3 @@
+export default () => (
+ Hello World 1
+)
diff --git a/test/integration/basic/components/world.jsx b/test/integration/client-navigation/components/world.jsx
similarity index 100%
rename from test/integration/basic/components/world.jsx
rename to test/integration/client-navigation/components/world.jsx
diff --git a/test/integration/basic/lib/async-function.js b/test/integration/client-navigation/lib/async-function.js
similarity index 100%
rename from test/integration/basic/lib/async-function.js
rename to test/integration/client-navigation/lib/async-function.js
diff --git a/test/integration/basic/lib/cdm.js b/test/integration/client-navigation/lib/cdm.js
similarity index 100%
rename from test/integration/basic/lib/cdm.js
rename to test/integration/client-navigation/lib/cdm.js
diff --git a/test/integration/basic/lib/data.json b/test/integration/client-navigation/lib/data.json
similarity index 100%
rename from test/integration/basic/lib/data.json
rename to test/integration/client-navigation/lib/data.json
diff --git a/test/integration/client-navigation/next.config.js b/test/integration/client-navigation/next.config.js
new file mode 100644
index 0000000000..35dcf0f6b8
--- /dev/null
+++ b/test/integration/client-navigation/next.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+ onDemandEntries: {
+ // Make sure entries are not getting disposed.
+ maxInactiveAge: 1000 * 60 * 60
+ }
+}
diff --git a/test/integration/basic/pages/async-props.js b/test/integration/client-navigation/pages/async-props.js
similarity index 100%
rename from test/integration/basic/pages/async-props.js
rename to test/integration/client-navigation/pages/async-props.js
diff --git a/test/integration/basic/pages/circular-json-error.js b/test/integration/client-navigation/pages/circular-json-error.js
similarity index 100%
rename from test/integration/basic/pages/circular-json-error.js
rename to test/integration/client-navigation/pages/circular-json-error.js
diff --git a/test/integration/basic/pages/custom-encoding.js b/test/integration/client-navigation/pages/custom-encoding.js
similarity index 100%
rename from test/integration/basic/pages/custom-encoding.js
rename to test/integration/client-navigation/pages/custom-encoding.js
diff --git a/test/integration/basic/pages/custom-extension.jsx b/test/integration/client-navigation/pages/custom-extension.jsx
similarity index 100%
rename from test/integration/basic/pages/custom-extension.jsx
rename to test/integration/client-navigation/pages/custom-extension.jsx
diff --git a/test/integration/basic/pages/default-head.js b/test/integration/client-navigation/pages/default-head.js
similarity index 100%
rename from test/integration/basic/pages/default-head.js
rename to test/integration/client-navigation/pages/default-head.js
diff --git a/test/integration/client-navigation/pages/dynamic/ssr.js b/test/integration/client-navigation/pages/dynamic/ssr.js
new file mode 100644
index 0000000000..6bec25ce24
--- /dev/null
+++ b/test/integration/client-navigation/pages/dynamic/ssr.js
@@ -0,0 +1,5 @@
+import dynamic from 'next/dynamic'
+
+const Hello = dynamic(import('../../components/hello1'))
+
+export default Hello
diff --git a/test/integration/basic/pages/empty-get-initial-props.js b/test/integration/client-navigation/pages/empty-get-initial-props.js
similarity index 100%
rename from test/integration/basic/pages/empty-get-initial-props.js
rename to test/integration/client-navigation/pages/empty-get-initial-props.js
diff --git a/test/integration/basic/pages/error-in-the-browser-global-scope.js b/test/integration/client-navigation/pages/error-in-the-browser-global-scope.js
similarity index 100%
rename from test/integration/basic/pages/error-in-the-browser-global-scope.js
rename to test/integration/client-navigation/pages/error-in-the-browser-global-scope.js
diff --git a/test/integration/basic/pages/error-in-the-global-scope.js b/test/integration/client-navigation/pages/error-in-the-global-scope.js
similarity index 100%
rename from test/integration/basic/pages/error-in-the-global-scope.js
rename to test/integration/client-navigation/pages/error-in-the-global-scope.js
diff --git a/test/integration/basic/pages/error-inside-browser-page.js b/test/integration/client-navigation/pages/error-inside-browser-page.js
similarity index 100%
rename from test/integration/basic/pages/error-inside-browser-page.js
rename to test/integration/client-navigation/pages/error-inside-browser-page.js
diff --git a/test/integration/basic/pages/error-inside-page.js b/test/integration/client-navigation/pages/error-inside-page.js
similarity index 100%
rename from test/integration/basic/pages/error-inside-page.js
rename to test/integration/client-navigation/pages/error-inside-page.js
diff --git a/test/integration/basic/pages/exports.js b/test/integration/client-navigation/pages/exports.js
similarity index 100%
rename from test/integration/basic/pages/exports.js
rename to test/integration/client-navigation/pages/exports.js
diff --git a/test/integration/basic/pages/forwardRef-component.js b/test/integration/client-navigation/pages/forwardRef-component.js
similarity index 100%
rename from test/integration/basic/pages/forwardRef-component.js
rename to test/integration/client-navigation/pages/forwardRef-component.js
diff --git a/test/integration/basic/pages/fragment-syntax.js b/test/integration/client-navigation/pages/fragment-syntax.js
similarity index 100%
rename from test/integration/basic/pages/fragment-syntax.js
rename to test/integration/client-navigation/pages/fragment-syntax.js
diff --git a/test/integration/basic/pages/head.js b/test/integration/client-navigation/pages/head.js
similarity index 100%
rename from test/integration/basic/pages/head.js
rename to test/integration/client-navigation/pages/head.js
diff --git a/test/integration/basic/pages/index.js b/test/integration/client-navigation/pages/index.js
similarity index 100%
rename from test/integration/basic/pages/index.js
rename to test/integration/client-navigation/pages/index.js
diff --git a/test/integration/basic/pages/instance-get-initial-props.js b/test/integration/client-navigation/pages/instance-get-initial-props.js
similarity index 100%
rename from test/integration/basic/pages/instance-get-initial-props.js
rename to test/integration/client-navigation/pages/instance-get-initial-props.js
diff --git a/test/integration/basic/pages/json.js b/test/integration/client-navigation/pages/json.js
similarity index 100%
rename from test/integration/basic/pages/json.js
rename to test/integration/client-navigation/pages/json.js
diff --git a/test/integration/basic/pages/link.js b/test/integration/client-navigation/pages/link.js
similarity index 100%
rename from test/integration/basic/pages/link.js
rename to test/integration/client-navigation/pages/link.js
diff --git a/test/integration/basic/pages/memo-component.js b/test/integration/client-navigation/pages/memo-component.js
similarity index 100%
rename from test/integration/basic/pages/memo-component.js
rename to test/integration/client-navigation/pages/memo-component.js
diff --git a/test/integration/basic/pages/nav/about.js b/test/integration/client-navigation/pages/nav/about.js
similarity index 100%
rename from test/integration/basic/pages/nav/about.js
rename to test/integration/client-navigation/pages/nav/about.js
diff --git a/test/integration/basic/pages/nav/as-path-pushstate.js b/test/integration/client-navigation/pages/nav/as-path-pushstate.js
similarity index 100%
rename from test/integration/basic/pages/nav/as-path-pushstate.js
rename to test/integration/client-navigation/pages/nav/as-path-pushstate.js
diff --git a/test/integration/basic/pages/nav/as-path-query.js b/test/integration/client-navigation/pages/nav/as-path-query.js
similarity index 100%
rename from test/integration/basic/pages/nav/as-path-query.js
rename to test/integration/client-navigation/pages/nav/as-path-query.js
diff --git a/test/integration/basic/pages/nav/as-path-using-router.js b/test/integration/client-navigation/pages/nav/as-path-using-router.js
similarity index 100%
rename from test/integration/basic/pages/nav/as-path-using-router.js
rename to test/integration/client-navigation/pages/nav/as-path-using-router.js
diff --git a/test/integration/basic/pages/nav/as-path.js b/test/integration/client-navigation/pages/nav/as-path.js
similarity index 100%
rename from test/integration/basic/pages/nav/as-path.js
rename to test/integration/client-navigation/pages/nav/as-path.js
diff --git a/test/integration/basic/pages/nav/hash-changes.js b/test/integration/client-navigation/pages/nav/hash-changes.js
similarity index 100%
rename from test/integration/basic/pages/nav/hash-changes.js
rename to test/integration/client-navigation/pages/nav/hash-changes.js
diff --git a/test/integration/basic/pages/nav/head-1.js b/test/integration/client-navigation/pages/nav/head-1.js
similarity index 100%
rename from test/integration/basic/pages/nav/head-1.js
rename to test/integration/client-navigation/pages/nav/head-1.js
diff --git a/test/integration/basic/pages/nav/head-2.js b/test/integration/client-navigation/pages/nav/head-2.js
similarity index 100%
rename from test/integration/basic/pages/nav/head-2.js
rename to test/integration/client-navigation/pages/nav/head-2.js
diff --git a/test/integration/basic/pages/nav/index.js b/test/integration/client-navigation/pages/nav/index.js
similarity index 100%
rename from test/integration/basic/pages/nav/index.js
rename to test/integration/client-navigation/pages/nav/index.js
diff --git a/test/integration/basic/pages/nav/on-click.js b/test/integration/client-navigation/pages/nav/on-click.js
similarity index 100%
rename from test/integration/basic/pages/nav/on-click.js
rename to test/integration/client-navigation/pages/nav/on-click.js
diff --git a/test/integration/basic/pages/nav/pass-href-prop.js b/test/integration/client-navigation/pages/nav/pass-href-prop.js
similarity index 100%
rename from test/integration/basic/pages/nav/pass-href-prop.js
rename to test/integration/client-navigation/pages/nav/pass-href-prop.js
diff --git a/test/integration/basic/pages/nav/querystring.js b/test/integration/client-navigation/pages/nav/querystring.js
similarity index 100%
rename from test/integration/basic/pages/nav/querystring.js
rename to test/integration/client-navigation/pages/nav/querystring.js
diff --git a/test/integration/basic/pages/nav/redirect.js b/test/integration/client-navigation/pages/nav/redirect.js
similarity index 100%
rename from test/integration/basic/pages/nav/redirect.js
rename to test/integration/client-navigation/pages/nav/redirect.js
diff --git a/test/integration/basic/pages/nav/self-reload.js b/test/integration/client-navigation/pages/nav/self-reload.js
similarity index 100%
rename from test/integration/basic/pages/nav/self-reload.js
rename to test/integration/client-navigation/pages/nav/self-reload.js
diff --git a/test/integration/basic/pages/nav/shallow-routing.js b/test/integration/client-navigation/pages/nav/shallow-routing.js
similarity index 100%
rename from test/integration/basic/pages/nav/shallow-routing.js
rename to test/integration/client-navigation/pages/nav/shallow-routing.js
diff --git a/test/integration/basic/pages/nav/url-prop-change.js b/test/integration/client-navigation/pages/nav/url-prop-change.js
similarity index 100%
rename from test/integration/basic/pages/nav/url-prop-change.js
rename to test/integration/client-navigation/pages/nav/url-prop-change.js
diff --git a/test/integration/basic/pages/nav/with-hoc.js b/test/integration/client-navigation/pages/nav/with-hoc.js
similarity index 100%
rename from test/integration/basic/pages/nav/with-hoc.js
rename to test/integration/client-navigation/pages/nav/with-hoc.js
diff --git a/test/integration/basic/pages/nested-cdm/index.js b/test/integration/client-navigation/pages/nested-cdm/index.js
similarity index 100%
rename from test/integration/basic/pages/nested-cdm/index.js
rename to test/integration/client-navigation/pages/nested-cdm/index.js
diff --git a/test/integration/basic/pages/nested-index/index/index.js b/test/integration/client-navigation/pages/nested-index/index/index.js
similarity index 100%
rename from test/integration/basic/pages/nested-index/index/index.js
rename to test/integration/client-navigation/pages/nested-index/index/index.js
diff --git a/test/integration/basic/pages/no-default-export.js b/test/integration/client-navigation/pages/no-default-export.js
similarity index 100%
rename from test/integration/basic/pages/no-default-export.js
rename to test/integration/client-navigation/pages/no-default-export.js
diff --git a/test/integration/basic/pages/read-only-object-error.js b/test/integration/client-navigation/pages/read-only-object-error.js
similarity index 100%
rename from test/integration/basic/pages/read-only-object-error.js
rename to test/integration/client-navigation/pages/read-only-object-error.js
diff --git a/test/integration/basic/pages/stateless.js b/test/integration/client-navigation/pages/stateless.js
similarity index 100%
rename from test/integration/basic/pages/stateless.js
rename to test/integration/client-navigation/pages/stateless.js
diff --git a/test/integration/basic/pages/styled-jsx.js b/test/integration/client-navigation/pages/styled-jsx.js
similarity index 100%
rename from test/integration/basic/pages/styled-jsx.js
rename to test/integration/client-navigation/pages/styled-jsx.js
diff --git a/test/integration/basic/pages/throw-undefined.js b/test/integration/client-navigation/pages/throw-undefined.js
similarity index 82%
rename from test/integration/basic/pages/throw-undefined.js
rename to test/integration/client-navigation/pages/throw-undefined.js
index 6309859fb7..b129f35a15 100644
--- a/test/integration/basic/pages/throw-undefined.js
+++ b/test/integration/client-navigation/pages/throw-undefined.js
@@ -8,4 +8,4 @@ ThrowUndefined.getInitialProps = () => {
throw undefined
}
-export default ThrowUndefined
\ No newline at end of file
+export default ThrowUndefined
diff --git a/test/integration/basic/pages/url-prop-override.js b/test/integration/client-navigation/pages/url-prop-override.js
similarity index 100%
rename from test/integration/basic/pages/url-prop-override.js
rename to test/integration/client-navigation/pages/url-prop-override.js
diff --git a/test/integration/basic/pages/url-prop.js b/test/integration/client-navigation/pages/url-prop.js
similarity index 100%
rename from test/integration/basic/pages/url-prop.js
rename to test/integration/client-navigation/pages/url-prop.js
diff --git a/test/integration/basic/pages/with-cdm.js b/test/integration/client-navigation/pages/with-cdm.js
similarity index 100%
rename from test/integration/basic/pages/with-cdm.js
rename to test/integration/client-navigation/pages/with-cdm.js
diff --git a/test/integration/client-navigation/test/index.test.js b/test/integration/client-navigation/test/index.test.js
new file mode 100644
index 0000000000..6ec4ffab08
--- /dev/null
+++ b/test/integration/client-navigation/test/index.test.js
@@ -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 ', () => {
+ 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 tag inside the ', () => {
+ 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 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 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 ', 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 /', 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))
+})
diff --git a/test/integration/basic/test/rendering.js b/test/integration/client-navigation/test/rendering.js
similarity index 99%
rename from test/integration/basic/test/rendering.js
rename to test/integration/client-navigation/test/rendering.js
index 65f9a9130b..0d2874f1fc 100644
--- a/test/integration/basic/test/rendering.js
+++ b/test/integration/client-navigation/test/rendering.js
@@ -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('')).toBeTruthy()
diff --git a/yarn.lock b/yarn.lock
index d2bca03378..a73eabbb2e 100644
--- a/yarn.lock
+++ b/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"
--
GitLab