mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
Merge branch 'canary' into public-env
This commit is contained in:
commit
ea828dbec5
|
@ -765,7 +765,7 @@ class MyLink extends React.Component {
|
||||||
const { router } = this.props
|
const { router } = this.props
|
||||||
router.prefetch('/dynamic')
|
router.prefetch('/dynamic')
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { router } = this.props
|
const { router } = this.props
|
||||||
return (
|
return (
|
||||||
|
@ -773,7 +773,7 @@ class MyLink extends React.Component {
|
||||||
<a onClick={() => setTimeout(() => router.push('/dynamic'), 100)}>
|
<a onClick={() => setTimeout(() => router.push('/dynamic'), 100)}>
|
||||||
A route transition will happen after 100ms
|
A route transition will happen after 100ms
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1343,7 +1343,7 @@ module.exports = {
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Example next.config.js for adding a loader that depends on babel-loader
|
// Example next.config.js for adding a loader that depends on babel-loader
|
||||||
// This source was taken from the @zeit/next-mdx plugin source:
|
// This source was taken from the @zeit/next-mdx plugin source:
|
||||||
// https://github.com/zeit/next-plugins/blob/master/packages/next-mdx
|
// https://github.com/zeit/next-plugins/blob/master/packages/next-mdx
|
||||||
module.exports = {
|
module.exports = {
|
||||||
webpack: (config, {}) => {
|
webpack: (config, {}) => {
|
||||||
|
@ -1464,7 +1464,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
注意:Next.js 运行时将会自动添加前缀,但是对于`/static`是没有效果的,如果你想这些静态资源也能使用 CDN,你需要自己添加前缀。有一个方法可以判断你的环境来加前缀,如 [in this example](https://github.com/zeit/next.js/tree/master/examples/with-universal-configuration)。
|
注意:Next.js 运行时将会自动添加前缀,但是对于`/static`是没有效果的,如果你想这些静态资源也能使用 CDN,你需要自己添加前缀。有一个方法可以判断你的环境来加前缀,如 [in this example](https://github.com/zeit/next.js/tree/master/examples/with-universal-configuration-build-time)。
|
||||||
|
|
||||||
<a id="production-deployment" style="display: none"></a>
|
<a id="production-deployment" style="display: none"></a>
|
||||||
## 项目部署
|
## 项目部署
|
||||||
|
|
|
@ -41,4 +41,4 @@ now
|
||||||
|
|
||||||
This example shows how to import images, videos, etc. from `/static` and get the URL with a hash query allowing to use better cache without problems.
|
This example shows how to import images, videos, etc. from `/static` and get the URL with a hash query allowing to use better cache without problems.
|
||||||
|
|
||||||
This example supports `.svg`, `.png` and `.txt` extensions, but it can be configured to support any possible extension changing the `extensions` array in the `.babelrc` file.
|
This example supports `.svg`, `.png` and `.txt` extensions, but it can be configured to support any possible extension changing the `extensions` array in the `next.config.js` [file](https://github.com/zeit/next.js/blob/canary/examples/with-hashed-statics/next.config.js#L4).
|
||||||
|
|
23
examples/with-sentry/next.config.js
Normal file
23
examples/with-sentry/next.config.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
const webpack = require('webpack')
|
||||||
|
const nextSourceMaps = require('@zeit/next-source-maps')()
|
||||||
|
|
||||||
|
const SENTRY_DSN = ''
|
||||||
|
|
||||||
|
module.exports = nextSourceMaps({
|
||||||
|
webpack: (config, { dev, isServer, buildId }) => {
|
||||||
|
if (!dev) {
|
||||||
|
config.plugins.push(
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env.SENTRY_DSN': JSON.stringify(SENTRY_DSN),
|
||||||
|
'process.env.SENTRY_RELEASE': JSON.stringify(buildId)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isServer) {
|
||||||
|
config.resolve.alias['@sentry/node'] = '@sentry/browser'
|
||||||
|
}
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
})
|
|
@ -1,23 +1,48 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import Link from 'next/link'
|
||||||
|
|
||||||
class Index extends React.Component {
|
class Index extends React.Component {
|
||||||
|
static getInitialProps ({ query, req }) {
|
||||||
|
if (query.raiseError) {
|
||||||
|
throw new Error('Error in getInitialProps')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
raiseError: false
|
raiseError: false
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate () {
|
componentDidUpdate () {
|
||||||
if (this.state.raiseError) {
|
if (this.state.raiseErrorInUpdate) {
|
||||||
throw new Error('Houston, we have a problem')
|
throw new Error('Error in componentDidUpdate')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
raiseError = () => this.setState({ raiseError: true })
|
raiseErrorInUpdate = () => this.setState({ raiseErrorInUpdate: '1' })
|
||||||
|
raiseErrorInRender = () => this.setState({ raiseErrorInRender: '1' })
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
|
if (this.state.raiseErrorInRender) {
|
||||||
|
throw new Error('Error in render')
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>Index page</h2>
|
<h2>Index page</h2>
|
||||||
<button onClick={this.raiseError}>Click to raise the error</button>
|
<ul>
|
||||||
|
<li><a href='#' onClick={this.raiseErrorInRender}>Raise the error in render</a></li>
|
||||||
|
<li><a href='#' onClick={this.raiseErrorInUpdate}>Raise the error in componentDidUpdate</a></li>
|
||||||
|
<li>
|
||||||
|
<Link href={{ pathname: '/', query: { raiseError: '1' } }}>
|
||||||
|
<a>Raise error in getInitialProps of client-loaded page</a>
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href='/?raiseError=1'>
|
||||||
|
Raise error in getInitialProps of server-loaded page
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
66
examples/with-sentry/server.js
Normal file
66
examples/with-sentry/server.js
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
const next = require('next')
|
||||||
|
const express = require('express')
|
||||||
|
const cookieParser = require('cookie-parser')
|
||||||
|
const Sentry = require('@sentry/node')
|
||||||
|
const uuidv4 = require('uuid/v4')
|
||||||
|
const port = parseInt(process.env.PORT, 10) || 3000
|
||||||
|
const dev = process.env.NODE_ENV !== 'production'
|
||||||
|
const app = next({ dev })
|
||||||
|
const handle = app.getRequestHandler()
|
||||||
|
|
||||||
|
require('./utils/sentry')
|
||||||
|
|
||||||
|
app.prepare()
|
||||||
|
.then(() => {
|
||||||
|
const server = express()
|
||||||
|
|
||||||
|
// This attaches request information to sentry errors
|
||||||
|
server.use(Sentry.Handlers.requestHandler())
|
||||||
|
|
||||||
|
server.use(cookieParser())
|
||||||
|
|
||||||
|
server.use((req, res, next) => {
|
||||||
|
const htmlPage =
|
||||||
|
!req.path.match(/^\/(_next|static)/) &&
|
||||||
|
!req.path.match(/\.(js|map)$/) &&
|
||||||
|
req.accepts('text/html', 'text/css', 'image/png') === 'text/html'
|
||||||
|
|
||||||
|
if (!htmlPage) {
|
||||||
|
next()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!req.cookies.sid || req.cookies.sid.length === 0) {
|
||||||
|
req.cookies.sid = uuidv4()
|
||||||
|
res.cookie('sid', req.cookies.sid)
|
||||||
|
}
|
||||||
|
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
|
||||||
|
// In production we don't want to serve sourcemaps for anyone
|
||||||
|
if (!dev) {
|
||||||
|
const hasSentryToken = !!process.env.SENTRY_TOKEN
|
||||||
|
server.get(/\.map$/, (req, res, next) => {
|
||||||
|
if (hasSentryToken && req.headers['x-sentry-token'] !== process.env.SENTRY_TOKEN) {
|
||||||
|
res
|
||||||
|
.status(401)
|
||||||
|
.send(
|
||||||
|
'Authentication access token is required to access the source map.'
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
server.get('*', (req, res) => handle(req, res))
|
||||||
|
|
||||||
|
// This handles errors if they are thrown before raching the app
|
||||||
|
server.use(Sentry.Handlers.errorHandler())
|
||||||
|
|
||||||
|
server.listen(port, err => {
|
||||||
|
if (err) throw err
|
||||||
|
console.log(`> Ready on http://localhost:${port}`)
|
||||||
|
})
|
||||||
|
})
|
63
examples/with-sentry/utils/sentry.js
Normal file
63
examples/with-sentry/utils/sentry.js
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
const Sentry = require('@sentry/node')
|
||||||
|
const Cookie = require('js-cookie')
|
||||||
|
|
||||||
|
if (process.env.SENTRY_DSN) {
|
||||||
|
Sentry.init({
|
||||||
|
dsn: process.env.SENTRY_DSN,
|
||||||
|
release: process.env.SENTRY_RELEASE,
|
||||||
|
maxBreadcrumbs: 50,
|
||||||
|
attachStacktrace: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function captureException (err, { req, res, errorInfo, query, pathname }) {
|
||||||
|
Sentry.configureScope(scope => {
|
||||||
|
if (err.message) {
|
||||||
|
// De-duplication currently doesn't work correctly for SSR / browser errors
|
||||||
|
// so we force deduplication by error message if it is present
|
||||||
|
scope.setFingerprint([err.message])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err.statusCode) {
|
||||||
|
scope.setExtra('statusCode', err.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (res && res.statusCode) {
|
||||||
|
scope.setExtra('statusCode', res.statusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.browser) {
|
||||||
|
scope.setTag('ssr', false)
|
||||||
|
scope.setExtra('query', query)
|
||||||
|
scope.setExtra('pathname', pathname)
|
||||||
|
|
||||||
|
// On client-side we use js-cookie package to fetch it
|
||||||
|
const sessionId = Cookie.get('sid')
|
||||||
|
if (sessionId) {
|
||||||
|
scope.setUser({ id: sessionId })
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
scope.setTag('ssr', true)
|
||||||
|
scope.setExtra('url', req.url)
|
||||||
|
scope.setExtra('method', req.method)
|
||||||
|
scope.setExtra('headers', req.headers)
|
||||||
|
scope.setExtra('params', req.params)
|
||||||
|
scope.setExtra('query', req.query)
|
||||||
|
|
||||||
|
// On server-side we take session cookie directly from request
|
||||||
|
if (req.cookies.sid) {
|
||||||
|
scope.setUser({ id: req.cookies.sid })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorInfo) {
|
||||||
|
scope.setExtra('componentStack', errorInfo.componentStack)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
Sentry.captureException(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
captureException
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
@import "./button.css";
|
@import "./button.css";
|
||||||
|
|
||||||
@tailwind preflight;
|
@tailwind preflight;
|
||||||
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
.hero {
|
.hero {
|
||||||
|
|
|
@ -10,9 +10,12 @@
|
||||||
},
|
},
|
||||||
"publish": {
|
"publish": {
|
||||||
"npmClient": "npm",
|
"npmClient": "npm",
|
||||||
"allowBranch": "canary",
|
"allowBranch": [
|
||||||
|
"master",
|
||||||
|
"canary"
|
||||||
|
],
|
||||||
"registry": "https://registry.npmjs.org/"
|
"registry": "https://registry.npmjs.org/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"version": "8.0.0-canary.24"
|
"version": "8.0.1-canary.0"
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
"typescript": "lerna run typescript",
|
"typescript": "lerna run typescript",
|
||||||
"prepublish": "lerna run prepublish",
|
"prepublish": "lerna run prepublish",
|
||||||
"publish-canary": "lerna version prerelease --preid canary --force-publish && release --pre",
|
"publish-canary": "lerna version prerelease --preid canary --force-publish && release --pre",
|
||||||
|
"publish-stable": "lerna version --force-publish",
|
||||||
"lint-staged": "lint-staged"
|
"lint-staged": "lint-staged"
|
||||||
},
|
},
|
||||||
"pre-commit": "lint-staged",
|
"pre-commit": "lint-staged",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "next-server",
|
"name": "next-server",
|
||||||
"version": "8.0.0-canary.24",
|
"version": "8.0.1-canary.0",
|
||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"files": [
|
"files": [
|
||||||
|
|
|
@ -836,7 +836,7 @@ You can add `prefetch` prop to any `<Link>` and Next.js will prefetch those page
|
||||||
```jsx
|
```jsx
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
|
||||||
function Header() {
|
function Header() {
|
||||||
return (
|
return (
|
||||||
<nav>
|
<nav>
|
||||||
<ul>
|
<ul>
|
||||||
|
@ -870,7 +870,7 @@ Most prefetching needs are addressed by `<Link />`, but we also expose an impera
|
||||||
```jsx
|
```jsx
|
||||||
import { withRouter } from 'next/router'
|
import { withRouter } from 'next/router'
|
||||||
|
|
||||||
function MyLink({ router }) {
|
function MyLink({ router }) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<a onClick={() => setTimeout(() => router.push('/dynamic'), 100)}>
|
<a onClick={() => setTimeout(() => router.push('/dynamic'), 100)}>
|
||||||
|
@ -1082,7 +1082,7 @@ function Home() {
|
||||||
<p>HOME PAGE is here!</p>
|
<p>HOME PAGE is here!</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Home
|
export default Home
|
||||||
```
|
```
|
||||||
|
@ -1575,7 +1575,7 @@ Example usage of `defaultLoaders.babel`:
|
||||||
// This source was taken from the @zeit/next-mdx plugin source:
|
// This source was taken from the @zeit/next-mdx plugin source:
|
||||||
// https://github.com/zeit/next-plugins/blob/master/packages/next-mdx
|
// https://github.com/zeit/next-plugins/blob/master/packages/next-mdx
|
||||||
module.exports = {
|
module.exports = {
|
||||||
webpack: (config, {}) => {
|
webpack: (config, options) => {
|
||||||
config.module.rules.push({
|
config.module.rules.push({
|
||||||
test: /\.mdx/,
|
test: /\.mdx/,
|
||||||
use: [
|
use: [
|
||||||
|
@ -1684,10 +1684,10 @@ export default Index
|
||||||
|
|
||||||
> :warning: Note that this option is not available when using `target: 'serverless'`
|
> :warning: Note that this option is not available when using `target: 'serverless'`
|
||||||
|
|
||||||
> :warning: Generally you want to use build-time configuration to provide your configuration.
|
> :warning: Generally you want to use build-time configuration to provide your configuration.
|
||||||
The reason for this is that runtime configuration adds a small rendering / initialization overhead.
|
The reason for this is that runtime configuration adds a small rendering / initialization overhead.
|
||||||
|
|
||||||
The `next/config` module gives your app access to the `publicRuntimeConfig` and `serverRuntimeConfig` stored in your `next.config.js`.
|
The `next/config` module gives your app access to the `publicRuntimeConfig` and `serverRuntimeConfig` stored in your `next.config.js`.
|
||||||
|
|
||||||
Place any server-only runtime config under a `serverRuntimeConfig` property.
|
Place any server-only runtime config under a `serverRuntimeConfig` property.
|
||||||
|
|
||||||
|
@ -1742,7 +1742,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: Next.js will automatically use that prefix in the scripts it loads, but this has no effect whatsoever on `/static`. If you want to serve those assets over the CDN, you'll have to introduce the prefix yourself. One way of introducing a prefix that works inside your components and varies by environment is documented [in this example](https://github.com/zeit/next.js/tree/master/examples/with-universal-configuration).
|
Note: Next.js will automatically use that prefix in the scripts it loads, but this has no effect whatsoever on `/static`. If you want to serve those assets over the CDN, you'll have to introduce the prefix yourself. One way of introducing a prefix that works inside your components and varies by environment is documented [in this example](https://github.com/zeit/next.js/tree/master/examples/with-universal-configuration-build-time).
|
||||||
|
|
||||||
If your CDN is on a separate domain and you would like assets to be requested using a [CORS aware request](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) you can set a config option for that.
|
If your CDN is on a separate domain and you would like assets to be requested using a [CORS aware request](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) you can set a config option for that.
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "next",
|
"name": "next",
|
||||||
"version": "8.0.0-canary.24",
|
"version": "8.0.1-canary.0",
|
||||||
"description": "The React Framework",
|
"description": "The React Framework",
|
||||||
"main": "./dist/server/next.js",
|
"main": "./dist/server/next.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
"loader-utils": "1.1.0",
|
"loader-utils": "1.1.0",
|
||||||
"mkdirp-then": "1.2.0",
|
"mkdirp-then": "1.2.0",
|
||||||
"nanoid": "1.2.1",
|
"nanoid": "1.2.1",
|
||||||
"next-server": "8.0.0-canary.24",
|
"next-server": "8.0.1-canary.0",
|
||||||
"prop-types": "15.6.2",
|
"prop-types": "15.6.2",
|
||||||
"prop-types-exact": "1.2.0",
|
"prop-types-exact": "1.2.0",
|
||||||
"react-error-overlay": "4.0.0",
|
"react-error-overlay": "4.0.0",
|
||||||
|
|
|
@ -7,29 +7,38 @@ export default class Error extends React.Component {
|
||||||
static displayName = 'ErrorPage'
|
static displayName = 'ErrorPage'
|
||||||
|
|
||||||
static getInitialProps ({ res, err }) {
|
static getInitialProps ({ res, err }) {
|
||||||
const statusCode = res ? res.statusCode : (err ? err.statusCode : null)
|
const statusCode =
|
||||||
|
res && res.statusCode ? res.statusCode : err ? err.statusCode : 404
|
||||||
return { statusCode }
|
return { statusCode }
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { statusCode } = this.props
|
const { statusCode } = this.props
|
||||||
const title = statusCode === 404
|
const title =
|
||||||
? 'This page could not be found'
|
statusCode === 404
|
||||||
: HTTPStatus[statusCode] || 'An unexpected error has occurred'
|
? 'This page could not be found'
|
||||||
|
: HTTPStatus[statusCode] || 'An unexpected error has occurred'
|
||||||
|
|
||||||
return <div style={styles.error}>
|
return (
|
||||||
<Head>
|
<div style={styles.error}>
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
|
<Head>
|
||||||
<title>{statusCode}: {title}</title>
|
<meta
|
||||||
</Head>
|
name='viewport'
|
||||||
<div>
|
content='width=device-width, initial-scale=1.0'
|
||||||
<style dangerouslySetInnerHTML={{ __html: 'body { margin: 0 }' }} />
|
/>
|
||||||
{statusCode ? <h1 style={styles.h1}>{statusCode}</h1> : null}
|
<title>
|
||||||
<div style={styles.desc}>
|
{statusCode}: {title}
|
||||||
<h2 style={styles.h2}>{title}.</h2>
|
</title>
|
||||||
|
</Head>
|
||||||
|
<div>
|
||||||
|
<style dangerouslySetInnerHTML={{ __html: 'body { margin: 0 }' }} />
|
||||||
|
{statusCode ? <h1 style={styles.h1}>{statusCode}</h1> : null}
|
||||||
|
<div style={styles.desc}>
|
||||||
|
<h2 style={styles.h2}>{title}.</h2>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +52,8 @@ const styles = {
|
||||||
error: {
|
error: {
|
||||||
color: '#000',
|
color: '#000',
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
fontFamily: '-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
|
fontFamily:
|
||||||
|
'-apple-system, BlinkMacSystemFont, Roboto, "Segoe UI", "Fira Sans", Avenir, "Helvetica Neue", "Lucida Grande", sans-serif',
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
|
|
6
test/integration/client-404/next.config.js
Normal file
6
test/integration/client-404/next.config.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
module.exports = {
|
||||||
|
onDemandEntries: {
|
||||||
|
// Make sure entries are not getting disposed.
|
||||||
|
maxInactiveAge: 1000 * 60 * 60
|
||||||
|
}
|
||||||
|
}
|
23
test/integration/client-404/pages/_error.js
Normal file
23
test/integration/client-404/pages/_error.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import Link from 'next/link'
|
||||||
|
import NextError from 'next/error'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default class Error extends React.Component {
|
||||||
|
static getInitialProps (ctx) {
|
||||||
|
const { statusCode } = NextError.getInitialProps(ctx)
|
||||||
|
return { statusCode: statusCode || null }
|
||||||
|
}
|
||||||
|
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div id='errorStatusCode'>{this.props.statusCode || 'unknown'}</div>
|
||||||
|
<p>
|
||||||
|
<Link href='/'>
|
||||||
|
<a id='errorGoHome'>go home</a>
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
1
test/integration/client-404/pages/index.js
Normal file
1
test/integration/client-404/pages/index.js
Normal file
|
@ -0,0 +1 @@
|
||||||
|
export default () => <div id='hellom8'>OK</div>
|
27
test/integration/client-404/test/client-navigation.js
Normal file
27
test/integration/client-404/test/client-navigation.js
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
/* eslint-env jest */
|
||||||
|
|
||||||
|
import webdriver from 'next-webdriver'
|
||||||
|
|
||||||
|
export default (context) => {
|
||||||
|
describe('Client Navigation 404', () => {
|
||||||
|
describe('should show 404 upon client replacestate', () => {
|
||||||
|
it('should navigate the page', async () => {
|
||||||
|
const browser = await webdriver(context.appPort, '/asd')
|
||||||
|
const serverCode = await browser
|
||||||
|
.waitForElementByCss('#errorStatusCode')
|
||||||
|
.text()
|
||||||
|
await browser.waitForElementByCss('#errorGoHome').click()
|
||||||
|
await browser.waitForElementByCss('#hellom8').back()
|
||||||
|
const clientCode = await browser
|
||||||
|
.waitForElementByCss('#errorStatusCode')
|
||||||
|
.text()
|
||||||
|
|
||||||
|
expect({ serverCode, clientCode }).toMatchObject({
|
||||||
|
serverCode: '404',
|
||||||
|
clientCode: '404'
|
||||||
|
})
|
||||||
|
browser.close()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
23
test/integration/client-404/test/index.test.js
Normal file
23
test/integration/client-404/test/index.test.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/* eslint-env jest */
|
||||||
|
/* global jasmine */
|
||||||
|
import { join } from 'path'
|
||||||
|
import { renderViaHTTP, findPort, launchApp, killApp } from 'next-test-utils'
|
||||||
|
|
||||||
|
// test suite
|
||||||
|
import clientNavigation from './client-navigation'
|
||||||
|
|
||||||
|
const context = {}
|
||||||
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 5
|
||||||
|
|
||||||
|
describe('Client 404', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
context.appPort = await findPort()
|
||||||
|
context.server = await launchApp(join(__dirname, '../'), context.appPort)
|
||||||
|
|
||||||
|
// pre-build page at the start
|
||||||
|
await renderViaHTTP(context.appPort, '/')
|
||||||
|
})
|
||||||
|
afterAll(() => killApp(context.server))
|
||||||
|
|
||||||
|
clientNavigation(context, (p, q) => renderViaHTTP(context.appPort, p, q))
|
||||||
|
})
|
Loading…
Reference in a new issue