This commit is contained in:
The Jared Wilcurt 2019-09-16 00:49:40 -04:00
parent 89b3371869
commit 0c4f455122
16 changed files with 4071 additions and 351 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@ node_modules
/dist/ /dist/
/dist-vue/ /dist-vue/
tests/unit/coverage/**/*
/tests/e2e/videos/ /tests/e2e/videos/
/tests/e2e/screenshots/ /tests/e2e/screenshots/
/tests/e2e/reports/ /tests/e2e/reports/

View File

@ -1,10 +1,36 @@
process.env.VUE_CLI_BABEL_TARGET_NODE = true;
process.env.VUE_CLI_BABEL_TRANSPILE_MODULES = true;
module.exports = { module.exports = {
collectCoverageFrom: [
'src/**/*.{js,vue}',
'!src/main.js',
'!**/node_modules/**'
],
coverageDirectory: '<rootDir>/tests/unit/coverage',
moduleFileExtensions: [ moduleFileExtensions: [
'js', 'js',
'jsx', 'jsx',
'json', 'json',
'vue' 'vue'
], ],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
setupFilesAfterEnv: [
'<rootDir>/tests/unit/setup.js'
],
snapshotSerializers: [
'<rootDir>/tests/unit/serializer.js'
],
testEnvironment: 'jest-environment-jsdom-global',
testMatch: [
'**/tests/unit/**/*.test.js'
],
testPathIgnorePatterns: [
'<rootDir>/tests/e2e'
],
testURL: 'http://localhost/',
transform: { transform: {
'^.+\\.vue$': 'vue-jest', '^.+\\.vue$': 'vue-jest',
'.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', '.+\\.(css|styl|less|sass|scss|svg|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub',
@ -13,16 +39,6 @@ module.exports = {
transformIgnorePatterns: [ transformIgnorePatterns: [
'/node_modules/' '/node_modules/'
], ],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
},
snapshotSerializers: [
'jest-serializer-vue'
],
testMatch: [
'**/tests/unit/**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)'
],
testURL: 'http://localhost/',
watchPlugins: [ watchPlugins: [
'jest-watch-typeahead/filename', 'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname' 'jest-watch-typeahead/testname'

3975
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,8 +22,9 @@
"run:win": "dist\\nw-vue-1.0.0-win-x86\\nw-vue.exe", "run:win": "dist\\nw-vue-1.0.0-win-x86\\nw-vue.exe",
"lint": "vue-cli-service lint --no-fix", "lint": "vue-cli-service lint --no-fix",
"fix": "vue-cli-service lint --fix", "fix": "vue-cli-service lint --fix",
"test": "npm run test:unit",
"test:e2e": "vue-cli-service test:e2e", "test:e2e": "vue-cli-service test:e2e",
"test:unit": "vue-cli-service test:unit" "test:unit": "jest --config jest.config.js --coverage --runInBand"
}, },
"dependencies": { "dependencies": {
"express": "^4.17.1" "express": "^4.17.1"
@ -38,17 +39,26 @@
"babel-core": "7.0.0-bridge.0", "babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.0.3", "babel-eslint": "^10.0.3",
"babel-jest": "^24.9.0", "babel-jest": "^24.9.0",
"babel-plugin-dynamic-import-node": "^2.3.0",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1",
"babel-preset-vue-app": "^2.0.0",
"concurrently": "^4.1.2", "concurrently": "^4.1.2",
"core-js": "^2.6.5", "core-js": "^2.6.5",
"eslint": "^6.3.0", "eslint": "^6.4.0",
"eslint-plugin-jest": "^22.16.0", "eslint-plugin-jest": "^22.17.0",
"eslint-plugin-vue": "^5.2.3", "eslint-plugin-vue": "^5.2.3",
"jest": "^24.9.0",
"jest-environment-jsdom-global": "^1.2.0",
"jest-transform-stub": "^2.0.0",
"nw": "0.40.2-sdk", "nw": "0.40.2-sdk",
"nw-vue-devtools": "^1.3.0", "nw-vue-devtools": "^1.3.0",
"nwjs-builder-phoenix": "^1.15.0", "nwjs-builder-phoenix": "^1.15.0",
"nwjs-types": "^1.0.0", "nwjs-types": "^1.0.0",
"rimraf": "^3.0.0", "rimraf": "^3.0.0",
"vue": "^2.6.10", "vue": "^2.6.10",
"vue-jest": "^3.0.5",
"vue-template-compiler": "^2.6.10", "vue-template-compiler": "^2.6.10",
"wait-on": "^3.3.0" "wait-on": "^3.3.0"
}, },

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="hello"> <div class="hello">
<h1>{{ msg }}</h1> <h1 data-test="message">{{ msg }}</h1>
<h3 data-test="versions"> <h3 data-test="versions">
You are using You are using
Vue.js (v{{ vueVersion }}), Vue.js (v{{ vueVersion }}),
@ -10,7 +10,11 @@
Chromium (v{{ versions.chromium }}). Chromium (v{{ versions.chromium }}).
</h3> </h3>
<button v-if="devMode" @click="toggleDevTools"> <button
v-if="devMode"
data-test="toggleDevTools"
@click="toggleDevTools"
>
<template v-if="showDevTools"> <template v-if="showDevTools">
Show Show
</template> </template>
@ -142,16 +146,16 @@ export default {
methods: { methods: {
toggleDevTools: function () { toggleDevTools: function () {
if (this.showDevTools) { if (this.showDevTools) {
nw.Window.get().showDevTools(); window.nw.Window.get().showDevTools();
} else { } else {
nw.Window.get().closeDevTools(); window.nw.Window.get().closeDevTools();
} }
this.showDevTools = !this.showDevTools; this.showDevTools = !this.showDevTools;
} }
}, },
computed: { computed: {
devMode: function () { devMode: function () {
return process.env.NODE_ENV === 'development'; return window.process.env.NODE_ENV === 'development';
}, },
versions: function () { versions: function () {
return window.process.versions; return window.process.versions;

View File

@ -7,6 +7,7 @@
> >
<a <a
href="#" href="#"
data-test="link"
@click.prevent="open(link.url)" @click.prevent="open(link.url)"
> >
{{ link.name }} {{ link.name }}
@ -43,7 +44,7 @@ export default {
}, },
methods: { methods: {
open: function (url) { open: function (url) {
nw.Shell.openExternal(url); window.nw.Shell.openExternal(url);
} }
} }
}; };

View File

@ -1,5 +1,8 @@
module.exports = { module.exports = {
env: { env: {
jest: true jest: true
},
rules: {
'import/no-extraneous-dependencies': 'off'
} }
}; };

11
tests/unit/App.test.js Normal file
View File

@ -0,0 +1,11 @@
import { shallowMount } from '@vue/test-utils';
import App from '@/App.vue';
describe('App.vue', () => {
test('Render default contents', () => {
const wrapper = shallowMount(App);
expect(wrapper.html())
.toMatchSnapshot();
});
});

View File

@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`App.vue Render default contents 1`] = `
<div id="app">
<img src="@/assets/vue.png" alt="Vue.js logo" title="Vue.js" class="logo">
<img src="@/assets/nw.png" alt="NW.js logo" title="NW.js" class="logo">
<helloworld-stub msg="Welcome to your Vue.js Desktop App in NW.js!"></helloworld-stub>
</div>
`;

View File

@ -0,0 +1,39 @@
import { shallowMount, mount } from '@vue/test-utils';
import HelloWorld from '@/components/HelloWorld.vue';
describe('HelloWorld.vue', () => {
test('Render props.msg', () => {
const msg = 'new message';
const wrapper = shallowMount(HelloWorld, {
propsData: { msg }
});
expect(wrapper.find('[data-test="message"]').text())
.toEqual(msg);
});
test('Render default contents', () => {
const wrapper = mount(HelloWorld);
expect(wrapper.html())
.toMatchSnapshot();
});
test('Activate dev tools', () => {
const wrapper = shallowMount(HelloWorld);
const button = wrapper.find('[data-test="toggleDevTools"]');
button.trigger('click');
wrapper.vm.$nextTick();
expect(wrapper.find('[data-test="toggleDevTools').html())
.toMatchSnapshot('hide');
button.trigger('click');
wrapper.vm.$nextTick();
expect(wrapper.find('[data-test="toggleDevTools').html())
.toMatchSnapshot('show');
});
});

View File

@ -0,0 +1,50 @@
import { shallowMount } from '@vue/test-utils';
import LinkList from '@/components/LinkList.vue';
describe('LinkList.vue', () => {
const link = {
name: 'Site',
url: 'https://nwjs.io'
};
test('Validate props', () => {
const wrapper = shallowMount(LinkList);
const links = wrapper.vm.$options.props.links;
expect(links.required)
.toBeFalsy();
expect(links.type)
.toBe(Array);
expect(links.default)
.toBeNull();
expect(links.validator && links.validator([{ name: '', url: '' }]))
.toBeFalsy();
expect(links.validator && links.validator([link]))
.toBeTruthy();
});
test('Render default contents', () => {
const wrapper = shallowMount(LinkList, {
propsData: { links: [link] }
});
expect(wrapper.html())
.toMatchSnapshot();
});
test('Click link', () => {
const wrapper = shallowMount(LinkList, {
propsData: { links: [link] }
});
let domLink = wrapper.findAll('[data-test="link"]').at(0);
domLink.trigger('click');
expect(window.nw.Shell.openExternal)
.toHaveBeenCalledWith('https://nwjs.io');
});
});

View File

@ -0,0 +1,143 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`HelloWorld.vue Activate dev tools: hide 1`] = `
<button>
Hide
Developer Tools
</button>
`;
exports[`HelloWorld.vue Activate dev tools: show 1`] = `
<button>
Show
Developer Tools
</button>
`;
exports[`HelloWorld.vue Render default contents 1`] = `
<div class="hello">
<h1>Welcome to your Vue Desktop App in NW.js!</h1>
<h3>
You are using
Vue.js (v2.6.10),
NW.js (v0.40.2-sdk),
Node.js (v12.9.1),
and
Chromium (v76.0.3809.132).
</h3>
<button>
Show
Developer Tools
</button>
<p>You can use the resources below to find more information around building your Vue App.</p>
<ul>
<li title="github.com/nwutils/nw-vue-cli-example">
<a href="#">
nw-vue-cli-example
</a>
</li>
<li title="nwjs.io">
<a href="#">
NW.js
</a>
</li>
<li title="github.com/nwutils/nw-vue-devtools">
<a href="#">
nw-vue-devtools
</a>
</li>
<li title="github.com/evshiron/nwjs-builder-phoenix">
<a href="#">
nwjs-builder-phoenix
</a>
</li>
<li title="cli.vuejs.org">
<a href="#">
vue-cli
</a>
</li>
</ul>
<h3>Installed CLI Plugins</h3>
<ul>
<li title="github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel">
<a href="#">
babel
</a>
</li>
<li title="github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint">
<a href="#">
eslint
</a>
</li>
<li title="github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-unit-jest">
<a href="#">
unit-jest
</a>
</li>
<li title="github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-e2e-cypress">
<a href="#">
e2e-cypress
</a>
</li>
</ul>
<h3>Essential Links</h3>
<ul>
<li title="vuejs.org">
<a href="#">
Core Docs
</a>
</li>
<li title="forum.vuejs.org">
<a href="#">
Forum
</a>
</li>
<li title="chat.vuejs.org">
<a href="#">
Community Chat
</a>
</li>
<li title="twitter.com/vuejs">
<a href="#">
Twitter
</a>
</li>
<li title="news.vuejs.org">
<a href="#">
News
</a>
</li>
</ul>
<h3>Ecosystem</h3>
<ul>
<li title="router.vuejs.org">
<a href="#">
vue-router
</a>
</li>
<li title="vuex.vuejs.org">
<a href="#">
vuex
</a>
</li>
<li title="github.com/vuejs/vue-devtools#vue-devtools">
<a href="#">
vue-devtools
</a>
</li>
<li title="vue-loader.vuejs.org">
<a href="#">
vue-loader
</a>
</li>
<li title="github.com/vuejs/awesome-vue">
<a href="#">
awesome-vue
</a>
</li>
</ul>
</div>
`;

View File

@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`LinkList.vue Render default contents 1`] = `
<ul>
<li title="nwjs.io">
<a href="#">
Site
</a>
</li>
</ul>
`;

View File

@ -1,12 +0,0 @@
import { shallowMount } from '@vue/test-utils';
import HelloWorld from '@/components/HelloWorld.vue';
describe('HelloWorld.vue', () => {
it('renders props.msg when passed', () => {
const msg = 'new message';
const wrapper = shallowMount(HelloWorld, {
propsData: { msg }
});
expect(wrapper.text()).toMatch(msg);
});
});

42
tests/unit/serializer.js Normal file
View File

@ -0,0 +1,42 @@
// Based on jest-serializer-vue
const beautify = require('pretty');
function isHtmlString (received) {
return received && typeof(received) === 'string' && received.startsWith('<');
}
function isVueWrapper (received) {
return received && typeof(received) === 'object' && typeof(received.isVueInstance) === 'function';
}
// This removes data-test="whatever" from your snapshots
// If you also want to remove them from your production builds, see:
// https://forum.vuejs.org/t/how-to-remove-attributes-from-tags-inside-vue-components/24138
function removeDataTestAttributes (html) {
// [-\w]+ will catch 1 or more instaces of a-z, A-Z, 0-9, hyphen (-), or underscore (_)
return html.replace(/ data-test="[-\w]+"/g, '');
}
module.exports = {
test: function (received) {
return isHtmlString(received) || isVueWrapper(received);
},
print: function (received) {
let html = received || '';
if (isVueWrapper(received)) {
html = received.html();
}
html = removeDataTestAttributes(html);
// To see available options: https://github.com/beautify-web/js-beautify/blob/master/js/src/html/options.js
const options = {
indent_size: 2,
unformatted: ['code', 'pre'],
inline: [],
indent_inner_html: true,
indent_char: ' ',
sep: '\n'
};
return beautify(html, options);
}
};

57
tests/unit/setup.js Normal file
View File

@ -0,0 +1,57 @@
import Vue from 'vue';
const { getComputedStyle } = window;
Vue.config.productionTip = false;
// Prevents console log message to install Vue DevTools
Vue.config.devtools = false;
// Monkeypatch JSDOM missing transition styles + vue-test-utils not properly stubbing transitions
// in globally included libs (VeeValidate in our case)
// https://github.com/vuejs/vue-test-utils/issues/839#issuecomment-410474714
window.getComputedStyle = function getComputedStyleStub (el) {
return {
...getComputedStyle(el),
transitionDelay: '',
transitionDuration: '',
animationDelay: '',
animationDuration: ''
};
};
global.beforeEach(() => {
window.process = {
env: {
NODE_ENV: 'development'
},
versions: {
chromium: '76.0.3809.132',
nw: '0.40.2',
'nw-flavor': 'sdk',
node: '12.9.1'
}
};
window.nw = {
Shell: {
openExternal: jest.fn()
},
Window: {
get: function () {
return {
showDevTools: jest.fn(),
closeDevTools: jest.fn()
};
}
}
};
});
global.afterEach(() => {
window.nw.Window.get().showDevTools.mockClear();
});
// Jest's setTimeout defaults to 5 seconds.
// Bump the timeout to 60 seconds.
jest.setTimeout(60 * 1000);