2
0
mirror of https://github.com/tenrok/vue-meta.git synced 2026-06-13 14:02:25 +03:00

Merge branch 'next' into docs

This commit is contained in:
pimlie
2019-03-24 15:43:55 +01:00
12 changed files with 1324 additions and 648 deletions
+83 -42
View File
@@ -1,88 +1,129 @@
version: 2
version: 2.1
defaults: &defaults
working_directory: ~/project
docker:
- image: circleci/node:latest
environment:
NODE_ENV: test
executors:
node:
parameters:
browsers:
type: boolean
default: false
docker:
- image: circleci/node:latest<<# parameters.browsers >>-browsers<</ parameters.browsers >>
working_directory: ~/project
environment:
NODE_ENV: test
commands:
attach-project:
steps:
- checkout
- attach_workspace:
at: ~/project
jobs:
setup:
<<: *defaults
executor: node
steps:
# Checkout repository
- checkout
# Restore cache
- restore_cache:
key: yarn-{{ checksum "yarn.lock" }}
# Install dependencies
- run:
name: Install Dependencies
command: NODE_ENV=dev yarn
# Keep cache
- save_cache:
key: yarn-{{ checksum "yarn.lock" }}
paths:
- "node_modules"
# Persist workspace
- persist_to_workspace:
root: ~/project
paths:
- node_modules
lint:
<<: *defaults
executor: node
steps:
- checkout
- attach_workspace:
at: ~/project
- attach-project
- run:
name: Lint
command: yarn lint
audit:
<<: *defaults
executor: node
steps:
- checkout
- attach_workspace:
at: ~/project
- attach-project
- run:
name: Security Audit
command: yarn audit
test-unit:
<<: *defaults
executor: node
steps:
- checkout
- attach_workspace:
at: ~/project
- attach-project
- run:
name: Unit Tests
command: yarn test:unit --coverage && yarn coverage
test-e2e:
docker:
- image: circleci/node:latest-browsers
test-e2e-ssr:
executor: node
steps:
- checkout
- attach_workspace:
at: ~/project
- attach-project
- run:
name: E2E Tests
command: yarn test:e2e
name: E2E SSR Tests
command: yarn test:e2e-ssr
- persist_to_workspace:
root: ~/project
paths:
- test/fixtures
test-e2e-browser:
parameters:
browserString:
type: string
executor:
name: node
browsers: true
steps:
- attach-project
- run:
name: E2E Browser Tests
command: yarn test:e2e-browser
environment:
BROWSER_STRING: << parameters.browserString >>
workflows:
version: 2
version : 2
commit:
jobs:
- setup
- lint: { requires: [setup] }
- audit: { requires: [setup] }
- test-unit: { requires: [lint] }
- test-e2e: { requires: [lint] }
- lint: { requires: [setup] }
- audit: { requires: [setup] }
- test-unit: { requires: [lint] }
- test-e2e-ssr: { requires: [lint] }
- test-e2e-browser:
name: test-e2e-firefox
browserString: firefox/headless
requires: [test-e2e-ssr]
- test-e2e-browser:
name: test-e2e-chrome
browserString: chrome/selenium
requires: [test-e2e-ssr]
- test-e2e-browser:
name: test-e2e-ie
browserString: browserstack/local/windows 7/ie:9
requires: [test-e2e-ssr]
filters:
branches: { ignore: /^pull\/.*/ }
- test-e2e-browser:
name: test-e2e-edge
browserString: browserstack/local/edge:15
requires: [test-e2e-ssr]
filters:
branches: { ignore: /^pull\/.*/ }
- test-e2e-browser:
name: test-e2e-safari
browserString: browserstack/local/os x=snow leopard/safari:5.1
requires: [test-e2e-ssr]
filters:
branches: { ignore: /^pull\/.*/ }
+4
View File
@@ -1,6 +1,7 @@
# Logs
logs
*.log
*.err
npm-debug.log*
# Runtime data
@@ -44,3 +45,6 @@ es
# examples yarn lock
examples/yarn.lock
# env vars
.env*
+4 -2
View File
@@ -1,11 +1,13 @@
{
module.exports = {
"plugins": ["@babel/plugin-syntax-dynamic-import"],
"env": {
"test": {
"plugins": ["dynamic-import-node"],
"presets": [
[ "@babel/env", {
"targets": { "node": "current" }
targets: {
node: "current"
}
}]
]
}
+26 -18
View File
@@ -54,8 +54,9 @@
"prerelease": "git checkout master && git pull -r",
"release": "yarn lint && yarn test && yarn build && standard-version",
"postrelease": "git push origin master --follow-tags && yarn publish",
"test": "yarn test:unit && yarn test:e2e",
"test:e2e": "jest test/e2e",
"test": "yarn test:unit && yarn test:e2e-ssr && yarn test:e2e-browser",
"test:e2e-ssr": "jest test/e2e/ssr",
"test:e2e-browser": "jest test/e2e/browser",
"test:unit": "jest test/unit"
},
"dependencies": {
@@ -66,51 +67,58 @@
},
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.3.4",
"@babel/core": "^7.4.0",
"@babel/node": "^7.2.2",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/preset-env": "^7.3.4",
"@nuxt/babel-preset-app": "^2.4.5",
"@babel/preset-env": "^7.4.2",
"@nuxt/babel-preset-app": "^2.5.0",
"@nuxtjs/eslint-config": "^0.0.1",
"@vue/server-test-utils": "^1.0.0-beta.29",
"@vue/test-utils": "^1.0.0-beta.29",
"babel-core": "^7.0.0-bridge",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.4.0",
"babel-jest": "^24.5.0",
"babel-loader": "^8.0.5",
"babel-plugin-dynamic-import-node": "^2.2.0",
"browserstack-local": "^1.3.7",
"chromedriver": "^2.46.0",
"codecov": "^3.2.0",
"eslint": "^5.15.1",
"eslint": "^5.15.3",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.16.0",
"eslint-plugin-jest": "^22.3.0",
"eslint-plugin-jest": "^22.4.1",
"eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.0.1",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^5.2.2",
"esm": "^3.2.18",
"fs-extra": "^7.0.1",
"geckodriver": "^1.16.0",
"is-wsl": "^1.1.0",
"jest": "^24.4.0",
"jest-environment-jsdom": "^24.4.0",
"jest-environment-jsdom-global": "^1.1.1",
"jest": "^24.5.0",
"jest-environment-jsdom": "^24.5.0",
"jest-environment-jsdom-global": "^1.2.0",
"jsdom": "^14.0.0",
"lodash": "^4.17.11",
"node-env-file": "^0.1.8",
"puppeteer-core": "^1.13.0",
"rimraf": "^2.6.3",
"rollup": "^1.6.0",
"rollup": "^1.7.0",
"rollup-plugin-buble": "^0.19.6",
"rollup-plugin-commonjs": "^9.2.1",
"rollup-plugin-json": "^3.1.0",
"rollup-plugin-json": "^4.0.0",
"rollup-plugin-node-resolve": "^4.0.1",
"rollup-plugin-replace": "^2.1.0",
"rollup-plugin-replace": "^2.1.1",
"rollup-plugin-terser": "^4.0.4",
"standard-version": "^5.0.1",
"vue": "^2.6.8",
"selenium-webdriver": "^4.0.0-alpha.1",
"standard-version": "^5.0.2",
"tib": "^0.4.0",
"vue": "^2.6.10",
"vue-jest": "^3.0.4",
"vue-loader": "^15.7.0",
"vue-router": "^3.0.2",
"vue-server-renderer": "^2.6.8",
"vue-template-compiler": "^2.6.8",
"vue-server-renderer": "^2.6.10",
"vue-template-compiler": "^2.6.10",
"vuepress": "^0.14.10",
"vuepress-theme-vue": "^1.1.0",
"webpack": "^4.29.6"
+6 -3
View File
@@ -47,7 +47,7 @@ export default function createMixin(Vue, options) {
// coerce function-style metaInfo to a computed prop so we can observe
// it on creation
if (isFunction(this.$options[options.keyName])) {
if (isUndefined(this.$options.computed)) {
if (!this.$options.computed) {
this.$options.computed = {}
}
this.$options.computed.$metaInfo = this.$options[options.keyName]
@@ -82,7 +82,7 @@ export default function createMixin(Vue, options) {
}
})
// add the navigation guards if they havent been added yet
// add the navigation guards if requested
if (options.refreshOnceOnNavigation) {
addNavGuards(this)
}
@@ -94,7 +94,10 @@ export default function createMixin(Vue, options) {
// add the navigation guards if they havent been added yet
// if metaInfo is defined as a function, this does call the computed fn redundantly
// but as Vue internally caches the results of computed props it shouldnt hurt performance
if (!options.refreshOnceOnNavigation && this.$options[options.keyName].afterNavigation) {
if (!options.refreshOnceOnNavigation && (
(this.$options[options.keyName] && this.$options[options.keyName].afterNavigation) ||
(this.$options.computed && this.$options.computed.$metaInfo && (this.$options.computed.$metaInfo() || {}).afterNavigation)
)) {
addNavGuards(this)
}
+89 -52
View File
@@ -1,79 +1,116 @@
import Browser from '../utils/browser'
import { buildFixture } from '../utils/build'
/**
* @jest-environment node
*/
import fs from 'fs'
import path from 'path'
import env from 'node-env-file'
import { browser as startBrowser } from 'tib'
const browser = new Browser()
const browserString = process.env.BROWSER_STRING || 'puppeteer/core'
describe('basic browser with ssr page', () => {
let page = null
let url
let html
describe(browserString, () => {
let browser
let page
const folder = path.resolve(__dirname, '..', 'fixtures/basic/.vue-meta/')
beforeAll(async () => {
const fixture = await buildFixture('basic')
url = fixture.url
html = fixture.html
if (browserString.includes('browserstack') && browserString.includes('local')) {
const envFile = path.resolve(__dirname, '..', '..', '.env-browserstack')
if (fs.existsSync(envFile)) {
env(envFile)
}
}
await browser.start({
// slowMo: 50,
// headless: false
browser = await startBrowser(browserString, {
BrowserStackLocal: { folder },
extendPage(page) {
return {
async navigate(path) {
// IMPORTANT: use (arrow) function with block'ed body
// see: https://github.com/tunnckoCoreLabs/parse-function/issues/179
await page.runAsyncScript((path) => {
return new Promise((resolve) => {
const oldTitle = document.title
// local firefox has sometimes not updated the title
// even when the DOM is supposed to be fully updated
const waitTitleChanged = function () {
setTimeout(function () {
if (oldTitle !== document.title) {
resolve()
} else {
waitTitleChanged()
}
}, 50)
}
window.$vueMeta.$once('routeChanged', waitTitleChanged)
window.$vueMeta.$router.push(path)
})
}, path)
},
routeData() {
return page.runScript(() => ({
path: window.$vueMeta.$route.path,
query: window.$vueMeta.$route.query
}))
}
}
}
})
})
// Stop browser
afterAll(async () => {
if (page) await page.close()
await browser.close()
if (browser) {
await browser.close()
}
})
test('validate ssr', () => {
const htmlTag = html.match(/<html([^>]+)>/)[0]
expect(htmlTag).toContain('data-vue-meta-server-rendered')
expect(htmlTag).toContain(' lang="en" ')
expect(htmlTag).toContain(' amp ')
expect(htmlTag).not.toContain('allowfullscreen')
expect(html.match(/<title[^>]*>(.*?)<\/title>/)[1]).toBe('Home | Vue Meta Test')
expect(html.match(/<meta/g).length).toBe(2)
expect(html.match(/<meta/g).length).toBe(2)
test('open page', async () => {
const webPath = '/index.html'
const re = /<(no)?script[^>]+type="application\/ld\+json"[^>]*>(.*?)</g
const sanitizeCheck = []
let match
while ((match = re.exec(html))) {
sanitizeCheck.push(match[2])
let url
if (browser.getLocalFolderUrl) {
url = browser.getLocalFolderUrl(webPath)
} else {
url = `file://${path.join(folder, webPath)}`
}
expect(sanitizeCheck.length).toBe(3)
expect(() => JSON.parse(sanitizeCheck[0])).not.toThrow()
expect(() => JSON.parse(sanitizeCheck[1])).toThrow()
expect(() => JSON.parse(sanitizeCheck[2])).not.toThrow()
})
test('Open /', async () => {
page = await browser.page(url)
expect(await page.$attr('html', 'data-vue-meta-server-rendered')).toBe(null)
expect(await page.$attr('html', 'lang')).toBe('en')
expect(await page.$attr('html', 'amp')).toBe('')
expect(await page.$attr('html', 'allowfullscreen')).toBe(null)
expect(await page.$attr('head', 'test')).toBe('true')
expect(await page.$text('h1')).toBe('Basic')
expect(await page.$text('title')).toBe('Home | Vue Meta Test')
expect(await page.$$eval('meta', metas => metas.length)).toBe(2)
expect(await page.getAttribute('html', 'data-vue-meta-server-rendered')).toBe(null)
expect(await page.getAttribute('html', 'lang')).toBe('en')
expect(await page.getAttribute('html', 'amp')).toBe('')
expect(await page.getAttribute('html', 'allowfullscreen')).toBe(null)
expect(await page.getAttribute('head', 'test')).toBe('true')
expect(await page.getText('h1')).toBe('Basic')
expect(await page.getText('title')).toBe('Home | Vue Meta Test')
expect(await page.getElementCount('meta')).toBe(2)
let sanitizeCheck = await page.$$text('script')
sanitizeCheck.push(...(await page.$$text('noscript')))
let sanitizeCheck = await page.getTexts('script')
sanitizeCheck.push(...(await page.getTexts('noscript')))
sanitizeCheck = sanitizeCheck.filter(v => !!v)
expect(sanitizeCheck.length).toBe(3)
expect(() => JSON.parse(sanitizeCheck[0])).not.toThrow()
expect(() => JSON.parse(sanitizeCheck[1])).not.toThrow()
// TODO: check why this doesnt Throw when Home is dynamic loaded
// (but that causes hydration error)
expect(() => JSON.parse(sanitizeCheck[1])).toThrow()
expect(() => JSON.parse(sanitizeCheck[2])).not.toThrow()
})
test('/about', async () => {
const { hook } = await page.vueMeta.navigate('/about', false)
await hook
expect(await page.$text('title')).toBe('About')
expect(await page.$$eval('meta', metas => metas.length)).toBe(1)
try {
await page.navigate('/about', false)
} catch (e) {
if (e.constructor.name !== 'ScriptTimeoutError') {
throw e
} else {
console.warn(e) // eslint-disable-line no-console
}
}
expect(await page.getText('title')).toBe('About')
expect(await page.getElementCount('meta')).toBe(1)
})
})
+33
View File
@@ -0,0 +1,33 @@
import { buildFixture } from '../utils/build'
describe('basic browser with ssr page', () => {
let html
beforeAll(async () => {
const fixture = await buildFixture('basic')
html = fixture.html
})
test('validate ssr', () => {
const htmlTag = html.match(/<html([^>]+)>/)[0]
expect(htmlTag).toContain('data-vue-meta-server-rendered')
expect(htmlTag).toContain(' lang="en" ')
expect(htmlTag).toContain(' amp ')
expect(htmlTag).not.toContain('allowfullscreen')
expect(html.match(/<title[^>]*>(.*?)<\/title>/)[1]).toBe('Home | Vue Meta Test')
expect(html.match(/<meta/g).length).toBe(2)
expect(html.match(/<meta/g).length).toBe(2)
const re = /<(no)?script[^>]+type="application\/ld\+json"[^>]*>(.*?)</g
const sanitizeCheck = []
let match
while ((match = re.exec(html))) {
sanitizeCheck.push(match[2])
}
expect(sanitizeCheck.length).toBe(3)
expect(() => JSON.parse(sanitizeCheck[0])).not.toThrow()
expect(() => JSON.parse(sanitizeCheck[1])).toThrow()
expect(() => JSON.parse(sanitizeCheck[2])).not.toThrow()
})
})
+9 -5
View File
@@ -8,13 +8,17 @@
<script>
export default {
metaInfo: {
meta: [
{ vmid: 'charset', charset: 'utf-8' }
]
metaInfo() {
return {
meta: [
{ vmid: 'charset', charset: 'utf-8' }
],
afterNavigation: () => {
this.$emit('routeChanged')
}
}
},
mounted() {
this.$router.afterEach((to, from) => this.$emit('routeChanged', to, from))
window.$vueMeta = this
}
}
+1 -1
View File
@@ -1,9 +1,9 @@
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/home.vue'
Vue.use(Router)
const Home = () => import('./views/home.vue')
const Post = () => import('./views/about.vue')
export default function createRouter() {
+25 -2
View File
@@ -38,6 +38,7 @@ export async function buildFixture(fixture, config = {}) {
await fs.remove(webpackConfig.output.path)
// run webpack
process.env.NODE_ENV = 'test'
const webpackStats = await webpackRun(webpackConfig)
// for test debugging
@@ -73,6 +74,7 @@ export function createWebpackConfig(config = {}) {
return {
mode: 'development',
devtool: 'none',
output: {
path: path.join(path.dirname(config.entry), publicPath),
filename: '[name].js',
@@ -81,7 +83,22 @@ export function createWebpackConfig(config = {}) {
},
module: {
rules: [
{ test: /\.js$/, exclude: /node_modules/, use: 'babel-loader' },
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
useBuiltIns: 'usage',
corejs: 'core-js@2',
targets: { ie: 9, safari: '5.1' }
}]
]
}
}
},
{ test: /\.vue$/, use: 'vue-loader' }
]
},
@@ -102,7 +119,13 @@ export function createWebpackConfig(config = {}) {
}
},
plugins: [
new VueLoaderPlugin()
new VueLoaderPlugin(),
new webpack.DefinePlugin({
'process.env': {
// make sure our simple polyfills are enabled
'NODE_ENV': '"test"'
}
})
],
resolve: {
alias: {
+1 -1
View File
@@ -1,2 +1,2 @@
jest.useFakeTimers()
jest.setTimeout(15000)
jest.setTimeout(30000)
+1043 -522
View File
File diff suppressed because it is too large Load Diff