Merge pull request #3052 from nuxt/feat/jest

feat: migrate testing tool to jest
This commit is contained in:
Sébastien Chopin 2018-03-19 17:58:45 +01:00 committed by GitHub
commit 9c8837d52b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
127 changed files with 3498 additions and 4104 deletions

View File

@ -1,9 +1,13 @@
version: 2 version: 2
defaults: &defaults
working_directory: ~/project/nuxt
docker:
- image: banian/node-headless-chrome
jobs: jobs:
build: setup:
working_directory: /usr/src/app <<: *defaults
docker:
- image: banian/node-headless-chrome
steps: steps:
# Checkout repository # Checkout repository
- checkout - checkout
@ -23,17 +27,95 @@ jobs:
paths: paths:
- "node_modules" - "node_modules"
# Test # Persist files
- run: - persist_to_workspace:
name: Tests root: ~/project
command: NODE_ENV=test yarn test && yarn coverage paths:
- nuxt
# Release edge lint:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run: - run:
name: Publish nuxt-edge name: ESLint
command: yarn lint
build:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run:
name: Build Fixtures
command: yarn build-fixtures
- persist_to_workspace:
root: ~/project
paths:
- nuxt/test/fixtures
test:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run:
name: Test
command: yarn test:unit && yarn coverage
environment:
- NODE_ENV: "test"
test-e2e:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run:
name: Test (e2e)
command: yarn test:e2e && yarn coverage
environment:
- NODE_ENV: "test"
release:
<<: *defaults
steps:
- attach_workspace:
at: ~/project
- run:
name: release
command: | command: |
if [ "${CIRCLE_BRANCH}" == "dev" ]; then if [ "${CIRCLE_BRANCH}" == "dev" ]; then
echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" >> ~/.npmrc
echo "//registry.yarnpkg.com/:_authToken=$NPM_TOKEN" >> ~/.npmrc echo "//registry.yarnpkg.com/:_authToken=$NPM_TOKEN" >> ~/.npmrc
./scripts/release-edge ./scripts/release-edge
fi fi
workflows:
version: 2
setup-and-parallel-test:
jobs:
- setup
- lint:
requires:
- setup
- build:
requires:
- setup
- test:
requires:
- build
- test-e2e:
requires:
- build
- release:
requires:
- build
- lint
- test
- test-e2e

View File

@ -6,7 +6,8 @@ module.exports = {
}, },
env: { env: {
browser: true, browser: true,
node: true node: true,
'jest/globals': true
}, },
extends: [ extends: [
'standard', 'standard',
@ -14,9 +15,9 @@ module.exports = {
'plugin:import/errors', 'plugin:import/errors',
'plugin:import/warnings' 'plugin:import/warnings'
], ],
// required to lint *.vue files
plugins: [ plugins: [
'html' 'html',
'jest'
], ],
settings: { settings: {
'import/resolver': { 'import/resolver': {

View File

@ -19,10 +19,13 @@ install:
test_script: test_script:
# Output useful info for debugging. # Output useful info for debugging.
- node --version - node --version
- npm --version # - npm --version
- yarn --version - yarn --version
# run tests # run tests
- yarn test - yarn test
# Don't actually build. # Don't actually build.
build: off build: off
# Do not build feature branch with open Pull Requests
skip_branch_with_pr: true

View File

@ -1,5 +1,5 @@
{ {
"name": "components-injection-nuxt", "name": "example-async-components-injection",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-async-data", "name": "example-async-data",
"dependencies": { "dependencies": {
"axios": "latest", "axios": "latest",
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "auth-routes", "name": "example-auth-routes",
"dependencies": { "dependencies": {
"axios": "^0.16.1", "axios": "^0.16.1",
"body-parser": "^1.17.2", "body-parser": "^1.17.2",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-proxy", "name": "example-axios",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@nuxtjs/axios": "^5.0.0", "@nuxtjs/axios": "^5.0.0",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-cached-components", "name": "example-cached-components",
"dependencies": { "dependencies": {
"lru-cache": "^4.0.2", "lru-cache": "^4.0.2",
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "coffeescript", "name": "example-coffeescript",
"version": "1.0.0", "version": "1.0.0",
"description": "Nuxt.js with CoffeeScript", "description": "Nuxt.js with CoffeeScript",
"author": "Alex Ananiev <alex.ananiev.r@gmail.com>", "author": "Alex Ananiev <alex.ananiev.r@gmail.com>",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-custom-build", "name": "example-custom-build",
"description": "", "description": "",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-custom-layouts", "name": "example-custom-layouts",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-custom-loading", "name": "example-custom-loading",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-custom-routes", "name": "example-custom-routes",
"dependencies": { "dependencies": {
"axios": "latest", "axios": "latest",
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-custom-server", "name": "example-custom-server",
"dependencies": { "dependencies": {
"express": "^4.15.3", "express": "^4.15.3",
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "dynamic-components-nuxt", "name": "example-dynamic-components",
"dependencies": { "dependencies": {
"chart.js": "^2.7.0", "chart.js": "^2.7.0",
"nuxt": "latest", "nuxt": "latest",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-dynamic-layouts", "name": "example-dynamic-layouts",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-global-css", "name": "example-global-css",
"dependencies": { "dependencies": {
"bulma": "^0.5.1", "bulma": "^0.5.1",
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "hello-nuxt", "name": "example-hello-world",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-i18n", "name": "example-i18n",
"dependencies": { "dependencies": {
"nuxt": "latest", "nuxt": "latest",
"vue-i18n": "^7.3.2" "vue-i18n": "^7.3.2"

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-jsx", "name": "example-jsx",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-layout-transitions", "name": "example-layout-transitions",
"dependencies": { "dependencies": {
"axios": "^0.15.3", "axios": "^0.15.3",
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-markdownit", "name": "example-markdownit",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@nuxtjs/markdownit": "^1.1.2", "@nuxtjs/markdownit": "^1.1.2",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-head-elements", "name": "example-meta-info",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-middleware", "name": "example-middleware",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-nested-components", "name": "example-nested-components",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-nested-routes", "name": "example-nested-routes",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "no-ssr-cmp-nuxt", "name": "example-no-ssr",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-plugins-vendor", "name": "example-plugins-vendor",
"dependencies": { "dependencies": {
"axios": "^0.16.2", "axios": "^0.16.2",
"mini-toastr": "^0.6.5", "mini-toastr": "^0.6.5",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-routes-transitions", "name": "example-routes-transitions",
"dependencies": { "dependencies": {
"axios": "^0.15.3", "axios": "^0.15.3",
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-routes-transitions", "name": "example-scroll-behavior",
"dependencies": { "dependencies": {
"axios": "^0.15.3", "axios": "^0.15.3",
"nuxt": "latest", "nuxt": "latest",

View File

@ -1,5 +1,5 @@
{ {
"name": "hello-nuxt", "name": "example-spa",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-hello-world", "name": "example-static-images",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "storybook", "name": "example-storybook",
"version": "1.0.0", "version": "1.0.0",
"description": "sample storybook setup for nuxt", "description": "sample storybook setup for nuxt",
"scripts": { "scripts": {

View File

@ -1,5 +1,5 @@
{ {
"name": "style-resources", "name": "example-style-resources",
"dependencies": { "dependencies": {
"less": "^2.7.3", "less": "^2.7.3",
"less-loader": "^4.0.5", "less-loader": "^4.0.5",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-tailwindcss", "name": "example-tailwindcss",
"description": "Nuxt.js with Tailwind CSS", "description": "Nuxt.js with Tailwind CSS",
"scripts": { "scripts": {
"dev": "nuxt", "dev": "nuxt",

View File

@ -1,5 +1,5 @@
{ {
"name": "uikit-nuxt", "name": "example-uikit",
"dependencies": { "dependencies": {
"nuxt": "latest", "nuxt": "latest",
"uikit": "^3.0.0-beta.30", "uikit": "^3.0.0-beta.30",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-vue-apollo", "name": "example-vue-apollo",
"dependencies": { "dependencies": {
"@nuxtjs/apollo": "^2.1.1", "@nuxtjs/apollo": "^2.1.1",
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "vue-chartjs-nuxt", "name": "example-vue-chartjs",
"dependencies": { "dependencies": {
"axios": "^0.16.2", "axios": "^0.16.2",
"chart.js": "^2.7.1", "chart.js": "^2.7.1",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-class-component", "name": "example-vue-class-component",
"dependencies": { "dependencies": {
"babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.4", "babel-plugin-transform-decorators-legacy": "^1.3.4",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-vuex-store", "name": "example-persisted-state",
"dependencies": { "dependencies": {
"nuxt": "latest", "nuxt": "latest",
"vuex-persistedstate": "^2.0.0" "vuex-persistedstate": "^2.0.0"

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-vuex-store", "name": "example-vuex-store-modules",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-vuex-store", "name": "example-vuex-store",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "with-amp", "name": "example-with-amp",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-with-ava", "name": "example-with-ava",
"scripts": { "scripts": {
"dev": "nuxt", "dev": "nuxt",
"build": "nuxt build", "build": "nuxt build",

View File

@ -8,16 +8,16 @@ import { Nuxt, Builder } from 'nuxt'
let nuxt = null let nuxt = null
// Init Nuxt.js and create a server listening on localhost:4000 // Init Nuxt.js and create a server listening on localhost:4000
test.before('Init Nuxt.js', async t => { beforeAll(async () => {
const rootDir = resolve(__dirname, '..') const rootDir = resolve(__dirname, '..')
let config = {} let config = {}
try { config = require(resolve(rootDir, 'nuxt.config.js')) } catch (e) {} try { config = require(resolve(rootDir, 'nuxt.config.js')) } catch (e) {}
config.rootDir = rootDir // project folder config.rootDir = rootDir // project folder
config.dev = false // production build config.dev = false // production build
nuxt = new Nuxt(config) nuxt = new Nuxt(config)
await new Builder(nuxt).build() new Builder(nuxt).build()
await nuxt.listen(4000, 'localhost') await nuxt.listen(4000, 'localhost')
}) }, 30000)
// Example of testing only generated html // Example of testing only generated html
test('Route / exits and render HTML', async t => { test('Route / exits and render HTML', async t => {

View File

@ -1,5 +1,5 @@
{ {
"name": "with-buefy", "name": "example-with-buefy",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "index.js",
"license": "MIT", "license": "MIT",

View File

@ -1,5 +1,5 @@
{ {
"name": "hello-nuxt", "name": "example-with-cookies",
"dependencies": { "dependencies": {
"cookie": "^0.3.1", "cookie": "^0.3.1",
"js-cookie": "^2.1.4", "js-cookie": "^2.1.4",

View File

@ -1,5 +1,5 @@
{ {
"name": "with-element-ui", "name": "example-with-element-ui",
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@ -1,5 +1,5 @@
{ {
"name": "with-feathers", "name": "example-with-feathers",
"description": "Nuxt.js with FeathersJS", "description": "Nuxt.js with FeathersJS",
"version": "1.0.0", "version": "1.0.0",
"main": "src/", "main": "src/",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-firebase", "name": "example-with-firebase",
"version": "1.0.0", "version": "1.0.0",
"description": "Nuxt.js with Firebase", "description": "Nuxt.js with Firebase",
"scripts": { "scripts": {

View File

@ -1,5 +1,5 @@
{ {
"name": "hello-nuxt-keep-alive", "name": "example-with-keep-alive",
"dependencies": { "dependencies": {
"nuxt": "latest" "nuxt": "latest"
}, },

View File

@ -1,5 +1,5 @@
{ {
"name": "with-museui", "name": "example-with-museui",
"dependencies": { "dependencies": {
"nuxt": "latest", "nuxt": "latest",
"muse-ui": "latest" "muse-ui": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-tailwindcss", "name": "example-with-purgecss",
"description": "Nuxt.js with Tailwind CSS", "description": "Nuxt.js with Tailwind CSS",
"scripts": { "scripts": {
"dev": "nuxt", "dev": "nuxt",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-socketio", "name": "example-with-sockets",
"scripts": { "scripts": {
"dev": "nodemon -w server.js -w nuxt.config.js server.js", "dev": "nodemon -w server.js -w nuxt.config.js server.js",
"build": "nuxt build", "build": "nuxt build",

View File

@ -1,5 +1,5 @@
{ {
"name": "nuxt-with-tape", "name": "example-with-tape",
"scripts": { "scripts": {
"dev": "nuxt", "dev": "nuxt",
"build": "nuxt build", "build": "nuxt build",

View File

@ -1,5 +1,5 @@
{ {
"name": "with-vue-material", "name": "example-with-vue-material",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"nuxt": "latest", "nuxt": "latest",

View File

@ -1,5 +1,5 @@
{ {
"name": "with-vuetify", "name": "example-with-vuetify",
"dependencies": { "dependencies": {
"nuxt": "latest", "nuxt": "latest",
"vuetify": "latest" "vuetify": "latest"

View File

@ -1,5 +1,5 @@
{ {
"name": "with-vux", "name": "example-with-vux",
"version": "1.0.0", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

5
jest.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
testEnvironment: 'node',
coverageDirectory: './coverage/',
setupTestFrameworkScriptFile: './test/utils/setup'
}

View File

@ -40,6 +40,7 @@ export default class Builder {
this.filesWatcher = null this.filesWatcher = null
this.customFilesWatcher = null this.customFilesWatcher = null
this.spinner = createSpinner() this.spinner = createSpinner()
this.spinner.enabled = !this.options.test
// Mute stats on dev // Mute stats on dev
this.webpackStats = this.options.dev ? false : this.options.build.stats this.webpackStats = this.options.dev ? false : this.options.build.stats

View File

@ -21,6 +21,7 @@ export default class Generator {
) )
this.spinner = createSpinner() this.spinner = createSpinner()
this.spinner.enabled = !this.options.test
} }
async generate({ build = true, init = true } = {}) { async generate({ build = true, init = true } = {}) {

View File

@ -1,5 +1,6 @@
import path from 'path' import path from 'path'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import FriendlyErrorsWebpackPlugin from '@nuxtjs/friendly-errors-webpack-plugin' import FriendlyErrorsWebpackPlugin from '@nuxtjs/friendly-errors-webpack-plugin'
import TimeFixPlugin from 'time-fix-plugin' import TimeFixPlugin from 'time-fix-plugin'
import webpack from 'webpack' import webpack from 'webpack'
@ -127,14 +128,16 @@ export default function webpackBaseConfig({ name, isServer }) {
} }
// Build progress indicator // Build progress indicator
if (this.options.build.profile) { if (!this.options.test) {
config.plugins.push(new webpack.ProgressPlugin({ profile: true })) if (this.options.build.profile) {
} else { config.plugins.push(new webpack.ProgressPlugin({ profile: true }))
config.plugins.push(new ProgressPlugin({ } else {
spinner: this.spinner, config.plugins.push(new ProgressPlugin({
name: isServer ? 'server' : 'client', spinner: this.spinner,
color: isServer ? 'green' : 'darkgreen' name: isServer ? 'server' : 'client',
})) color: isServer ? 'green' : 'darkgreen'
}))
}
} }
// Add timefix-plugin before others plugins // Add timefix-plugin before others plugins
@ -154,6 +157,22 @@ export default function webpackBaseConfig({ name, isServer }) {
new FriendlyErrorsWebpackPlugin({ clearConsole: shouldClearConsole }) new FriendlyErrorsWebpackPlugin({ clearConsole: shouldClearConsole })
) )
// CSS extraction
const extractCSS = this.options.build.extractCSS
// TODO: Temporary disabled in dev mode for fixing source maps
// (We need `source-map` devtool for *.css modules)
if (extractCSS && !this.options.dev) {
config.plugins.push(new ExtractTextPlugin(Object.assign({
filename: this.getFileName('css')
// When using optimization.splitChunks and there are
// extracted chunks in the commons chunk,
// allChunks *must* be set to true
// TODO: For nuxt this makes duplicate css assets!
// allChunks: true
}, typeof extractCSS === 'object' ? extractCSS : {})))
}
// Clone deep avoid leaking config between Client and Server // Clone deep avoid leaking config between Client and Server
return _.cloneDeep(config) return _.cloneDeep(config)
} }

View File

@ -2,8 +2,8 @@ import path from 'path'
import _ from 'lodash' import _ from 'lodash'
import webpack from 'webpack' import webpack from 'webpack'
import HTMLPlugin from 'html-webpack-plugin' import HTMLPlugin from 'html-webpack-plugin'
import ExtractTextPlugin from 'extract-text-webpack-plugin'
import StylishPlugin from 'webpack-stylish' import StylishPlugin from 'webpack-stylish'
import BundleAnalyzer from 'webpack-bundle-analyzer' import BundleAnalyzer from 'webpack-bundle-analyzer'
@ -119,25 +119,7 @@ export default function webpackClientConfig() {
} }
// Create additional runtime chunk for cache boosting // Create additional runtime chunk for cache boosting
// config.optimization.runtimeChunk = true config.optimization.runtimeChunk = true
// CSS extraction
const extractCSS = this.options.build.extractCSS
// TODO: Temporary disabled in dev mode for fixing source maps
// (We need `source-map` devtool for *.css modules)
if (extractCSS && !this.options.dev) {
config.plugins.push(new ExtractTextPlugin(Object.assign({
filename: this.getFileName('css')
// When using optimization.splitChunks and there are
// extracted chunks in the commons chunk,
// allChunks *must* be set to true
// TODO: For nuxt this makes duplicate css assets!
// allChunks: true
},
typeof extractCSS === 'object' ? extractCSS : {}
)))
}
// -------------------------------------- // --------------------------------------
// Dev specific config // Dev specific config
@ -172,7 +154,7 @@ export default function webpackClientConfig() {
} }
// https://github.com/webpack-contrib/webpack-stylish // https://github.com/webpack-contrib/webpack-stylish
if (!this.options.dev) { if (!this.options.dev && !this.options.test) {
config.plugins.push(new StylishPlugin()) config.plugins.push(new StylishPlugin())
} }

View File

@ -4,6 +4,7 @@ export default {
mode: 'universal', mode: 'universal',
dev: process.env.NODE_ENV !== 'production', dev: process.env.NODE_ENV !== 'production',
debug: undefined, // Will be equal to dev if not provided debug: undefined, // Will be equal to dev if not provided
test: process.env.NODE_ENV === 'test',
buildDir: '.nuxt', buildDir: '.nuxt',
cacheDir: '.cache', cacheDir: '.cache',
nuxtDir: path.resolve(__dirname, '../..'), nuxtDir: path.resolve(__dirname, '../..'),

View File

@ -42,19 +42,6 @@ export const getContext = function getContext(req, res) {
return { req, res } return { req, res }
} }
export const setAnsiColors = function setAnsiColors(ansiHTML) {
ansiHTML.setColors({
reset: ['efefef', 'a6004c'],
darkgrey: '5a012b',
yellow: 'ffab07',
green: 'aeefba',
magenta: 'ff84bf',
blue: '3505a0',
cyan: '56eaec',
red: '4e053a'
})
}
export const waitFor = function waitFor(ms) { export const waitFor = function waitFor(ms) {
return new Promise(resolve => setTimeout(resolve, ms || 0)) return new Promise(resolve => setTimeout(resolve, ms || 0))
} }
@ -133,8 +120,8 @@ export const parallel = function parallel(tasks, fn) {
export const chainFn = function chainFn(base, fn) { export const chainFn = function chainFn(base, fn) {
/* istanbul ignore if */ /* istanbul ignore if */
if (!(fn instanceof Function)) { if (typeof fn !== 'function') {
return return base
} }
return function () { return function () {
if (typeof base !== 'function') { if (typeof base !== 'function') {

View File

@ -127,12 +127,14 @@ export default class Nuxt {
} }
const _host = host === '0.0.0.0' ? 'localhost' : host const _host = host === '0.0.0.0' ? 'localhost' : host
// eslint-disable-next-line no-console if (!this.options.test) {
console.log( // eslint-disable-next-line no-console
'\n' + console.log(
'\n' +
chalk.bgGreen.black(' OPEN ') + chalk.bgGreen.black(' OPEN ') +
chalk.green(` http://${_host}:${port}\n`) chalk.green(` http://${_host}:${port}\n`)
) )
}
// Close server on nuxt close // Close server on nuxt close
this.hook( this.hook(

View File

@ -1,7 +1,6 @@
import path from 'path' import path from 'path'
import crypto from 'crypto' import crypto from 'crypto'
import ansiHTML from 'ansi-html'
import serialize from 'serialize-javascript' import serialize from 'serialize-javascript'
import serveStatic from 'serve-static' import serveStatic from 'serve-static'
import compression from 'compression' import compression from 'compression'
@ -12,7 +11,7 @@ import Debug from 'debug'
import connect from 'connect' import connect from 'connect'
import launchMiddleware from 'launch-editor-middleware' import launchMiddleware from 'launch-editor-middleware'
import { setAnsiColors, isUrl, waitFor, timeout } from '../common/utils' import { isUrl, waitFor, timeout } from '../common/utils'
import defaults from '../common/nuxt.config' import defaults from '../common/nuxt.config'
import MetaRenderer from './meta' import MetaRenderer from './meta'
@ -22,8 +21,6 @@ import nuxtMiddleware from './middleware/nuxt'
const debug = Debug('nuxt:render') const debug = Debug('nuxt:render')
debug.color = 4 // Force blue color debug.color = 4 // Force blue color
setAnsiColors(ansiHTML)
let jsdom = null let jsdom = null
export default class Renderer { export default class Renderer {

View File

@ -40,14 +40,12 @@
"bin": { "bin": {
"nuxt": "./bin/nuxt" "nuxt": "./bin/nuxt"
}, },
"nyc": {
"include": [
"lib"
]
},
"scripts": { "scripts": {
"test": "npm run lint && nyc ava -v test -- && nyc report --reporter=html", "test": "npm run build-fixtures && npm run test:unit",
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov", "build-fixtures": "node ./scripts/build-fixtures",
"test:unit": "jest --maxWorkers=4 --coverage test/unit",
"test:e2e": "jest --maxWorkers=1 test/e2e",
"coverage": "codecov",
"lint": "eslint --ext .js,.mjs,.vue bin/* build/ lib/ test/ examples/", "lint": "eslint --ext .js,.mjs,.vue bin/* build/ lib/ test/ examples/",
"precommit": "npm run lint", "precommit": "npm run lint",
"postinstall": "opencollective postinstall || exit 0" "postinstall": "opencollective postinstall || exit 0"
@ -119,24 +117,27 @@
"webpack-stylish": "^0.1.6" "webpack-stylish": "^0.1.6"
}, },
"devDependencies": { "devDependencies": {
"ava": "^0.25.0", "@expo/spawn-async": "^1.3.0",
"babel-eslint": "^8.2.1", "babel-eslint": "^8.2.1",
"babel-plugin-istanbul": "^4.1.5",
"codecov": "^3.0.0", "codecov": "^3.0.0",
"eslint": "^4.18.2", "eslint": "^4.18.2",
"eslint-config-standard": "^11.0.0", "eslint-config-standard": "^11.0.0",
"eslint-config-standard-jsx": "^5.0.0", "eslint-config-standard-jsx": "^5.0.0",
"eslint-plugin-html": "^4.0.2", "eslint-plugin-html": "^4.0.2",
"eslint-plugin-import": "^2.8.0", "eslint-plugin-import": "^2.8.0",
"eslint-plugin-jest": "^21.15.0",
"eslint-plugin-node": "^6.0.0", "eslint-plugin-node": "^6.0.0",
"eslint-plugin-promise": "^3.7.0", "eslint-plugin-promise": "^3.7.0",
"eslint-plugin-react": "^7.6.1", "eslint-plugin-react": "^7.6.1",
"eslint-plugin-standard": "^3.0.1", "eslint-plugin-standard": "^3.0.1",
"express": "^4.16.2", "express": "^4.16.2",
"finalhandler": "^1.1.1", "finalhandler": "^1.1.1",
"get-port": "^3.2.0",
"jest": "^22.4.2",
"jest-runner-eslint": "^0.4.0",
"jsdom": "^11.6.2", "jsdom": "^11.6.2",
"nyc": "^11.6.0", "listr": "^0.13.0",
"puppeteer": "^1.0.0", "puppeteer": "^1.2.0",
"request": "^2.83.0", "request": "^2.83.0",
"request-promise-native": "^1.0.5", "request-promise-native": "^1.0.5",
"sinon": "^4.4.5" "sinon": "^4.4.5"

65
scripts/build-fixtures Executable file
View File

@ -0,0 +1,65 @@
#!/usr/bin/env node
process.env.NODE_ENV = 'test'
const { resolve } = require('path')
const { cpus } = require('os')
const spawnAsync = require('@expo/spawn-async')
const Listr = require('listr')
const fixtures = [
'children',
'custom-dirs',
'debug',
'deprecate',
'dynamic-routes',
'empty',
'error',
'module',
'ssr',
'with-config',
// csr,
// dev,
// generate,
// fail generate,
// fallback generate,
// ssr,
// ssr csp,
// spa
'basic'
]
const nuxtBuild = resolve(__dirname, '../bin/nuxt-build')
function buildFixture(name) {
const rootDir = resolve(__dirname, '../test/fixtures', name)
return spawnAsync('node', [nuxtBuild, rootDir])
}
const tasks = []
for (let fixture of fixtures) {
tasks.push({
title: 'Building fixtures ' + fixture,
task: (ctx, task) => buildFixture(fixture)
.then(() => {
task.title = task.title.replace(/^Building/, 'Built') + ' Successfully'
})
})
}
const options = {
renderer: process.env.CI ? 'silent' : 'default',
nonTTYRenderer: 'silent',
concurrent: Math.min(4, cpus().length)
}
new Listr([{
title: `Build ${fixtures.length} fixtures with concurrency of ${options.concurrent}`,
task: () => new Listr(tasks, {concurrent: options.concurrent})
}], options)
.run()
.then(() => process.exit(0))
.catch((err) => {
console.error(err) // eslint-disable-line no-console
process.exit(1)
})

9
test/.babelrc Normal file
View File

@ -0,0 +1,9 @@
{
"env": {
"test": {
"presets": [
"env"
]
}
}
}

View File

@ -1,16 +0,0 @@
import { resolve } from 'path'
import test from 'ava'
import { Nuxt, Options } from '..'
import { version } from '../package.json'
test('Nuxt.version is same as package', t => {
t.is(Nuxt.version, version)
})
test('modulesDir uses /node_modules as default if not set', async t => {
const options = Options.from({})
const currentNodeModulesDir = resolve(__dirname, '..', 'node_modules')
t.true(options.modulesDir.includes(currentNodeModulesDir))
})

View File

@ -1,255 +0,0 @@
import { resolve } from 'path'
import test from 'ava'
import { Nuxt, Builder } from '..'
import * as browser from './helpers/browser'
import { interceptLog } from './helpers/console'
const port = 4003
const url = route => 'http://localhost:' + port + route
let nuxt = null
let page = null
const waitFor = ms => new Promise(resolve => setTimeout(resolve, ms || 0))
// Init nuxt.js and create server listening on localhost:4003
test.serial('Init Nuxt.js', async t => {
const options = {
rootDir: resolve(__dirname, 'fixtures/basic'),
buildDir: '.nuxt-csr',
dev: true,
head: {
titleTemplate(titleChunk) {
return titleChunk ? `${titleChunk} - Nuxt.js` : 'Nuxt.js'
}
},
build: {
stats: false
}
}
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(options)
await new Builder(nuxt).build()
await nuxt.listen(port, 'localhost')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test.serial('Start browser', async t => {
t.plan(0) // suppress 'no assertions' warning
await browser.start({
// slowMo: 50,
// headless: false
})
})
test.serial('Open /', async t => {
page = await browser.page(url('/'))
await waitFor(1000)
t.is(await page.$text('h1'), 'Index page')
})
test.serial('/stateless', async t => {
const { hook } = await page.nuxt.navigate('/stateless', false)
const loading = await page.nuxt.loadingData()
await waitFor(1000)
t.is(loading.show, true)
await hook
t.is(await page.$text('h1'), 'My component!')
})
test.serial('/css', async t => {
await page.nuxt.navigate('/css')
await waitFor(1000)
t.is(await page.$text('.red'), 'This is red')
t.is(
await page.$eval('.red', red => window.getComputedStyle(red).color),
'rgb(255, 0, 0)'
)
})
test.serial('/stateful', async t => {
await page.nuxt.navigate('/stateful')
await waitFor(1000)
t.is(await page.$text('p'), 'The answer is 42')
})
test.serial('/store', async t => {
await page.nuxt.navigate('/store')
await waitFor(1000)
t.is(await page.$text('h1'), 'Vuex Nested Modules')
t.is(await page.$text('p'), '1')
})
test.serial('/head', async t => {
const msg = new Promise(resolve =>
page.on('console', msg => resolve(msg.text()))
)
await page.nuxt.navigate('/head')
const metas = await page.$$attr('meta', 'content')
await waitFor(1000)
t.is(await msg, 'Body script!')
t.is(await page.title(), 'My title - Nuxt.js')
t.is(await page.$text('h1'), 'I can haz meta tags')
t.is(metas[0], 'my meta')
})
test.serial('/async-data', async t => {
await page.nuxt.navigate('/async-data')
await waitFor(1000)
t.is(await page.$text('p'), 'Nuxt.js')
})
test.serial('/await-async-data', async t => {
await page.nuxt.navigate('/await-async-data')
await waitFor(1000)
t.is(await page.$text('p'), 'Await Nuxt.js')
})
test.serial('/callback-async-data', async t => {
await page.nuxt.navigate('/callback-async-data')
await waitFor(1000)
t.is(await page.$text('p'), 'Callback Nuxt.js')
})
test.serial('/users/1', async t => {
await page.nuxt.navigate('/users/1')
await waitFor(1000)
t.is(await page.$text('h1'), 'User: 1')
})
test.serial('/validate should display a 404', async t => {
await page.nuxt.navigate('/validate')
await waitFor(1000)
const error = await page.nuxt.errorData()
t.is(error.statusCode, 404)
t.is(error.message, 'This page could not be found')
})
test.serial('/validate?valid=true', async t => {
await page.nuxt.navigate('/validate?valid=true')
await waitFor(1000)
t.is(await page.$text('h1'), 'I am valid')
})
test.serial('/redirect', async t => {
await page.nuxt.navigate('/redirect')
await waitFor(1000)
t.is(await page.$text('h1'), 'Index page')
})
test.serial('/error', async t => {
await page.nuxt.navigate('/error')
await waitFor(1000)
t.deepEqual(await page.nuxt.errorData(), { statusCode: 500 })
t.is(await page.$text('.title'), 'Error mouahahah')
})
test.serial('/error2', async t => {
await page.nuxt.navigate('/error2')
await waitFor(1000)
t.is(await page.$text('.title'), 'Custom error')
t.deepEqual(await page.nuxt.errorData(), { message: 'Custom error' })
})
test.serial('/redirect-middleware', async t => {
await page.nuxt.navigate('/redirect-middleware')
await waitFor(1000)
t.is(await page.$text('h1'), 'Index page')
})
test.serial('/redirect-external', async t => {
// New page for redirecting to external link.
const page = await browser.page(url('/'))
await page.nuxt.navigate('/redirect-external', false)
await waitFor(1000)
await page.waitForFunction(
() => window.location.href === 'https://nuxtjs.org/'
)
page.close()
t.pass()
})
test.serial('/redirect-name', async t => {
await page.nuxt.navigate('/redirect-name')
await waitFor(1000)
t.is(await page.$text('h1'), 'My component!')
})
test.serial('/no-ssr', async t => {
await page.nuxt.navigate('/no-ssr')
await waitFor(1000)
t.is(await page.$text('h1'), 'Displayed only on client-side')
})
test.serial('/meta', async t => {
await page.nuxt.navigate('/meta')
await waitFor(1000)
const state = await page.nuxt.storeState()
t.deepEqual(state.meta, [{ works: true }])
})
test.serial('/fn-midd', async t => {
await page.nuxt.navigate('/fn-midd')
await waitFor(1000)
t.is(await page.$text('.title'), 'You need to ask the permission')
t.deepEqual(await page.nuxt.errorData(), {
message: 'You need to ask the permission',
statusCode: 403
})
})
test.serial('/fn-midd?please=true', async t => {
await page.nuxt.navigate('/fn-midd?please=true')
await waitFor(1000)
const h1 = await page.$text('h1')
t.true(h1.includes('Date:'))
})
test.serial('/router-guard', async t => {
await page.nuxt.navigate('/router-guard')
await waitFor(1000)
const p = await page.$text('p')
t.is(p, 'Nuxt.js')
})
test.after.always('Stop browser', async () => {
process.on('unhandledRejection', () => {})
await browser.stop()
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async () => {
await nuxt.close()
})

View File

@ -1,67 +0,0 @@
import test from 'ava'
import { resolve } from 'path'
import { intercept, release } from './helpers/console'
import { Nuxt, Builder } from '..'
const port = 4001
const url = route => 'http://localhost:' + port + route
const rootDir = resolve(__dirname, 'fixtures/basic')
let nuxt = null
// Init nuxt.js and create server listening on localhost:4000
test.serial('Init Nuxt.js', async t => {
const options = {
rootDir,
buildDir: '.nuxt-dev',
dev: true,
build: {
stats: false,
profile: true,
extractCSS: {
allChunks: true
}
}
}
const spies = await intercept({ log: true, stderr: true }, async () => {
nuxt = new Nuxt(options)
await new Builder(nuxt).build()
await nuxt.listen(port, 'localhost')
})
t.true(spies.log.calledWithMatch('DONE'))
t.true(spies.log.calledWithMatch('OPEN'))
})
// TODO: enable test when style-loader.js:60 was resolved
// test.serial('/extractCSS', async t => {
// const window = await nuxt.renderAndGetWindow(url('/extractCSS'))
// const html = window.document.head.innerHTML
// t.true(html.includes('vendor.css'))
// t.true(!html.includes('30px'))
// t.is(window.getComputedStyle(window.document.body).getPropertyValue('font-size'), '30px')
// })
test.serial('/stateless', async t => {
const spies = await intercept()
const window = await nuxt.renderAndGetWindow(url('/stateless'))
const html = window.document.body.innerHTML
t.true(html.includes('<h1>My component!</h1>'))
t.true(spies.info.calledWithMatch('You are running Vue in development mode.'))
release()
})
// test('/_nuxt/test.hot-update.json should returns empty html', async t => {
// try {
// await rp(url('/_nuxt/test.hot-update.json'))
// } catch (err) {
// t.is(err.statusCode, 404)
// t.is(err.response.body, '')
// }
// })
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -1,34 +0,0 @@
import { resolve } from 'path'
import test from 'ava'
import { Nuxt, Builder, Generator } from '..'
import { intercept } from './helpers/console'
test('Fail with routes() which throw an error', async t => {
const options = {
rootDir: resolve(__dirname, 'fixtures/basic'),
buildDir: '.nuxt-fail',
dev: false,
build: {
stats: false
},
generate: {
async routes() {
throw new Error('Not today!')
}
}
}
const spies = await intercept(async () => {
const nuxt = new Nuxt(options)
const builder = new Builder(nuxt)
const generator = new Generator(nuxt, builder)
return generator.generate().catch(e => {
t.true(e.message === 'Not today!')
})
})
t.true(spies.log.calledWithMatch('DONE'))
t.true(spies.error.withArgs('Could not resolve routes').calledOnce)
})

View File

@ -1,190 +0,0 @@
import { existsSync } from 'fs'
import http from 'http'
import { resolve } from 'path'
import test from 'ava'
import { remove } from 'fs-extra'
import serveStatic from 'serve-static'
import finalhandler from 'finalhandler'
import rp from 'request-promise-native'
import { Nuxt, Builder, Generator } from '..'
import { interceptLog, release } from './helpers/console'
import { loadConfig } from './helpers/config'
const port = 4002
const url = route => 'http://localhost:' + port + route
const rootDir = resolve(__dirname, 'fixtures/basic')
let nuxt = null
let server = null
let generator = null
// Init nuxt.js and create server listening on localhost:4000
test.serial('Init Nuxt.js', async t => {
const config = loadConfig('basic', {
buildDir: '.nuxt-generate',
dev: false
})
config.build.stats = false
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(config)
const builder = new Builder(nuxt)
generator = new Generator(nuxt, builder)
await generator.generate()
})
t.true(logSpy.calledWithMatch('DONE'))
const serve = serveStatic(resolve(__dirname, 'fixtures/basic/dist'))
server = http.createServer((req, res) => {
serve(req, res, finalhandler(req, res))
})
server.listen(port)
})
test.serial('Check ready hook called', async t => {
t.true(nuxt.__hook_called__)
})
test.serial('/stateless', async t => {
const window = await nuxt.renderAndGetWindow(url('/stateless'))
const html = window.document.body.innerHTML
t.true(html.includes('<h1>My component!</h1>'))
})
test.serial('/css', async t => {
const window = await nuxt.renderAndGetWindow(url('/css'))
const headHtml = window.document.head.innerHTML
t.true(headHtml.includes('.red{color:red}'))
const element = window.document.querySelector('.red')
t.not(element, null)
t.is(element.textContent, 'This is red')
t.is(element.className, 'red')
// t.is(window.getComputedStyle(element), 'red')
})
test.serial('/stateful', async t => {
const window = await nuxt.renderAndGetWindow(url('/stateful'))
const html = window.document.body.innerHTML
t.true(html.includes('<div><p>The answer is 42</p></div>'))
})
test.serial('/head', async t => {
const logSpy = await interceptLog()
const window = await nuxt.renderAndGetWindow(url('/head'))
const html = window.document.body.innerHTML
const metas = window.document.getElementsByTagName('meta')
t.is(window.document.title, 'My title')
t.is(metas[0].getAttribute('content'), 'my meta')
t.true(html.includes('<div><h1>I can haz meta tags</h1></div>'))
release()
t.is(logSpy.getCall(0).args[0], 'Body script!')
})
test.serial('/async-data', async t => {
const window = await nuxt.renderAndGetWindow(url('/async-data'))
const html = window.document.body.innerHTML
t.true(html.includes('<p>Nuxt.js</p>'))
})
test.serial('/users/1/index.html', async t => {
const html = await rp(url('/users/1/index.html'))
t.true(html.includes('<h1>User: 1</h1>'))
t.true(
existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1/index.html'))
)
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1.html')))
})
test.serial('/users/2', async t => {
const html = await rp(url('/users/2'))
t.true(html.includes('<h1>User: 2</h1>'))
})
test.serial('/users/3 (payload given)', async t => {
const html = await rp(url('/users/3'))
t.true(html.includes('<h1>User: 3000</h1>'))
})
test.serial('/users/4 -> Not found', async t => {
const error = await t.throws(rp(url('/users/4')))
t.true(error.statusCode === 404)
t.true(error.response.body.includes('Cannot GET /users/4'))
})
test.serial('/validate should not be server-rendered', async t => {
const html = await rp(url('/validate'))
t.true(html.includes('<div id="__nuxt"></div>'))
t.true(html.includes('serverRendered:!1'))
})
test.serial('/validate -> should display a 404', async t => {
const window = await nuxt.renderAndGetWindow(url('/validate'))
const html = window.document.body.innerHTML
t.true(html.includes('This page could not be found'))
})
test.serial('/validate?valid=true', async t => {
const window = await nuxt.renderAndGetWindow(url('/validate?valid=true'))
const html = window.document.body.innerHTML
t.true(html.includes('I am valid</h1>'))
})
test.serial('/redirect should not be server-rendered', async t => {
const html = await rp(url('/redirect'))
t.true(html.includes('<div id="__nuxt"></div>'))
t.true(html.includes('serverRendered:!1'))
})
test.serial('/redirect -> check redirected source', async t => {
const window = await nuxt.renderAndGetWindow(url('/redirect'))
const html = window.document.body.innerHTML
t.true(html.includes('<h1>Index page</h1>'))
})
test.serial('/users/1 not found', async t => {
await remove(resolve(rootDir, 'dist/users'))
const error = await t.throws(rp(url('/users/1')))
t.true(error.statusCode === 404)
t.true(error.response.body.includes('Cannot GET /users/1'))
})
test.serial('nuxt re-generating with no subfolders', async t => {
const logSpy = await interceptLog()
nuxt.options.generate.subFolders = false
await generator.generate()
release()
t.true(logSpy.calledWithMatch('DONE'))
})
test.serial('/users/1.html', async t => {
const html = await rp(url('/users/1.html'))
t.true(html.includes('<h1>User: 1</h1>'))
t.true(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1.html')))
t.false(
existsSync(resolve(__dirname, 'fixtures/basic/dist', 'users/1/index.html'))
)
})
test.serial('/-ignored', async t => {
const error = await t.throws(rp(url('/-ignored')))
t.true(error.statusCode === 404)
t.true(error.response.body.includes('Cannot GET /-ignored'))
})
test.serial('/ignored.test', async t => {
const error = await t.throws(rp(url('/ignored.test')))
t.true(error.statusCode === 404)
t.true(error.response.body.includes('Cannot GET /ignored.test'))
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server', async t => {
await server.close()
})

View File

@ -1,125 +0,0 @@
import { resolve } from 'path'
import test from 'ava'
import rp from 'request-promise-native'
import { Nuxt, Builder } from '..'
import { interceptLog } from './helpers/console'
const port = 4005
const url = route => 'http://localhost:' + port + route
// Init nuxt.js and create server listening on localhost:4005
const startCSPTestServer = async (t, csp) => {
const options = {
rootDir: resolve(__dirname, 'fixtures/basic'),
buildDir: '.nuxt-ssr-csp',
dev: false,
head: {
titleTemplate(titleChunk) {
return titleChunk ? `${titleChunk} - Nuxt.js` : 'Nuxt.js'
}
},
build: { stats: false },
render: { csp }
}
let nuxt = null
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(options)
const builder = await new Builder(nuxt)
await builder.build()
await nuxt.listen(port, '0.0.0.0')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
return nuxt
}
test.serial('Not contain Content-Security-Policy header, when csp.enabled is not set', async t => {
const nuxt = await startCSPTestServer(t, {})
const { headers } = await rp(url('/stateless'), {
resolveWithFullResponse: true
})
t.is(headers['content-security-policy'], undefined)
await nuxt.close()
})
test.serial('Contain Content-Security-Policy header, when csp.enabled is only set', async t => {
const cspOption = {
enabled: true
}
const nuxt = await startCSPTestServer(t, cspOption)
const { headers } = await rp(url('/stateless'), {
resolveWithFullResponse: true
})
t.regex(headers['content-security-policy'], /^script-src 'self' 'sha256-.*'$/)
await nuxt.close()
})
test.serial('Contain Content-Security-Policy header, when csp.allowedSources set', async t => {
const cspOption = {
enabled: true,
allowedSources: ['https://example.com', 'https://example.io']
}
const nuxt = await startCSPTestServer(t, cspOption)
const { headers } = await rp(url('/stateless'), {
resolveWithFullResponse: true
})
t.regex(headers['content-security-policy'], /^script-src 'self' 'sha256-.*'/)
t.true(headers['content-security-policy'].includes('https://example.com'))
t.true(headers['content-security-policy'].includes('https://example.io'))
await nuxt.close()
})
test.serial('Contain Content-Security-Policy header, when csp.policies set', async t => {
const cspOption = {
enabled: true,
policies: {
'default-src': [`'none'`],
'script-src': ['https://example.com', 'https://example.io']
}
}
const nuxt = await startCSPTestServer(t, cspOption)
const { headers } = await rp(url('/stateless'), {
resolveWithFullResponse: true
})
t.regex(headers['content-security-policy'], /default-src 'none'/)
t.regex(headers['content-security-policy'], /script-src 'self' 'sha256-.*'/)
t.true(headers['content-security-policy'].includes('https://example.com'))
t.true(headers['content-security-policy'].includes('https://example.io'))
await nuxt.close()
})
test.serial('Contain Content-Security-Policy header, when csp.policies.script-src is not set', async t => {
const cspOption = {
enabled: true,
policies: {
'default-src': [`'none'`]
}
}
const nuxt = await startCSPTestServer(t, cspOption)
const { headers } = await rp(url('/stateless'), {
resolveWithFullResponse: true
})
t.regex(headers['content-security-policy'], /default-src 'none'/)
t.regex(headers['content-security-policy'], /script-src 'self' 'sha256-.*'/)
await nuxt.close()
})

View File

@ -1,312 +0,0 @@
import { resolve } from 'path'
import test from 'ava'
import rp from 'request-promise-native'
import { Nuxt, Builder } from '..'
import { interceptLog, interceptError, release } from './helpers/console'
const port = 4004
const url = route => 'http://localhost:' + port + route
let nuxt = null
// Init nuxt.js and create server listening on localhost:4004
test.serial('Init Nuxt.js', async t => {
const options = {
rootDir: resolve(__dirname, 'fixtures/basic'),
buildDir: '.nuxt-ssr',
dev: false,
head: {
titleTemplate(titleChunk) {
return titleChunk ? `${titleChunk} - Nuxt.js` : 'Nuxt.js'
}
},
build: {
stats: false
}
}
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(options)
const builder = await new Builder(nuxt)
await builder.build()
await nuxt.listen(port, '0.0.0.0')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test('/stateless', async t => {
const { html } = await nuxt.renderRoute('/stateless')
t.true(html.includes('<h1>My component!</h1>'))
})
/*
** Example of testing via dom checking
*/
test('/css', async t => {
const window = await nuxt.renderAndGetWindow(url('/css'))
const headHtml = window.document.head.innerHTML
t.true(headHtml.includes('color:red'))
const element = window.document.querySelector('.red')
t.not(element, null)
t.is(element.textContent, 'This is red')
t.is(element.className, 'red')
// t.is(window.getComputedStyle(element).color, 'red')
})
test('/postcss', async t => {
const window = await nuxt.renderAndGetWindow(url('/css'))
const headHtml = window.document.head.innerHTML
t.true(headHtml.includes('background-color:blue'))
// const element = window.document.querySelector('div.red')
// t.is(window.getComputedStyle(element)['background-color'], 'blue')
})
test('/stateful', async t => {
const { html } = await nuxt.renderRoute('/stateful')
t.true(html.includes('<div><p>The answer is 42</p></div>'))
})
test('/store', async t => {
const { html } = await nuxt.renderRoute('/store')
t.true(html.includes('<h1>Vuex Nested Modules</h1>'))
t.true(html.includes('<p>1</p>'))
})
test.serial('/head', async t => {
const logSpy = await interceptLog()
const window = await nuxt.renderAndGetWindow(url('/head'), {
virtualConsole: false
})
t.is(window.document.title, 'My title - Nuxt.js')
const html = window.document.body.innerHTML
t.true(html.includes('<div><h1>I can haz meta tags</h1></div>'))
t.true(
html.includes('<script data-n-head="true" src="/body.js" data-body="true">')
)
const metas = window.document.getElementsByTagName('meta')
t.is(metas[0].getAttribute('content'), 'my meta')
release()
t.true(logSpy.calledOnce)
t.is(logSpy.args[0][0], 'Body script!')
})
test('/async-data', async t => {
const { html } = await nuxt.renderRoute('/async-data')
t.true(html.includes('<p>Nuxt.js</p>'))
})
test('/await-async-data', async t => {
const { html } = await nuxt.renderRoute('/await-async-data')
t.true(html.includes('<p>Await Nuxt.js</p>'))
})
test('/callback-async-data', async t => {
const { html } = await nuxt.renderRoute('/callback-async-data')
t.true(html.includes('<p>Callback Nuxt.js</p>'))
})
test('/users/1', async t => {
const { html } = await nuxt.renderRoute('/users/1')
t.true(html.includes('<h1>User: 1</h1>'))
})
test('/validate should display a 404', async t => {
const { html } = await nuxt.renderRoute('/validate')
t.true(html.includes('This page could not be found'))
})
test('/validate?valid=true', async t => {
const { html } = await nuxt.renderRoute('/validate?valid=true')
t.true(html.includes('<h1>I am valid</h1>'))
})
test('/redirect', async t => {
const { html, redirected } = await nuxt.renderRoute('/redirect')
t.true(html.includes('<div id="__nuxt"></div>'))
t.true(redirected.path === '/')
t.true(redirected.status === 302)
})
test('/redirect -> check redirected source', async t => {
// there are no transition properties in jsdom, ignore the error log
await interceptError()
const window = await nuxt.renderAndGetWindow(url('/redirect'))
release()
const html = window.document.body.innerHTML
t.true(html.includes('<h1>Index page</h1>'))
})
test('/redirect -> external link', async t => {
let _headers, _status
const { html } = await nuxt.renderRoute('/redirect-external', {
res: {
writeHead(status, headers) {
_status = status
_headers = headers
},
end() {}
}
})
t.is(_status, 302)
t.is(_headers.Location, 'https://nuxtjs.org')
t.true(html.includes('<div data-server-rendered="true"></div>'))
})
test('/special-state -> check window.__NUXT__.test = true', async t => {
const window = await nuxt.renderAndGetWindow(url('/special-state'))
t.is(window.document.title, 'Nuxt.js')
t.is(window.__NUXT__.test, true)
})
test('/error', async t => {
const err = await t.throws(nuxt.renderRoute('/error', { req: {}, res: {} }))
t.true(err.message.includes('Error mouahahah'))
})
test.serial('/error status code', async t => {
const errorSpy = await interceptError()
const err = await t.throws(rp(url('/error')))
t.true(err.statusCode === 500)
t.true(
err.response.body.includes(
'An error occurred in the application and your page could not be served'
)
)
release()
t.true(errorSpy.calledOnce)
t.true(errorSpy.args[0][0].message.includes('Error mouahahah'))
})
test('/error2', async t => {
const { html, error } = await nuxt.renderRoute('/error2')
t.true(html.includes('Custom error'))
t.true(error.message.includes('Custom error'))
t.true(error.statusCode === undefined)
})
test('/error2 status code', async t => {
const error = await t.throws(rp(url('/error2')))
t.is(error.statusCode, 500)
t.true(error.response.body.includes('Custom error'))
})
test.serial('/error-midd', async t => {
const errorSpy = await interceptError()
const err = await t.throws(rp(url('/error-midd')))
t.is(err.statusCode, 505)
t.true(err.response.body.includes('Middleware Error'))
release()
// Don't display error since redirect returns a noopApp
t.true(errorSpy.notCalled)
})
test.serial('/redirect-middleware', async t => {
const errorSpy = await interceptError()
await rp(url('/redirect-middleware')) // Should not console.error
release()
// Don't display error since redirect returns a noopApp
t.true(errorSpy.notCalled)
})
test('/redirect-name', async t => {
const { html, redirected } = await nuxt.renderRoute('/redirect-name')
t.true(html.includes('<div id="__nuxt"></div>'))
t.true(redirected.path === '/stateless')
t.true(redirected.status === 302)
})
test('/no-ssr', async t => {
const { html } = await nuxt.renderRoute('/no-ssr')
t.true(
html.includes(
'<div class="no-ssr-placeholder">&lt;p&gt;Loading...&lt;/p&gt;</div>'
)
)
})
test('/no-ssr (client-side)', async t => {
const window = await nuxt.renderAndGetWindow(url('/no-ssr'))
const html = window.document.body.innerHTML
t.true(html.includes('Displayed only on client-side</h1>'))
})
test('ETag Header', async t => {
const { headers: { etag } } = await rp(url('/stateless'), {
resolveWithFullResponse: true
})
// Verify functionality
const error = await t.throws(
rp(url('/stateless'), { headers: { 'If-None-Match': etag } })
)
t.is(error.statusCode, 304)
})
test('/_nuxt/server-bundle.json should return 404', async t => {
const err = await t.throws(
rp(url('/_nuxt/server-bundle.json'), { resolveWithFullResponse: true })
)
t.is(err.statusCode, 404)
})
test('/_nuxt/ should return 404', async t => {
const err = await t.throws(
rp(url('/_nuxt/'), { resolveWithFullResponse: true })
)
t.is(err.statusCode, 404)
})
test('/meta', async t => {
const { html } = await nuxt.renderRoute('/meta')
t.true(html.includes('"meta":[{"works":true}]'))
})
test('/fn-midd', async t => {
const err = await t.throws(
rp(url('/fn-midd'), { resolveWithFullResponse: true })
)
t.is(err.statusCode, 403)
t.true(err.response.body.includes('You need to ask the permission'))
})
test('/fn-midd?please=true', async t => {
const { html } = await nuxt.renderRoute('/fn-midd?please=true')
t.true(html.includes('<h1>Date:'))
})
test('/router-guard', async t => {
const { html } = await nuxt.renderRoute('/router-guard')
t.true(html.includes('<p>Nuxt.js</p>'))
t.false(html.includes('Router Guard'))
})
test('/jsx', async t => {
const { html } = await nuxt.renderRoute('/jsx')
t.true(html.includes('<h1>JSX Page</h1>'))
})
test('/jsx-link', async t => {
const { html } = await nuxt.renderRoute('/jsx-link')
t.true(html.includes('<h1>JSX Link Page</h1>'))
})
test('/js-link', async t => {
const { html } = await nuxt.renderRoute('/js-link')
t.true(html.includes('<h1>vue file is first-class</h1>'))
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -1,149 +0,0 @@
import { resolve } from 'path'
import test from 'ava'
import { Nuxt, Builder, Utils } from '..'
import * as browser from './helpers/browser'
import { interceptLog } from './helpers/console'
const port = 4014
const url = route => 'http://localhost:' + port + route
let nuxt = null
let page
const dates = {}
// Init nuxt.js and create server listening on localhost:4000
test.serial('Init Nuxt.js', async t => {
const options = {
rootDir: resolve(__dirname, 'fixtures/children'),
buildDir: '.nuxt-patch',
dev: false,
build: {
stats: false
}
}
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(options)
await new Builder(nuxt).build()
await nuxt.listen(port, 'localhost')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test.serial('Start browser', async t => {
t.plan(0) // suppress 'no assertions' warning
await browser.start({
// slowMo: 50,
// headless: false
})
})
test.serial('Loading /patch and keep ', async t => {
page = await browser.page(url('/patch'))
const h1 = await page.$text('h1')
t.true(h1.includes('patch:'))
const h2 = await page.$text('h2')
t.is(h2, 'Index')
dates.patch = await page.$text('[data-date-patch]')
})
test.serial('Navigate to /patch/1', async t => {
const { hook } = await page.nuxt.navigate('/patch/1', false)
const loading = await page.nuxt.loadingData()
t.is(loading.show, true)
await hook
const h2 = await page.$text('h2')
t.true(h2.includes('_id:'))
dates.id = await page.$text('[data-date-id]')
t.is(dates.patch, await page.$text('[data-date-patch]'))
})
test.serial('Navigate to /patch/2', async t => {
await page.nuxt.navigate('/patch/2')
const date = await page.$text('[data-date-id]')
t.is(await page.$text('h3'), 'Index')
t.is(dates.patch, await page.$text('[data-date-patch]'))
t.true(+dates.id < +date)
dates.id = date
})
test.serial('Navigate to /patch/2?test=true', async t => {
await page.nuxt.navigate('/patch/2?test=true')
t.is(dates.patch, await page.$text('[data-date-patch]'))
t.is(dates.id, await page.$text('[data-date-id]'))
})
test.serial('Navigate to /patch/2#test', async t => {
await page.nuxt.navigate('/patch/2#test')
t.is(dates.patch, await page.$text('[data-date-patch]'))
t.is(dates.id, await page.$text('[data-date-id]'))
})
test.serial('Navigate to /patch/2/child', async t => {
await page.nuxt.navigate('/patch/2/child')
dates.child = await page.$text('[data-date-child]')
dates.slug = await page.$text('[data-date-child-slug]')
t.is(dates.patch, await page.$text('[data-date-patch]'))
t.is(dates.id, await page.$text('[data-date-id]'))
t.true(+dates.child > +dates.id)
t.true(+dates.slug > +dates.child)
})
test.serial('Navigate to /patch/2/child/1', async t => {
await page.nuxt.navigate('/patch/2/child/1')
const date = await page.$text('[data-date-child-slug]')
t.is(dates.patch, await page.$text('[data-date-patch]'))
t.is(dates.id, await page.$text('[data-date-id]'))
t.is(dates.child, await page.$text('[data-date-child]'))
t.true(+date > +dates.slug)
dates.slug = date
})
test.serial('Navigate to /patch/2/child/1?foo=bar', async t => {
await page.nuxt.navigate('/patch/2/child/1?foo=bar')
t.is(dates.patch, await page.$text('[data-date-patch]'))
t.is(dates.id, await page.$text('[data-date-id]'))
t.is(dates.child, await page.$text('[data-date-child]'))
t.is(dates.slug, await page.$text('[data-date-child-slug]'))
})
test.serial('Search a country', async t => {
const countries = await page.$$text('[data-test-search-result]')
t.is(countries.length, 5)
await page.type('[data-test-search-input]', 'gu')
await Utils.waitFor(250)
const newCountries = await page.$$text('[data-test-search-result]')
t.is(newCountries.length, 1)
t.deepEqual(newCountries, ['Guinea'])
t.deepEqual(await page.nuxt.routeData(), {
path: '/patch/2/child/1',
query: {
foo: 'bar',
q: 'gu'
}
})
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})
test.after.always('Stop browser', async t => {
await page.close()
await browser.stop()
})

View File

@ -1,71 +0,0 @@
import { resolve } from 'path'
import test from 'ava'
import { Nuxt, Builder } from '..'
import { interceptLog } from './helpers/console'
const port = 4013
// const url = (route) => 'http://localhost:' + port + route
let nuxt = null
// Init nuxt.js and create server listening on localhost:4000
test.serial('Init Nuxt.js', async t => {
const options = {
rootDir: resolve(__dirname, 'fixtures/children'),
dev: false,
build: {
stats: false
}
}
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(options)
await new Builder(nuxt).build()
await nuxt.listen(port, 'localhost')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test('/parent', async t => {
const { html } = await nuxt.renderRoute('/parent')
t.true(html.includes('<h1>I am the parent</h1>'))
})
test('/parent/child', async t => {
const { html } = await nuxt.renderRoute('/parent/child')
t.true(html.includes('<h1>I am the parent</h1>'))
t.true(html.includes('<h2>I am the child</h2>'))
})
test('/parent should call _id.vue', async t => {
const { html } = await nuxt.renderRoute('/parent')
t.true(html.includes('<h1>I am the parent</h1>'))
t.true(html.includes('<h2>Id=</h2>'))
})
test('/parent/1', async t => {
const { html } = await nuxt.renderRoute('/parent/1')
t.true(html.includes('<h1>I am the parent</h1>'))
t.true(html.includes('<h2>Id=1</h2>'))
})
test('/parent/validate-child should display 404', async t => {
const { html } = await nuxt.renderRoute('/parent/validate-child')
t.true(html.includes('This page could not be found'))
})
test('/parent/validate-child?key=12345', async t => {
const { html } = await nuxt.renderRoute('/parent/validate-child?key=12345')
t.true(html.includes('<h1>I am the parent</h1>'))
t.true(html.includes('<h2>Child valid</h2>'))
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -1,96 +0,0 @@
import { promisify } from 'util'
import test from 'ava'
import { resolve } from 'path'
import rp from 'request-promise-native'
import { exec, spawn } from 'child_process'
import { Utils } from '..'
const execify = promisify(exec)
const rootDir = resolve(__dirname, 'fixtures/basic')
const port = 4011
const url = route => 'http://localhost:' + port + route
const nuxtBin = resolve(__dirname, '..', 'bin', 'nuxt')
test.serial('nuxt build', async t => {
const { stdout } = await execify(`node ${nuxtBin} build ${rootDir}`)
t.true(stdout.includes('Compiled successfully'))
})
test.serial('nuxt build -> error config', async t => {
const { stderr } = await t.throws(execify(`node ${nuxtBin} build ${rootDir} -c config.js`))
t.true(stderr.includes('Could not load config file'))
})
test.serial('nuxt start', async t => {
let stdout = ''
// let stderr = ''
let error
let exitCode
const env = process.env
env.PORT = port
const nuxtStart = spawn('node', [nuxtBin, 'start', rootDir], { env: env })
nuxtStart.stdout.on('data', data => {
stdout += data
})
nuxtStart.stderr.on('data', data => {
// stderr += data
})
nuxtStart.on('error', err => {
error = err
})
nuxtStart.on('close', code => {
exitCode = code
})
// Give the process max 20s to start
let iterator = 0
while (!stdout.includes('OPEN') && iterator < 80) {
await Utils.waitFor(250)
iterator++
}
if (iterator === 80) {
t.log('WARN: server failed to start successfully in 20 seconds')
}
t.is(error, undefined)
t.true(stdout.includes('OPEN'))
const html = await rp(url('/users/1'))
t.true(html.includes('<h1>User: 1</h1>'))
nuxtStart.kill()
// Wait max 10s for the process to be killed
iterator = 0
// eslint-disable-next-line no-unmodified-loop-condition
while (exitCode === undefined && iterator < 40) {
await Utils.waitFor(250)
iterator++
}
if (iterator >= 40) {
t.log(
`WARN: we were unable to automatically kill the child process with pid: ${
nuxtStart.pid
}`
)
}
t.is(exitCode, null)
})
test.serial('nuxt generate', async t => {
const { stdout } = await execify(`node ${nuxtBin} generate ${rootDir}`)
t.true(stdout.includes('vue-ssr-client-manifest.json'))
})

View File

@ -1,59 +0,0 @@
import test from 'ava'
import rp from 'request-promise-native'
import { Nuxt, Builder } from '..'
import { interceptLog } from './helpers/console'
import { loadConfig } from './helpers/config'
const port = 4007
const url = route => 'http://localhost:' + port + route
let nuxt = null
let builder = null
// Init nuxt.js and create server listening on localhost:4000
test.before('Init Nuxt.js', async t => {
const config = loadConfig('/custom-dirs', { dev: false })
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(config)
builder = new Builder(nuxt)
await builder.build()
await nuxt.listen(4007, 'localhost')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test('custom assets directory', async t => {
const { html } = await nuxt.renderRoute('/')
t.true(html.includes('.global-css-selector'))
})
test('custom layouts directory', async t => {
const { html } = await nuxt.renderRoute('/')
t.true(html.includes('<p>I have custom layouts directory</p>'))
})
test('custom middleware directory', async t => {
const window = await nuxt.renderAndGetWindow(url('/user-agent'))
const html = window.document.body.innerHTML
t.true(html.includes('<pre>Mozilla'))
})
test('custom pages directory', async t => {
const { html } = await nuxt.renderRoute('/')
t.true(html.includes('<h1>I have custom pages directory</h1>'))
})
test('custom static directory', async t => {
const { headers } = await rp(url('/test.txt'), {
resolveWithFullResponse: true
})
t.is(headers['cache-control'], 'public, max-age=0')
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -1,102 +0,0 @@
import test from 'ava'
import rp from 'request-promise-native'
import { Nuxt, Builder } from '..'
import { interceptLog, interceptError, release } from './helpers/console'
import { loadConfig } from './helpers/config'
const port = 4009
const url = route => 'http://localhost:' + port + route
let nuxt = null
// Init nuxt.js and create server listening on localhost:4000
test.before('Init Nuxt.js', async t => {
const config = loadConfig('debug')
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(config)
await new Builder(nuxt).build()
await nuxt.listen(port, 'localhost')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test.serial('/test/__open-in-editor (open-in-editor)', async t => {
const { body } = await rp(
url('/test/__open-in-editor?file=pages/index.vue'),
{ resolveWithFullResponse: true }
)
t.is(body, '')
})
test.serial(
'/test/__open-in-editor should return error (open-in-editor)',
async t => {
const { error, statusCode } = await t.throws(
rp(url('/test/__open-in-editor?file='), { resolveWithFullResponse: true })
)
t.is(statusCode, 500)
t.is(
error,
'launch-editor-middleware: required query param "file" is missing.'
)
}
)
test.serial('/test/error should return error stack trace (Youch)', async t => {
const errorSpy = await interceptError()
const { response, error } = await t.throws(
nuxt.renderAndGetWindow(url('/test/error'))
)
t.is(response.statusCode, 500)
t.is(response.statusMessage, 'NuxtServerError')
t.true(error.includes('test youch !'))
t.true(error.includes('<div class="error-frames">'))
release()
t.true(errorSpy.calledTwice)
t.true(errorSpy.getCall(0).args[0].includes('test youch !'))
t.true(errorSpy.getCall(1).args[0].message.includes('test youch !'))
})
test.serial('/test/error no source-map (Youch)', async t => {
const sourceMaps = nuxt.renderer.resources.serverBundle.maps
nuxt.renderer.resources.serverBundle.maps = {}
const errorSpy = await interceptError()
const { response, error } = await t.throws(
nuxt.renderAndGetWindow(url('/test/error'))
)
t.is(response.statusCode, 500)
t.is(response.statusMessage, 'NuxtServerError')
t.true(error.includes('test youch !'))
t.true(error.includes('<div class="error-frames">'))
release()
t.true(errorSpy.calledTwice)
t.true(errorSpy.getCall(0).args[0].includes('test youch !'))
t.true(errorSpy.getCall(1).args[0].message.includes('test youch !'))
nuxt.renderer.resources.serverBundle.maps = sourceMaps
})
test.serial('/test/error should return json format error (Youch)', async t => {
const opts = {
headers: {
accept: 'application/json'
},
resolveWithFullResponse: true
}
const errorSpy = await interceptError()
const { response: { headers } } = await t.throws(rp(url('/test/error'), opts))
t.is(headers['content-type'], 'text/json; charset=utf-8')
release()
t.true(errorSpy.calledTwice)
t.true(errorSpy.getCall(0).args[0].includes('test youch !'))
t.true(errorSpy.getCall(1).args[0].message.includes('test youch !'))
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -1,34 +0,0 @@
import test from 'ava'
import { Nuxt, Builder } from '..'
import { intercept } from './helpers/console'
import { loadConfig } from './helpers/config'
const port = 4010
let nuxt = null
let builder = null
let buildSpies = null
// Init nuxt.js and create server listening on localhost:4000
test.serial('Init Nuxt.js', async t => {
const config = loadConfig('deprecate', { dev: false })
buildSpies = await intercept(async () => {
nuxt = new Nuxt(config)
builder = await new Builder(nuxt)
await builder.build()
await nuxt.listen(port, 'localhost')
})
t.true(buildSpies.log.calledWithMatch('DONE'))
t.true(buildSpies.log.calledWithMatch('OPEN'))
})
test.serial('Deprecated: module.addVendor()', async t => {
t.true(buildSpies.warn.calledWithMatch('module: addVendor is no longer necessary'))
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -1,122 +0,0 @@
import { resolve } from 'path'
import fs from 'fs'
import { promisify } from 'util'
import test from 'ava'
import { Nuxt, Builder } from '..'
import { interceptLog } from './helpers/console'
const readFile = promisify(fs.readFile)
test.serial('Init Nuxt.js', async t => {
const config = {
rootDir: resolve(__dirname, 'fixtures/dynamic-routes'),
dev: false,
build: {
stats: false
}
}
const logSpy = await interceptLog(async () => {
const nuxt = new Nuxt(config)
await new Builder(nuxt).build()
})
t.true(logSpy.calledWithMatch('DONE'))
})
test('Check .nuxt/router.js', t => {
return readFile(
resolve(__dirname, './fixtures/dynamic-routes/.nuxt/router.js'),
'utf-8'
).then(routerFile => {
routerFile = routerFile
.slice(routerFile.indexOf('routes: ['))
.replace('routes: [', '[')
.replace(/ _[0-9A-z]+,/g, ' "",')
routerFile = routerFile.substr(
routerFile.indexOf('['),
routerFile.lastIndexOf(']') + 1
)
let routes = eval('( ' + routerFile + ')') // eslint-disable-line no-eval
// pages/test/index.vue
t.is(routes[0].path, '/test')
t.is(routes[0].name, 'test')
// pages/posts.vue
t.is(routes[1].path, '/posts')
t.is(routes[1].name, 'posts')
t.is(routes[1].children.length, 1)
// pages/posts/_id.vue
t.is(routes[1].children[0].path, ':id?')
t.is(routes[1].children[0].name, 'posts-id')
// pages/parent.vue
t.is(routes[2].path, '/parent')
t.falsy(routes[2].name) // parent route has no name
// pages/parent/*.vue
t.is(routes[2].children.length, 3) // parent has 3 children
t.deepEqual(routes[2].children.map(r => r.path), ['', 'teub', 'child'])
t.deepEqual(routes[2].children.map(r => r.name), [
'parent',
'parent-teub',
'parent-child'
])
// pages/test/projects/index.vue
t.is(routes[3].path, '/test/projects')
t.is(routes[3].name, 'test-projects')
// pages/test/users.vue
t.is(routes[4].path, '/test/users')
t.falsy(routes[4].name) // parent route has no name
// pages/test/users/*.vue
t.is(routes[4].children.length, 5) // parent has 5 children
t.deepEqual(routes[4].children.map(r => r.path), [
'',
'projects',
'projects/:category',
':id',
':index/teub'
])
t.deepEqual(routes[4].children.map(r => r.name), [
'test-users',
'test-users-projects',
'test-users-projects-category',
'test-users-id',
'test-users-index-teub'
])
// pages/test/songs/toto.vue
t.is(routes[5].path, '/test/songs/toto')
t.is(routes[5].name, 'test-songs-toto')
// pages/test/projects/_category.vue
t.is(routes[6].path, '/test/projects/:category')
t.is(routes[6].name, 'test-projects-category')
// pages/test/songs/_id.vue
t.is(routes[7].path, '/test/songs/:id?')
t.is(routes[7].name, 'test-songs-id')
// pages/users/_id.vue
t.is(routes[8].path, '/users/:id?')
t.is(routes[8].name, 'users-id')
// pages/test/_.vue
t.is(routes[9].path, '/test/*')
t.is(routes[9].name, 'test-all')
// pages/index.vue
t.is(routes[10].path, '/')
t.is(routes[10].name, 'index')
// pages/_slug.vue
t.is(routes[11].path, '/:slug')
t.is(routes[11].name, 'slug')
// pages/_key/_id.vue
t.is(routes[12].path, '/:key/:id?')
t.is(routes[12].name, 'key-id')
// pages/_.vue
t.is(routes[13].path, '/*/p/*')
t.is(routes[13].name, 'all-p-all')
// pages/_/_.vue
t.is(routes[14].path, '/*/*')
t.is(routes[14].name, 'all-all')
// pages/_.vue
t.is(routes[15].path, '/*')
t.is(routes[15].name, 'all')
})
})

View File

@ -0,0 +1,203 @@
import { Nuxt } from '../..'
import Browser from '../utils/browser'
import { loadFixture, getPort } from '../utils'
let port
const browser = new Browser()
const url = route => 'http://localhost:' + port + route
let nuxt = null
let page = null
describe('basic browser', () => {
beforeAll(async () => {
const config = loadFixture('basic')
nuxt = new Nuxt(config)
port = await getPort()
await nuxt.listen(port, 'localhost')
await browser.start({
// slowMo: 50,
// headless: false
})
})
test('Open /', async () => {
page = await browser.page(url('/'))
expect(await page.$text('h1')).toBe('Index page')
})
test('/stateless', async () => {
const { hook } = await page.nuxt.navigate('/stateless', false)
const loading = await page.nuxt.loadingData()
expect(loading.show).toBe(true)
await hook
expect(await page.$text('h1')).toBe('My component!')
})
test('/css', async () => {
await page.nuxt.navigate('/css')
expect(await page.$text('.red')).toBe('This is red')
expect(await page.$eval('.red', red => window.getComputedStyle(red).color)).toBe('rgb(255, 0, 0)')
})
test.skip('/stateful', async () => {
const { hook } = await page.nuxt.navigate('/stateful')
await hook
expect(await page.$text('p')).toBe('The answer is 42')
})
test('/store', async () => {
await page.nuxt.navigate('/store')
expect(await page.$text('h1')).toBe('Vuex Nested Modules')
expect(await page.$text('p')).toBe('1')
})
test('/head', async () => {
const msg = new Promise(resolve =>
page.on('console', msg => resolve(msg.text()))
)
await page.nuxt.navigate('/head')
const metas = await page.$$attr('meta', 'content')
expect(await msg).toBe('Body script!')
expect(await page.title()).toBe('My title - Nuxt.js')
expect(await page.$text('h1')).toBe('I can haz meta tags')
expect(metas[0]).toBe('my meta')
})
test('/async-data', async () => {
await page.nuxt.navigate('/async-data')
expect(await page.$text('p')).toBe('Nuxt.js')
})
test('/await-async-data', async () => {
await page.nuxt.navigate('/await-async-data')
expect(await page.$text('p')).toBe('Await Nuxt.js')
})
test('/callback-async-data', async () => {
await page.nuxt.navigate('/callback-async-data')
expect(await page.$text('p')).toBe('Callback Nuxt.js')
})
test('/users/1', async () => {
await page.nuxt.navigate('/users/1')
expect(await page.$text('h1')).toBe('User: 1')
})
test('/validate should display a 404', async () => {
await page.nuxt.navigate('/validate')
const error = await page.nuxt.errorData()
expect(error.statusCode).toBe(404)
expect(error.message).toBe('This page could not be found')
})
test('/validate?valid=true', async () => {
await page.nuxt.navigate('/validate?valid=true')
expect(await page.$text('h1')).toBe('I am valid')
})
test('/redirect', async () => {
await page.nuxt.navigate('/redirect')
expect(await page.$text('h1')).toBe('Index page')
})
test('/error', async () => {
await page.nuxt.navigate('/error')
expect(await page.nuxt.errorData()).toEqual({ statusCode: 500 })
expect(await page.$text('.title')).toBe('Error mouahahah')
})
test('/error2', async () => {
await page.nuxt.navigate('/error2')
expect(await page.$text('.title')).toBe('Custom error')
expect(await page.nuxt.errorData()).toEqual({ message: 'Custom error' })
})
test('/redirect-middleware', async () => {
await page.nuxt.navigate('/redirect-middleware')
expect(await page.$text('h1')).toBe('Index page')
})
test('/redirect-external', async () => {
// New page for redirecting to external link.
const page = await browser.page(url('/'))
await page.nuxt.navigate('/redirect-external', false)
await page.waitForFunction(
() => window.location.href === 'https://nuxtjs.org/'
)
page.close()
})
test('/redirect-name', async () => {
await page.nuxt.navigate('/redirect-name')
expect(await page.$text('h1')).toBe('My component!')
})
test('/no-ssr', async () => {
await page.nuxt.navigate('/no-ssr')
expect(await page.$text('h1')).toBe('Displayed only on client-side')
})
test('/meta', async () => {
await page.nuxt.navigate('/meta')
const state = await page.nuxt.storeState()
expect(state.meta).toEqual([{ works: true }])
})
test('/fn-midd', async () => {
await page.nuxt.navigate('/fn-midd')
expect(await page.$text('.title')).toBe('You need to ask the permission')
expect(await page.nuxt.errorData()).toEqual({
message: 'You need to ask the permission',
statusCode: 403
})
})
test('/fn-midd?please=true', async () => {
await page.nuxt.navigate('/fn-midd?please=true')
const h1 = await page.$text('h1')
expect(h1.includes('Date:')).toBe(true)
})
test('/router-guard', async () => {
await page.nuxt.navigate('/router-guard')
const p = await page.$text('p')
expect(p).toBe('Nuxt.js')
})
// Close server and ask nuxt to stop listening to file changes
test('Closing server and nuxt.js', async () => {
await nuxt.close()
})
test('Stop browser', async () => {
await page.close()
await browser.close()
})
})

View File

@ -0,0 +1,133 @@
import { Nuxt, Utils } from '../..'
import Browser from '../utils/browser'
import { loadFixture, getPort } from '../utils'
let port
const browser = new Browser()
const url = route => 'http://localhost:' + port + route
let nuxt = null
let page
const dates = {}
describe('children patch (browser)', () => {
beforeAll(async () => {
const options = loadFixture('children')
nuxt = new Nuxt(options)
port = await getPort()
await nuxt.listen(port, 'localhost')
})
test('Start browser', async () => {
expect.assertions(0) // suppress 'no assertions' warning
await browser.start({
// slowMo: 50,
// headless: false
})
})
test('Loading /patch and keep ', async () => {
page = await browser.page(url('/patch'))
const h1 = await page.$text('h1')
expect(h1.includes('patch:')).toBe(true)
const h2 = await page.$text('h2')
expect(h2).toBe('Index')
dates.patch = await page.$text('[data-date-patch]')
})
test('Navigate to /patch/1', async () => {
const { hook } = await page.nuxt.navigate('/patch/1', false)
const loading = await page.nuxt.loadingData()
expect(loading.show).toBe(true)
await hook
const h2 = await page.$text('h2')
expect(h2.includes('_id:')).toBe(true)
dates.id = await page.$text('[data-date-id]')
expect(dates.patch).toBe(await page.$text('[data-date-patch]'))
})
test('Navigate to /patch/2', async () => {
await page.nuxt.navigate('/patch/2')
const date = await page.$text('[data-date-id]')
expect(await page.$text('h3')).toBe('Index')
expect(dates.patch).toBe(await page.$text('[data-date-patch]'))
expect(+dates.id < +date).toBe(true)
dates.id = date
})
test('Navigate to /patch/2?test=true', async () => {
await page.nuxt.navigate('/patch/2?test=true')
expect(dates.patch).toBe(await page.$text('[data-date-patch]'))
expect(dates.id).toBe(await page.$text('[data-date-id]'))
})
test('Navigate to /patch/2#test', async () => {
await page.nuxt.navigate('/patch/2#test')
expect(dates.patch).toBe(await page.$text('[data-date-patch]'))
expect(dates.id).toBe(await page.$text('[data-date-id]'))
})
test('Navigate to /patch/2/child', async () => {
await page.nuxt.navigate('/patch/2/child')
dates.child = await page.$text('[data-date-child]')
dates.slug = await page.$text('[data-date-child-slug]')
expect(dates.patch).toBe(await page.$text('[data-date-patch]'))
expect(dates.id).toBe(await page.$text('[data-date-id]'))
expect(+dates.child > +dates.id).toBe(true)
expect(+dates.slug > +dates.child).toBe(true)
})
test('Navigate to /patch/2/child/1', async () => {
await page.nuxt.navigate('/patch/2/child/1')
const date = await page.$text('[data-date-child-slug]')
expect(dates.patch).toBe(await page.$text('[data-date-patch]'))
expect(dates.id).toBe(await page.$text('[data-date-id]'))
expect(dates.child).toBe(await page.$text('[data-date-child]'))
expect(+date > +dates.slug).toBe(true)
dates.slug = date
})
test('Navigate to /patch/2/child/1?foo=bar', async () => {
await page.nuxt.navigate('/patch/2/child/1?foo=bar')
expect(dates.patch).toBe(await page.$text('[data-date-patch]'))
expect(dates.id).toBe(await page.$text('[data-date-id]'))
expect(dates.child).toBe(await page.$text('[data-date-child]'))
expect(dates.slug).toBe(await page.$text('[data-date-child-slug]'))
})
test('Search a country', async () => {
const countries = await page.$$text('[data-test-search-result]')
expect(countries.length).toBe(5)
await page.type('[data-test-search-input]', 'gu')
await Utils.waitFor(250)
const newCountries = await page.$$text('[data-test-search-result]')
expect(newCountries.length).toBe(1)
expect(newCountries).toEqual(['Guinea'])
expect(await page.nuxt.routeData()).toEqual({
path: '/patch/2/child/1',
query: {
foo: 'bar',
q: 'gu'
}
})
})
// Close server and ask nuxt to stop listening to file changes
test('Closing server and nuxt.js', async () => {
await nuxt.close()
})
test('Stop browser', async () => {
await page.close()
await browser.close()
})
})

View File

@ -1,89 +0,0 @@
import test from 'ava'
import rp from 'request-promise-native'
import { Nuxt, Builder } from '..'
import { interceptLog, interceptError, release } from './helpers/console'
import { loadConfig } from './helpers/config'
const port = 4005
const url = route => 'http://localhost:' + port + route
let nuxt = null
let logSpy
// Init nuxt.js and create server listening on localhost:4000
test.serial('Init Nuxt.js', async t => {
const config = loadConfig('error', { dev: false })
logSpy = await interceptLog(async () => {
nuxt = new Nuxt(config)
await new Builder(nuxt).build()
await nuxt.listen(port, 'localhost')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test.serial('/ should display an error', async t => {
const error = await t.throws(nuxt.renderRoute('/'))
t.true(error.message.includes('not_defined is not defined'))
})
test.serial('/404 should display an error too', async t => {
let { error } = await nuxt.renderRoute('/404')
t.true(error.message.includes('This page could not be found'))
})
test.serial('/ with renderAndGetWindow()', async t => {
const errorSpy = await interceptError()
const err = await t.throws(nuxt.renderAndGetWindow(url('/')))
t.is(err.response.statusCode, 500)
t.is(err.response.statusMessage, 'NuxtServerError')
release()
t.true(errorSpy.calledOnce)
t.true(
errorSpy
.getCall(0)
.args[0].message.includes(
'render function or template not defined in component: anonymous'
)
)
})
test.serial('/ with text/json content', async t => {
const opts = {
headers: {
accept: 'application/json'
},
resolveWithFullResponse: true
}
const errorSpy = await interceptError()
const { response: { headers } } = await t.throws(rp(url('/'), opts))
t.is(headers['content-type'], 'text/json; charset=utf-8')
release()
t.true(errorSpy.calledOnce)
t.true(
errorSpy
.getCall(0)
.args[0].message.includes(
'render function or template not defined in component: anonymous'
)
)
})
test.serial('Deprecated: dev in build.extend()', async t => {
t.true(logSpy.calledWith('[build:done]: hook error'))
})
test.serial('Error: resolvePath()', async t => {
let error = t.throws(() => nuxt.resolvePath())
t.true(error instanceof TypeError)
error = t.throws(() => nuxt.resolvePath('@/pages/about.vue'))
t.true(error.message.includes('Cannot resolve "@/pages/about.vue"'))
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -1,47 +0,0 @@
import { resolve } from 'path'
import test from 'ava'
import express from 'express'
import rp from 'request-promise-native'
import { Nuxt, Builder } from '..'
import { interceptLog } from './helpers/console'
const port = 4000
const url = route => 'http://localhost:' + port + route
let nuxt
let app
// Init nuxt.js and create express server
test.serial('Init Nuxt.js', async t => {
const config = {
rootDir: resolve(__dirname, 'fixtures/basic'),
buildDir: '.nuxt-express',
dev: false,
build: {
stats: false
}
}
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(config)
await new Builder(nuxt).build()
})
t.true(logSpy.calledWithMatch('DONE'))
// Create express app
app = express()
// Register nuxt
app.use(nuxt.render)
// Start listening on localhost:4000
app.listen(port)
})
test('/stateless with express', async t => {
const html = await rp(url('/stateless'))
t.true(html.includes('<h1>My component!</h1>'))
})

View File

@ -1,116 +0,0 @@
import http from 'http'
import { existsSync } from 'fs'
import { resolve } from 'path'
import test from 'ava'
import serveStatic from 'serve-static'
import finalhandler from 'finalhandler'
import rp from 'request-promise-native'
import { Nuxt, Builder, Generator, Options } from '..'
import { intercept, interceptLog } from './helpers/console'
import { loadConfig } from './helpers/config'
const port = 4015
const url = route => 'http://localhost:' + port + route
let nuxt = null
let server = null
let generator = null
// Init nuxt.js and create server listening on localhost:4015
test.serial('Init Nuxt.js', async t => {
let config = loadConfig('basic', {
buildDir: '.nuxt-spa-fallback',
dev: false
})
config.build.stats = false
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(config)
const builder = new Builder(nuxt)
generator = new Generator(nuxt, builder)
await generator.generate()
})
t.true(logSpy.calledWithMatch('DONE'))
const serve = serveStatic(resolve(__dirname, 'fixtures/basic/dist'))
server = http.createServer((req, res) => {
serve(req, res, finalhandler(req, res))
})
server.listen(port)
})
test.serial('default creates /200.html as fallback', async t => {
const html = await rp(url('/200.html'))
t.false(html.includes('<h1>Index page</h1>'))
t.false(html.includes('data-server-rendered'))
t.true(existsSync(resolve(__dirname, 'fixtures/basic/dist', '200.html')))
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', '404.html')))
})
test.serial('nuxt re-generating with generate.fallback = false', async t => {
const logSpy = await interceptLog(async () => {
nuxt.options.generate.fallback = false
await generator.generate()
})
t.true(logSpy.calledWithMatch('DONE'))
})
test.serial('false creates no fallback', async t => {
const error = await t.throws(rp(url('/200.html')))
t.true(error.statusCode === 404)
t.true(error.response.body.includes('Cannot GET /200.html'))
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', '200.html')))
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', '404.html')))
})
test.serial('generate.fallback = true is transformed to /404.html', async t => {
nuxt.options.generate.fallback = true
const options = Options.from(nuxt.options)
t.is(options.generate.fallback, '404.html')
})
test.serial('nuxt re-generating with generate.fallback = "spa-fallback.html"', async t => {
const logSpy = await interceptLog(async () => {
nuxt.options.generate.fallback = 'spa-fallback.html'
await generator.generate()
})
t.true(logSpy.calledWithMatch('DONE'))
})
test.serial('"spa-fallback.html" creates /spa-fallback.html as fallback', async t => {
const html = await rp(url('/spa-fallback.html'))
t.false(html.includes('<h1>Index page</h1>'))
t.false(html.includes('data-server-rendered'))
t.true(
existsSync(resolve(__dirname, 'fixtures/basic/dist', 'spa-fallback.html'))
)
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', '404.html')))
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', '200.html')))
})
test.serial('nuxt re-generating with generate.fallback = "index.html"', async t => {
const {log: logSpy, warn: warnSpy} = await intercept({warn: true, log: true}, async () => {
nuxt.options.generate.fallback = 'index.html'
await generator.generate()
})
t.true(warnSpy.calledWithMatch('WARN')) // Must emit warnning
t.true(logSpy.calledWithMatch('DONE'))
})
test.serial('"index.html" creates /index.html as fallback', async t => {
const html = await rp(url('/index.html'))
t.true(html.includes('<h1>Index page</h1>'))
t.true(html.includes('data-server-rendered'))
t.true(existsSync(resolve(__dirname, 'fixtures/basic/dist', 'index.html')))
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', '404.html')))
t.false(existsSync(resolve(__dirname, 'fixtures/basic/dist', '200.html')))
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server', async t => {
await server.close()
})

View File

@ -3,6 +3,15 @@ import path from 'path'
export default { export default {
generate: { generate: {
routes: [ routes: [
// TODO: generate with {build: false} does not scans pages!
'/stateless',
'/css',
'/stateful',
'/head',
'/async-data',
'/validate',
'/redirect',
'/users/1', '/users/1',
'/users/2', '/users/2',
{ route: '/users/3', payload: { id: 3000 } } { route: '/users/3', payload: { id: 3000 } }
@ -10,6 +19,11 @@ export default {
interval: 200, interval: 200,
subFolders: true subFolders: true
}, },
head: {
titleTemplate: (titleChunk) => {
return titleChunk ? `${titleChunk} - Nuxt.js` : 'Nuxt.js'
}
},
modulesDir: path.join(__dirname, '..', '..', '..', 'node_modules'), modulesDir: path.join(__dirname, '..', '..', '..', 'node_modules'),
hooks: { hooks: {
ready(nuxt) { ready(nuxt) {

View File

@ -1,12 +1,4 @@
export default { export default {
hooks(hook) {
hook('build:done', nuxt => {
throw new Error('hook error')
})
hook('error', ({message}, from) => {
console.log(`[${from}]: ${message}`) // eslint-disable-line no-console
})
},
build: { build: {
stats: false stats: false
} }

View File

@ -19,8 +19,4 @@ export default function () {
this.nuxt.hook('build:done', (builder) => { this.nuxt.hook('build:done', (builder) => {
this.nuxt.__builder_plugin = builder && ctr++ this.nuxt.__builder_plugin = builder && ctr++
}) })
this.nuxt.hook('build:extendRoutes', (builder) => {
throw new Error('hook error testing')
})
} }

View File

@ -1,76 +0,0 @@
import test from 'ava'
import { Nuxt, Generator } from '..'
test('initRoutes with routes (fn => array)', async t => {
const array = ['/1', '/2', '/3', '/4']
const config = {
generate: {
routes: array
}
}
const nuxt = new Nuxt(config)
const generator = new Generator(nuxt)
const routes = await generator.initRoutes()
t.is(routes.length, array.length)
routes.map((route, index) => {
t.is(route.route, array[index])
})
})
test('initRoutes with routes (fn())', async t => {
const array = ['/1', '/2', '/3', '/4']
const config = {
generate: {
routes() {
return array
}
}
}
const nuxt = new Nuxt(config)
const generator = new Generator(nuxt)
const routes = await generator.initRoutes()
t.is(routes.length, array.length)
routes.map((route, index) => {
t.is(route.route, array[index])
})
})
test('initRoutes with routes (fn(args))', async t => {
const config = {
generate: {
routes(array) {
return array
}
}
}
const nuxt = new Nuxt(config)
const generator = new Generator(nuxt)
const array = ['/1', '/2', '/3', '/4']
const routes = await generator.initRoutes(array)
t.is(routes.length, array.length)
routes.map((route, index) => {
t.is(route.route, array[index])
})
})
test('initRoutes with routes (fn(cb, args))', async t => {
const config = {
generate: {
routes(cb, arg1, arg2, arg3, arg4) {
cb(null, [arg1, arg2, arg3, arg4])
}
}
}
const nuxt = new Nuxt(config)
const generator = new Generator(nuxt)
const array = ['/1', '/2', '/3', '/4']
const routes = await generator.initRoutes(...array)
t.is(routes.length, array.length)
routes.map((route, index) => {
t.is(route.route, array[index])
})
})

View File

@ -1,76 +0,0 @@
import puppeteer from 'puppeteer'
let browser = null
export async function start(options = {}) {
// https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions
browser = await puppeteer.launch(
Object.assign(
{
args: ['--no-sandbox', '--disable-setuid-sandbox']
},
options
)
)
}
export async function stop() {
if (!browser) return
await browser.close()
}
export async function page(url) {
if (!browser) throw new Error('Please call start() before page(url)')
const page = await browser.newPage()
await page.goto(url)
await page.waitForFunction('!!window.$nuxt')
page.html = () =>
page.evaluate(() => window.document.documentElement.outerHTML)
page.$text = selector => page.$eval(selector, el => el.textContent)
page.$$text = selector =>
page.$$eval(selector, els => els.map(el => el.textContent))
page.$attr = (selector, attr) =>
page.$eval(selector, (el, attr) => el.getAttribute(attr), attr)
page.$$attr = (selector, attr) =>
page.$$eval(
selector,
(els, attr) => els.map(el => el.getAttribute(attr)),
attr
)
page.$nuxt = await page.evaluateHandle('window.$nuxt')
page.nuxt = {
async navigate(path, waitEnd = true) {
const hook = page.evaluate(() => {
return new Promise(resolve =>
window.$nuxt.$once('routeChanged', resolve)
).then(() => new Promise(resolve => setTimeout(resolve, 50)))
})
await page.evaluate(
($nuxt, path) => $nuxt.$router.push(path),
page.$nuxt,
path
)
if (waitEnd) await hook
return { hook }
},
routeData() {
return page.evaluate($nuxt => {
return {
path: $nuxt.$route.path,
query: $nuxt.$route.query
}
}, page.$nuxt)
},
loadingData() {
return page.evaluate($nuxt => $nuxt.$loading.$data, page.$nuxt)
},
errorData() {
return page.evaluate($nuxt => $nuxt.nuxt.err, page.$nuxt)
},
storeState() {
return page.evaluate($nuxt => $nuxt.$store.state, page.$nuxt)
}
}
return page
}

View File

@ -1,11 +0,0 @@
import { resolve } from 'path'
import { requireModule } from '../../lib/common/module'
export function loadConfig(fixture, overrides) {
const rootDir = resolve(__dirname, '../fixtures/' + fixture)
const config = requireModule(resolve(rootDir, 'nuxt.config.js'))
return Object.assign({ rootDir }, config, overrides)
}

View File

@ -1,142 +0,0 @@
import sinon from 'sinon'
let context = null
export function release() {
if (context === null) {
process.stderr.write(
'Console spy context was empty, did a previous test already release it?\n'
)
return
}
if (context.log) {
console.log = context.log // eslint-disable-line no-console
}
if (context.info) {
console.info = context.info // eslint-disable-line no-console
}
if (context.warn) {
console.warn = context.warn // eslint-disable-line no-console
}
if (context.error) {
console.error = context.error // eslint-disable-line no-console
}
if (context.stdout) {
process.stdout.write = context.stdout
}
if (context.stderr) {
process.stderr.write = context.stderr
}
context = null
delete console.spiedInTest // eslint-disable-line no-console
}
export async function intercept(levels, msg, cb) {
if (context !== null) {
process.stderr.write(
'Console spy context was not empty, did a previous test not release it?\n'
)
}
context = {}
console.spiedInTest = true // eslint-disable-line no-console
if (cb === undefined && typeof msg === 'function') {
cb = msg
msg = undefined
if (typeof levels === 'string') {
msg = levels
levels = undefined
}
}
if (cb === undefined && msg === undefined && typeof levels === 'function') {
cb = levels
levels = undefined
}
const all = levels === undefined || levels === {}
const spies = {}
if (all || levels.log) {
context.log = console.log // eslint-disable-line no-console
spies.log = console.log = sinon.spy() // eslint-disable-line no-console
}
if (all || levels.info) {
context.info = console.info // eslint-disable-line no-console
spies.info = console.info = sinon.spy() // eslint-disable-line no-console
}
if (all || levels.warn) {
context.warn = console.warn // eslint-disable-line no-console
spies.warn = console.warn = sinon.spy() // eslint-disable-line no-console
}
if (all || levels.error) {
context.error = console.error // eslint-disable-line no-console
spies.error = console.error = sinon.spy() // eslint-disable-line no-console
}
if (levels && levels.stdout) {
context.stdout = process.stdout.write
spies.stdout = process.stdout.write = sinon.spy()
}
if (levels && levels.stderr) {
context.stderr = process.stderr.write
spies.stderr = process.stderr.write = sinon.spy()
}
if (cb) {
if (msg) {
if (context.stdout) {
context.stdout(` ${msg}`)
} else {
process.stdout.write(` ${msg}`)
}
}
await cb()
release()
if (msg) {
process.stdout.write('\n')
}
}
return spies
}
export async function interceptLog(msg, cb) {
const { log } = await intercept({ log: true }, msg, cb)
return log
}
export async function interceptInfo(msg, cb) {
const { info } = await intercept({ info: true }, msg, cb)
return info
}
export async function interceptWarn(msg, cb) {
const { warn } = await intercept({ warn: true }, msg, cb)
return warn
}
export async function interceptError(msg, cb) {
const { error } = await intercept({ error: true }, msg, cb)
return error
}
export async function interceptStdout(msg, cb) {
const { stdout } = await intercept({ stdout: true }, msg, cb)
return stdout
}
export async function interceptStderr(msg, cb) {
const { stderr } = await intercept({ stderr: true }, msg, cb)
return stderr
}

View File

@ -1,45 +0,0 @@
import test from 'ava'
import { resolve } from 'path'
import { Nuxt, Builder } from '..'
test('Nuxt.js Class', t => {
t.is(typeof Nuxt, 'function')
})
test('Nuxt.js Instance', async t => {
const nuxt = new Nuxt({
dev: true,
rootDir: resolve(__dirname, 'fixtures', 'empty')
})
t.is(typeof nuxt, 'object')
t.is(nuxt.options.dev, true)
t.is(typeof nuxt._ready.then, 'function')
await nuxt.ready()
t.is(nuxt.initialized, true)
})
test('Fail to build when no pages/ directory but is in the parent', t => {
const nuxt = new Nuxt({
dev: false,
rootDir: resolve(__dirname, 'fixtures', 'empty', 'pages')
})
return new Builder(nuxt).build().catch(err => {
let s = String(err)
t.true(s.includes('No `pages` directory found'))
t.true(
s.includes('Did you mean to run `nuxt` in the parent (`../`) directory?')
)
})
})
test('Fail to build when no pages/ directory', t => {
const nuxt = new Nuxt({
dev: false,
rootDir: resolve(__dirname)
})
return new Builder(nuxt).build().catch(err => {
let s = String(err)
t.true(s.includes("Couldn't find a `pages` directory"))
t.true(s.includes('Please create one under the project root'))
})
})

View File

@ -1,83 +0,0 @@
import { normalize } from 'path'
import test from 'ava'
import rp from 'request-promise-native'
import { Nuxt, Builder } from '..'
import { intercept } from './helpers/console'
import { loadConfig } from './helpers/config'
const port = 4006
const url = route => 'http://localhost:' + port + route
let nuxt = null
let builder = null
let buildSpies = null
// Init nuxt.js and create server listening on localhost:4000
test.serial('Init Nuxt.js', async t => {
const config = loadConfig('module', { dev: false })
nuxt = new Nuxt(config)
builder = new Builder(nuxt)
buildSpies = await intercept({ log: true, error: true }, async () => {
await builder.build()
await nuxt.listen(port, 'localhost')
})
t.true(buildSpies.log.calledWithMatch('DONE'))
t.true(buildSpies.log.calledWithMatch('OPEN'))
})
test.serial('Plugin', async t => {
t.true(
normalize(nuxt.options.plugins[0].src).includes(
normalize('fixtures/module/.nuxt/basic.reverse.')
),
'plugin added to config'
)
const { html } = await nuxt.renderRoute('/')
t.true(html.includes('<h1>TXUN</h1>'), 'plugin works')
})
test.serial('Layout', async t => {
t.true(
nuxt.options.layouts.layout.includes('layout'),
'layout added to config'
)
const { html } = await nuxt.renderRoute('/layout')
t.true(html.includes('<h1>Module Layouts</h1>'), 'layout works')
})
test.serial('Hooks', async t => {
t.is(nuxt.__module_hook, 1)
t.is(nuxt.__renderer_hook, 2)
t.is(nuxt.__builder_hook, 3)
})
test.serial('Hooks - Functional', async t => {
t.true(nuxt.__ready_called__)
t.true(builder.__build_done__)
})
test.serial('Hooks - Error', async t => {
t.true(buildSpies.error.calledWithMatch(/build:extendRoutes/))
})
test('Middleware', async t => {
let response = await rp(url('/api'))
t.is(response, 'It works!', '/api response is correct')
})
test('Hooks - Use external middleware before render', async t => {
let response = await rp(url('/use-middleware'))
t.is(response, 'Use external middleware')
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -1,84 +0,0 @@
import test from 'ava'
import { Nuxt, Builder } from '..'
import { interceptLog, release } from './helpers/console'
import { loadConfig } from './helpers/config'
let nuxt = null
const port = 4012
const url = route => 'http://localhost:' + port + route
const renderRoute = async _url => {
const window = await nuxt.renderAndGetWindow(url(_url))
const head = window.document.head.innerHTML
const html = window.document.body.innerHTML
return { window, head, html }
}
// Init nuxt.js and create server listening on localhost:4000
test.serial('Init Nuxt.js', async t => {
const config = loadConfig('spa')
const logSpy = await interceptLog(async () => {
nuxt = new Nuxt(config)
await new Builder(nuxt).build()
await nuxt.listen(port, 'localhost')
})
t.true(logSpy.calledWithMatch('DONE'))
t.true(logSpy.calledWithMatch('OPEN'))
})
test.serial('/ (basic spa)', async t => {
const logSpy = await interceptLog()
const { html } = await renderRoute('/')
t.true(html.includes('Hello SPA!'))
release()
t.true(logSpy.withArgs('created').notCalled)
t.true(logSpy.withArgs('mounted').calledOnce)
})
test.serial('/custom (custom layout)', async t => {
const logSpy = await interceptLog()
const { html } = await renderRoute('/custom')
t.true(html.includes('Custom layout'))
release()
t.true(logSpy.withArgs('created').calledOnce)
t.true(logSpy.withArgs('mounted').calledOnce)
})
test.serial('/custom (not default layout)', async t => {
const logSpy = await interceptLog()
const { head } = await renderRoute('/custom')
t.false(head.includes('src="/_nuxt/layouts/default.'))
release()
t.true(logSpy.withArgs('created').calledOnce)
t.true(logSpy.withArgs('mounted').calledOnce)
})
test.serial('/custom (call mounted and created once)', async t => {
const logSpy = await interceptLog()
await renderRoute('/custom')
release()
t.true(logSpy.withArgs('created').calledOnce)
t.true(logSpy.withArgs('mounted').calledOnce)
})
test.serial('/mounted', async t => {
const { html } = await renderRoute('/mounted')
t.true(html.includes('<h1>Test: updated</h1>'))
})
test('/_nuxt/ (access publicPath in spa mode)', async t => {
const { response: { statusCode, statusMessage } } = await t.throws(
renderRoute('/_nuxt/')
)
t.is(statusCode, 404)
t.is(statusMessage, 'ResourceNotFound')
})
// Close server and ask nuxt to stop listening to file changes
test.after.always('Closing server and nuxt.js', async t => {
await nuxt.close()
})

View File

@ -0,0 +1,15 @@
import { resolve } from 'path'
import { Nuxt, Options } from '../../'
import { version } from '../../package.json'
describe('basic config defaults', () => {
test('Nuxt.version is same as package', () => {
expect(Nuxt.version).toBe(version)
})
test('modulesDir uses /node_modules as default if not set', async () => {
const options = Options.from({})
const currentNodeModulesDir = resolve(__dirname, '..', '..', 'node_modules')
expect(options.modulesDir.includes(currentNodeModulesDir)).toBe(true)
})
})

Some files were not shown because too many files have changed in this diff Show More