├── .babelrc ├── .editorconfig ├── .gitignore ├── .postcssrc ├── README.md ├── assets └── styles │ └── app.styl ├── config ├── dev.env.ts ├── hom.env.ts ├── index.ts ├── prod.env.ts └── test.env.ts ├── index.d.ts ├── jest.config.js ├── layouts ├── auth.vue └── default.vue ├── middleware └── README.md ├── modules └── typescript.js ├── nuxt.config.js ├── package.json ├── pages ├── index.vue ├── inspire.vue └── sign-in.vue ├── plugins ├── axios.ts ├── vue-rxjs.ts └── vuetify.ts ├── static └── favicon.ico ├── store ├── index.ts ├── modules │ └── auth │ │ ├── actions.spec.ts │ │ ├── actions.ts │ │ ├── getters.ts │ │ ├── index.ts │ │ ├── mutations.ts │ │ ├── state.ts │ │ └── types.ts └── root.ts ├── tests ├── e2e │ ├── custom-assertions │ │ └── elementCount.js │ ├── nightwatch.conf.js │ ├── runner.js │ └── specs │ │ └── test.js └── unit │ ├── .eslintrc │ ├── setup.ts │ └── specs │ ├── index.spec.ts │ └── sign-in.spec.ts ├── tsconfig.json ├── tslint.json ├── wallaby.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["vue-app", { "useBuiltIns": true } ], 4 | "stage-2" 5 | ], 6 | "plugins": [ 7 | "transform-class-properties" 8 | ], 9 | "comments": false, 10 | "env": { 11 | "test": { 12 | "presets": [ 13 | ["vue-app", { "useBuiltIns": true } ], 14 | "stage-2" 15 | ] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{js, ts}] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.vue] 12 | charset = utf-8 13 | indent_style = space 14 | indent_size = 4 15 | end_of_line = lf 16 | insert_final_newline = true 17 | trim_trailing_whitespace = true 18 | 19 | [*.{css, scss}] 20 | charset = utf-8 21 | indent_style = space 22 | indent_size = 2 23 | end_of_line = lf 24 | insert_final_newline = true 25 | trim_trailing_whitespace = true 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules 3 | 4 | # IDE/Editor 5 | .vs 6 | .idea 7 | 8 | # logs 9 | npm-debug.log 10 | 11 | # Nuxt build 12 | .nuxt 13 | 14 | # Nuxt generate 15 | dist 16 | 17 | # Web workers 18 | sw.* 19 | -------------------------------------------------------------------------------- /.postcssrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "autoprefixer": {} 4 | } 5 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nuxt-typescript 2 | 3 | > Nuxt + PWA + Typescript + Jest + Vuetify 4 | 5 | ## Build Setup 6 | 7 | ``` bash 8 | # install dependencies 9 | $ npm install # Or yarn 10 | 11 | # serve with hot reload at localhost:3000 12 | $ npm run dev 13 | 14 | # build for production and launch server 15 | $ npm run build 16 | $ npm start 17 | 18 | # generate static project 19 | $ npm run generate 20 | ``` 21 | 22 | For detailed explanation on how things work, checkout the [Nuxt.js docs](https://github.com/nuxt/nuxt.js). 23 | -------------------------------------------------------------------------------- /assets/styles/app.styl: -------------------------------------------------------------------------------- 1 | @require '~vuetify/src/stylus/main.styl' 2 | 3 | .page 4 | @extend .fade-transition -------------------------------------------------------------------------------- /config/dev.env.ts: -------------------------------------------------------------------------------- 1 | export const env = { 2 | NODE_ENV: 'development', 3 | url: { 4 | host: 'http://localhost:5000' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /config/hom.env.ts: -------------------------------------------------------------------------------- 1 | export const env = { 2 | NODE_ENV: 'development', 3 | url: { 4 | host: 'http://localhost:5000' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /config/index.ts: -------------------------------------------------------------------------------- 1 | import { env } from './dev.env'; 2 | 3 | export const config = env; 4 | -------------------------------------------------------------------------------- /config/prod.env.ts: -------------------------------------------------------------------------------- 1 | export const env = { 2 | NODE_ENV: 'production', 3 | url: { 4 | host: 'http://localhost:5000' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /config/test.env.ts: -------------------------------------------------------------------------------- 1 | export const env = { 2 | NODE_ENV: 'testing', 3 | url: { 4 | host: 'http://localhost:5000' 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import Vue from 'vue' 3 | const _default: Vue; 4 | export default _default; 5 | } 6 | 7 | declare module 'nuxt-class-component'; -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | moduleFileExtensions: [ 3 | 'ts', 4 | 'tsx', 5 | 'js', 6 | 'jsx', 7 | 'json', 8 | 'vue' 9 | ], 10 | 11 | transform: { 12 | '^.+\\.vue$': 'vue-jest', 13 | '.+\\.(css|styl|less|sass|scss|png|jpg|ttf|woff|woff2)$': 'jest-transform-stub', 14 | '^.+\\.(ts|js)x?$': 'ts-jest', 15 | }, 16 | 17 | moduleNameMapper: { 18 | '^~/(.*)$': '/$1', 19 | '^vue$': 'vue/dist/vue.common.js' 20 | }, 21 | 22 | snapshotSerializers: [ 23 | 'jest-serializer-vue' 24 | ], 25 | 26 | setupFiles: [ 27 | '/tests/unit/setup.ts' 28 | ], 29 | 30 | coverageDirectory: '/tests/unit/coverage', 31 | 32 | collectCoverageFrom: [ 33 | 'components/**/*.{js,ts,vue}', 34 | 'layouts/**/*.{js,ts,vue}', 35 | 'pages/**/*.{js,ts,vue}', 36 | 'store/**/*.{js,ts}', 37 | '!**/node_modules/**' 38 | ], 39 | 40 | testPathIgnorePatterns: [ 41 | '/tests/e2e' 42 | ], 43 | 44 | testMatch: [ 45 | '**/*.spec.(js|ts)' 46 | ] 47 | }; 48 | -------------------------------------------------------------------------------- /layouts/auth.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 23 | 24 | 26 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 74 | 75 | 101 | -------------------------------------------------------------------------------- /middleware/README.md: -------------------------------------------------------------------------------- 1 | # MIDDLEWARE 2 | 3 | This directory contains your Application Middleware. 4 | The middleware lets you define custom function to be ran before rendering a page or a group of pages (layouts). 5 | 6 | More informations about the usage of this directory in the documentation: 7 | https://nuxtjs.org/guide/routing#middleware 8 | 9 | **This directory is not required, to can delete it if you don't want to use it.** 10 | -------------------------------------------------------------------------------- /modules/typescript.js: -------------------------------------------------------------------------------- 1 | module.exports = function () { 2 | // Add .ts extension for store, middleware and more 3 | this.nuxt.options.extensions.push('ts'); 4 | // Extend build 5 | this.extendBuild(config => { 6 | const tsLoader = { 7 | loader: 'ts-loader', 8 | options: { 9 | appendTsSuffixTo: [/\.vue$/] 10 | } 11 | }; 12 | // Add TypeScript loader 13 | config.module.rules.push( 14 | Object.assign( 15 | { 16 | test: /((client|server)\.js)|(\.tsx?)$/ 17 | }, 18 | tsLoader 19 | ) 20 | ); 21 | // Add TypeScript loader for vue files 22 | for (let rule of config.module.rules) { 23 | if (rule.loader === 'vue-loader') { 24 | rule.options.loaders.ts = tsLoader; 25 | } 26 | } 27 | // Add .ts extension in webpack resolve 28 | if (config.resolve.extensions.indexOf('.ts') === -1) { 29 | config.resolve.extensions.push('.ts'); 30 | } 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /nuxt.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | baseUrl: process.env.BASE_URL || 'http://localhost:3000' 4 | }, 5 | head: { 6 | title: 'nuxt-typescript-starter', 7 | meta: [ 8 | { charset: 'utf-8' }, 9 | { name: 'viewport', content: 'width=device-width, initial-scale=1' }, 10 | { hid: 'description', name: 'description', content: 'Nuxt.js project' } 11 | ], 12 | link: [ 13 | { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, 14 | { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700|Material+Icons' } 15 | ] 16 | }, 17 | /* 18 | ** Customize the progress-bar color 19 | */ 20 | loading: false, 21 | /* 22 | ** Build configuration 23 | */ 24 | plugins: [ 25 | '~/plugins/vuetify.ts', 26 | // '~/plugins/axios', 27 | '~/plugins/vue-rxjs', 28 | ], 29 | css: ['~/assets/styles/app.styl'], 30 | build: { 31 | vendor: ['axios', 'vuex-class', 'nuxt-class-component'] 32 | }, 33 | modules: ['~/modules/typescript', '@nuxtjs/pwa'], 34 | vendor: [ 35 | '~/plugins/vuetify.ts' 36 | ], 37 | extractCSS: true 38 | }; 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nuxt-typescript-starter", 3 | "version": "1.0.4", 4 | "private": true, 5 | "dependencies": { 6 | "@nuxtjs/pwa": "^2.0.8", 7 | "axios": "^0.18.0", 8 | "nuxt": "^1.4.0", 9 | "nuxt-class-component": "^1.2.1", 10 | "qs": "^6.5.1", 11 | "rxjs": "^5.5.6", 12 | "vue-class-component": "^6.2.0", 13 | "vue-property-decorator": "^6.0.0", 14 | "vue-rx": "^5.0.0", 15 | "vuetify": "^1.0.17", 16 | "vuex": "^3.0.1", 17 | "vuex-class": "^0.3.0" 18 | }, 19 | "scripts": { 20 | "dev": "nuxt", 21 | "build": "nuxt build", 22 | "start": "nuxt start", 23 | "test": "jest --no-cache", 24 | "generate": "nuxt generate" 25 | }, 26 | "devDependencies": { 27 | "@reactivex/rxjs": "^5.5.6", 28 | "@types/axios": "^0.14.0", 29 | "@types/jest": "^22.2.3", 30 | "@types/node": "^8.10.10", 31 | "@types/qs": "^6.5.1", 32 | "@vue/test-utils": "^1.0.0-beta.15", 33 | "babel-jest": "^22.4.3", 34 | "babel-plugin-transform-class-properties": "^6.24.1", 35 | "babel-preset-stage-2": "^6.24.1", 36 | "babel-preset-vue-app": "^2.0.0", 37 | "jest": "^22.4.3", 38 | "jest-serializer-vue": "^1.0.0", 39 | "jest-transform-stub": "^1.0.0", 40 | "jest-vue-preprocessor": "^1.4.0", 41 | "stylus": "^0.54.5", 42 | "stylus-loader": "^3.0.2", 43 | "ts-jest": "^22.4.4", 44 | "ts-loader": "^3.5.0", 45 | "tslint": "^5.10.0", 46 | "tslint-config-standard": "^7.0.0", 47 | "typescript": "~2.5.3", 48 | "vue-jest": "^2.5.0", 49 | "wallaby-vue-compiler": "^1.0.2" 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /pages/index.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 47 | -------------------------------------------------------------------------------- /pages/inspire.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pages/sign-in.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 62 | 63 | 71 | -------------------------------------------------------------------------------- /plugins/axios.ts: -------------------------------------------------------------------------------- 1 | import axios, { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'; 2 | 3 | export const fetch = axios.create({ 4 | // baseURL: process.env.baseUrl, 5 | headers: { 6 | 'Access-Control-Allow-Origin': '*' 7 | } 8 | }); 9 | 10 | const requestSuccess = (req: AxiosRequestConfig) => { 11 | return req; 12 | }; 13 | 14 | const requestError = (err: AxiosError) => { 15 | return Promise.reject(err); 16 | }; 17 | 18 | const responseSuccess = (res: AxiosResponse) => { 19 | return Promise.resolve(res); 20 | }; 21 | 22 | const responseError = (err: AxiosError) => { 23 | if (err.request.status === 401 || err.request.status === 403) { 24 | console.log('UNAUTHORIZED'); 25 | } 26 | 27 | return Promise.reject(err); 28 | }; 29 | 30 | fetch.interceptors.request.use(requestSuccess, requestError); 31 | fetch.interceptors.response.use(responseSuccess, responseError); 32 | -------------------------------------------------------------------------------- /plugins/vue-rxjs.ts: -------------------------------------------------------------------------------- 1 | import Rx from 'rxjs/Rx'; 2 | import Vue from 'vue'; 3 | import VueRx from 'vue-rx'; 4 | 5 | Vue.use(VueRx, Rx); 6 | -------------------------------------------------------------------------------- /plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuetify from 'vuetify'; 3 | 4 | Vue.use(Vuetify); 5 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helmuthdu/typescript-nuxt-starter/7317847cb000c42cdf8a2be0e8a2ba9ab29d3b13/static/favicon.ico -------------------------------------------------------------------------------- /store/index.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import * as auth from './modules/auth'; 4 | import * as root from './root'; 5 | 6 | Vue.use(Vuex); 7 | 8 | // More info about store: https://vuex.vuejs.org/en/core-concepts.html 9 | // See https://nuxtjs.org/guide/vuex-store#classic-mode 10 | // structure of the store: 11 | // types: Types that represent the keys of the mutations to commit 12 | // state: The information of our app, we can get or update it. 13 | // getters: Get complex information from state 14 | // action: Sync or async operations that commit mutations 15 | // mutations: Modify the state 16 | interface ModulesStates { 17 | auth: auth.State; 18 | } 19 | 20 | export type RootState = root.State & ModulesStates; 21 | 22 | export default () => new Vuex.Store({ 23 | state: root.state() as any, 24 | getters: root.getters, 25 | mutations: root.mutations, 26 | actions: root.actions as any, 27 | modules: { 28 | [auth.name]: auth 29 | } 30 | }); 31 | -------------------------------------------------------------------------------- /store/modules/auth/actions.spec.ts: -------------------------------------------------------------------------------- 1 | import { State } from '~/store/modules/auth/state'; 2 | import { actions } from '~/store/modules/auth/actions'; 3 | import { mutations } from '~/store/modules/auth/mutations'; 4 | 5 | describe('store: authentication -> actions', () => { 6 | let state: State; 7 | let commit: any; 8 | const dispatch: any = null; 9 | const rootState: any = null; 10 | const getters: any = null; 11 | const rootGetters: any = null; 12 | 13 | beforeEach(() => { 14 | // mock state 15 | state = { client: '', username: '', email: '', isLogged: false, password: '' }; 16 | 17 | // mock commit 18 | commit = (type: string, payload: State) => { 19 | const mutation = mutations[type]; 20 | expect(mutation).toBeDefined(); 21 | mutation(state, { ...payload }); 22 | }; 23 | }); 24 | 25 | test('AUTH_LOGIN', async () => { 26 | // apply mutation 27 | await actions.doLogin({ state, commit, dispatch, getters, rootState, rootGetters }, { 28 | username: 'helmuthdu', 29 | email: 'helmuthdu@gmail.com' 30 | }); 31 | // assert result 32 | expect(state.isLogged).toBe(true); 33 | }); 34 | 35 | test('AUTH_LOGOUT', () => { 36 | // apply mutation 37 | actions.doLogout({ state, commit, dispatch, getters, rootState, rootGetters }); 38 | // assert result 39 | expect(state.isLogged).toBe(false); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /store/modules/auth/actions.ts: -------------------------------------------------------------------------------- 1 | import { config } from '~/config'; 2 | import { RootState } from '~/store'; 3 | import { State } from '~/store/modules/auth/state'; 4 | import { Types } from '~/store/modules/auth/types'; 5 | import axios from 'axios'; 6 | import qs from 'qs'; 7 | import { ActionContext, ActionTree } from 'vuex'; 8 | 9 | export interface Actions extends ActionTree { 10 | doLogin: (context: ActionContext, payload: any) => void; 11 | doLogout: (context: ActionContext) => void; 12 | } 13 | 14 | export const actions: Actions = { 15 | doLogin: async ({ commit }, payload: State) => { 16 | commit(Types.AUTH_SET_USER, { 17 | ...(await axios.post(`https://httpstat.us/200`, qs.stringify({ 18 | username: payload.email, 19 | password: payload.password 20 | }))).data, 21 | isLogged: true 22 | }); 23 | }, 24 | doLogout: ({ commit }) => { 25 | commit(Types.AUTH_SET_USER, { username: '', email: '', isLogged: false }); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /store/modules/auth/getters.ts: -------------------------------------------------------------------------------- 1 | import { RootState } from '~/store'; 2 | import { State } from '~/store/modules/auth/state'; 3 | import { GetterTree } from 'vuex'; 4 | 5 | export const getters: GetterTree = { 6 | isLogged: (state: State) => { 7 | return state.isLogged; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /store/modules/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './actions'; 2 | export * from './getters'; 3 | export * from './mutations'; 4 | export * from './state'; 5 | export * from './types'; 6 | 7 | export const name = 'auth'; 8 | 9 | export const namespaced = true; 10 | -------------------------------------------------------------------------------- /store/modules/auth/mutations.ts: -------------------------------------------------------------------------------- 1 | import { State } from '~/store/modules/auth/state'; 2 | import { Types } from '~/store/modules/auth/types'; 3 | import { MutationTree } from 'vuex'; 4 | 5 | export const mutations: MutationTree = { 6 | [Types.AUTH_SET_USER]: (state: State, payload: State) => { 7 | state.username = payload.username; 8 | state.email = payload.email; 9 | state.isLogged = payload.isLogged; 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /store/modules/auth/state.ts: -------------------------------------------------------------------------------- 1 | export interface State { 2 | username: string; 3 | email: string; 4 | client: string; 5 | isLogged?: boolean; 6 | password: string; 7 | } 8 | 9 | export const state = (): State => ({ 10 | username: '', 11 | email: '', 12 | client: '', 13 | isLogged: false, 14 | password: '' 15 | }); 16 | -------------------------------------------------------------------------------- /store/modules/auth/types.ts: -------------------------------------------------------------------------------- 1 | export enum Types { 2 | // Getters (GET*) 3 | // [CONTEXT]_[ACTION]_[PROPERTY] 4 | // Mutations (SET*, ADD*, REMOVE*) 5 | // [CONTEXT]_[ACTION]_[PROPERTY] 6 | AUTH_SET_USER = 'AUTH_SET_USER', 7 | // Actions (FETCH*, CREATE*, UPDATE*, DELETE*) 8 | // [CONTEXT]_[ACTION]_[PROPERTY]_[STATUS] 9 | AUTH_LOGIN = 'AUTH_LOGIN', 10 | AUTH_LOGOUT = 'AUTH_LOGOUT' 11 | } 12 | -------------------------------------------------------------------------------- /store/root.ts: -------------------------------------------------------------------------------- 1 | import { ActionContext, ActionTree, GetterTree, MutationTree } from 'vuex'; 2 | import { RootState } from './index'; 3 | 4 | export const types = {}; 5 | 6 | export interface State { 7 | version: string; 8 | } 9 | 10 | export const state = (): State => ({ 11 | version: '1.0.0' 12 | }); 13 | 14 | export const getters: GetterTree = {}; 15 | 16 | export interface Actions extends ActionTree { 17 | getVersion (context: ActionContext): void; 18 | } 19 | 20 | export const actions: Actions = { 21 | async getVersion (context: ActionContext) { 22 | return context.state.version; 23 | } 24 | }; 25 | 26 | export const mutations: MutationTree = {}; 27 | -------------------------------------------------------------------------------- /tests/e2e/custom-assertions/elementCount.js: -------------------------------------------------------------------------------- 1 | // A custom Nightwatch assertion. 2 | // the name of the method is the filename. 3 | // can be used in tests like this: 4 | // 5 | // browser.assert.elementCount(selector, count) 6 | // 7 | // for how to write custom assertions see 8 | // http://nightwatchjs.org/guide#writing-custom-assertions 9 | exports.assertion = function (selector, count) { 10 | this.message = 'Testing if element <' + selector + '> has count: ' + count; 11 | this.expected = count; 12 | this.pass = function (val) { 13 | return val === this.expected; 14 | }; 15 | this.value = function (res) { 16 | return res.value; 17 | }; 18 | this.command = function (cb) { 19 | var self = this; 20 | return this.api.execute(function (selector) { 21 | return document.querySelectorAll(selector).length; 22 | }, [selector], function (res) { 23 | cb.call(self, res); 24 | }); 25 | }; 26 | }; 27 | -------------------------------------------------------------------------------- /tests/e2e/nightwatch.conf.js: -------------------------------------------------------------------------------- 1 | require('babel-register'); 2 | var config = require('../../config/index'); 3 | 4 | // http://nightwatchjs.org/gettingstarted#settings-file 5 | module.exports = { 6 | src_folders: ['test/e2e/specs'], 7 | output_folder: 'test/e2e/reports', 8 | custom_assertions_path: ['test/e2e/custom-assertions'], 9 | 10 | selenium: { 11 | start_process: true, 12 | server_path: require('selenium-server').path, 13 | host: '127.0.0.1', 14 | port: 4444, 15 | cli_args: { 16 | 'webdriver.chrome.driver': require('chromedriver').path 17 | } 18 | }, 19 | 20 | test_settings: { 21 | default: { 22 | selenium_port: 4444, 23 | selenium_host: 'localhost', 24 | silent: true, 25 | globals: { 26 | devServerURL: 'http://localhost:' + (process.env.PORT || config.dev.port) 27 | } 28 | }, 29 | 30 | chrome: { 31 | desiredCapabilities: { 32 | browserName: 'chrome', 33 | javascriptEnabled: true, 34 | acceptSslCerts: true 35 | } 36 | }, 37 | 38 | firefox: { 39 | desiredCapabilities: { 40 | browserName: 'firefox', 41 | javascriptEnabled: true, 42 | acceptSslCerts: true 43 | } 44 | } 45 | } 46 | }; 47 | -------------------------------------------------------------------------------- /tests/e2e/runner.js: -------------------------------------------------------------------------------- 1 | // 1. start the dev server using production config 2 | process.env.NODE_ENV = 'testing'; 3 | 4 | const webpack = require('webpack'); 5 | const DevServer = require('webpack-dev-server'); 6 | 7 | const webpackConfig = require('../../build/webpack.prod.conf'); 8 | const devConfigPromise = require('../../build/webpack.dev.conf'); 9 | 10 | let server; 11 | 12 | devConfigPromise.then(devConfig => { 13 | const devServerOptions = devConfig.devServer; 14 | const compiler = webpack(webpackConfig); 15 | server = new DevServer(compiler, devServerOptions); 16 | const port = devServerOptions.port; 17 | const host = devServerOptions.host; 18 | return server.listen(port, host); 19 | }) 20 | .then(() => { 21 | // 2. run the nightwatch test suite against it 22 | // to run in additional browsers: 23 | // 1. add an entry in test/e2e/nightwatch.conf.json under "test_settings" 24 | // 2. add it to the --env flag below 25 | // or override the environment flag, for example: `npm run e2e -- --env chrome,firefox` 26 | // For more information on Nightwatch's config file, see 27 | // http://nightwatchjs.org/guide#settings-file 28 | let opts = process.argv.slice(2); 29 | if (opts.indexOf('--config') === -1) { 30 | opts = opts.concat(['--config', 'test/e2e/nightwatch.conf.js']); 31 | } 32 | if (opts.indexOf('--env') === -1) { 33 | opts = opts.concat(['--env', 'chrome']); 34 | } 35 | 36 | const spawn = require('cross-spawn'); 37 | const runner = spawn('./node_modules/.bin/nightwatch', opts, { stdio: 'inherit' }); 38 | 39 | runner.on('exit', function (code) { 40 | server.close(); 41 | process.exit(code); 42 | }); 43 | 44 | runner.on('error', function (err) { 45 | server.close(); 46 | throw err; 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /tests/e2e/specs/test.js: -------------------------------------------------------------------------------- 1 | // For authoring Nightwatch tests, see 2 | // http://nightwatchjs.org/guide#usage 3 | 4 | module.exports = { 5 | 'default e2e tests': function (browser) { 6 | // automatically uses dev Server port from /config.index.js 7 | // default: http://localhost:8080 8 | // see nightwatch.conf.js 9 | const devServer = browser.globals.devServerURL; 10 | 11 | browser 12 | .url(devServer) 13 | .waitForElementVisible('#app', 5000) 14 | .assert.elementPresent('.hello') 15 | .assert.containsText('h1', 'Welcome to Your Vue.js App') 16 | .assert.elementCount('img', 1) 17 | .end(); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /tests/unit/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "jest": true 4 | }, 5 | "globals": { 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/unit/setup.ts: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuetify from 'vuetify'; 3 | 4 | Vue.config.productionTip = false; 5 | Vue.use(Vuetify); 6 | 7 | jest.disableAutomock(); 8 | -------------------------------------------------------------------------------- /tests/unit/specs/index.spec.ts: -------------------------------------------------------------------------------- 1 | import HomePage from '~/pages/index.vue'; 2 | import { shallow } from '@vue/test-utils'; 3 | 4 | const $route = { 5 | path: '/some/path', 6 | query: { 7 | apikey: '', 8 | user: '' 9 | } 10 | }; 11 | 12 | describe('Test suite for HomePage', () => { 13 | test('Test initial layout', () => { 14 | const wrapper = shallow(HomePage as any, { 15 | mocks: { 16 | $route 17 | } 18 | }); 19 | 20 | expect(wrapper.isVueInstance()).toBeTruthy(); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /tests/unit/specs/sign-in.spec.ts: -------------------------------------------------------------------------------- 1 | import SignInPage from '~/pages/sign-in.vue'; 2 | import { shallow } from '@vue/test-utils'; 3 | 4 | describe('Test suite for SignInPage', () => { 5 | let wrapper: any; 6 | 7 | beforeEach(() => { 8 | wrapper = shallow(SignInPage as any); 9 | }); 10 | 11 | test('Test initial layout', () => { 12 | expect(wrapper.isVueInstance()).toBeTruthy(); 13 | }); 14 | 15 | it('should do the perform sign-in action', () => { 16 | wrapper.vm.email = 'user@mail.com'; 17 | wrapper.vm.password = '12345'; 18 | wrapper.vm.doLogin = jest.fn(); 19 | 20 | wrapper.vm.submit(); 21 | 22 | expect(wrapper.vm.doLogin).toBeCalledWith({ email: 'user@mail.com', password: '12345' }); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "strict": true, 6 | "jsx": "preserve", 7 | "moduleResolution": "node", 8 | "experimentalDecorators": true, 9 | "emitDecoratorMetadata": true, 10 | "allowSyntheticDefaultImports": true, 11 | "skipLibCheck" : true, 12 | "sourceMap": true, 13 | "baseUrl": ".", 14 | "allowJs": true, 15 | "lib": ["dom", "es2015"], 16 | "paths": { 17 | "~/*": [ 18 | "./*" 19 | ] 20 | } 21 | }, 22 | "exclude": [ 23 | ".nuxt", 24 | "dist", 25 | "node_modules" 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-config-standard" 4 | ], 5 | "rules": { 6 | "import-spacing": true, 7 | "semicolon": [ 8 | true, 9 | "always" 10 | ], 11 | "no-bitwise": false, 12 | "import-blacklist": [ 13 | true, 14 | "rxjs" 15 | ], 16 | "no-inferrable-types": [ 17 | true, 18 | "ignore-params" 19 | ], 20 | "interface-name": [ 21 | false 22 | ], 23 | // Pending fix for shorthand property names. 24 | "object-literal-sort-keys": false 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wallaby.js: -------------------------------------------------------------------------------- 1 | module.exports = function (wallaby) { 2 | const compiler = wallaby.compilers.babel({ 3 | 'presets': [ 4 | ['vue-app', { 'useBuiltIns': true }], 5 | 'stage-2' 6 | ], 7 | 'plugins': [ 8 | 'transform-class-properties' 9 | ] 10 | }); 11 | 12 | return { 13 | files: [ 14 | // loading kendo globally 15 | // { pattern: 'node_modules/@progress/kendo-ui/js/kendo.all.js', instrument: false }, 16 | { pattern: 'node_modules/vue/dist/vue.js', instrument: false }, 17 | { pattern: 'node_modules/babel-polyfill/dist/polyfill.js', instrument: false }, 18 | { pattern: 'assets/**/*.png' }, 19 | { pattern: 'assets/**/*.ttf' }, 20 | { pattern: 'components/**/*.vue' }, 21 | { pattern: 'layout/**/*.vue' }, 22 | { pattern: 'pages/**/*.vue' }, 23 | { pattern: 'services/**/*.vue' }, 24 | { pattern: 'store/**/*.vue' }, 25 | { pattern: 'components/**/*.ts' }, 26 | { pattern: 'enuns/**/*.ts' }, 27 | { pattern: 'layout/**/*.ts' }, 28 | { pattern: 'pages/**/*.ts' }, 29 | { pattern: 'pages/**/*.json' }, 30 | { pattern: 'services/**/*.ts' }, 31 | { pattern: 'store/**/*.ts' } 32 | ], 33 | 34 | tests: [ 35 | './components/**/*.spec.js', 36 | './store/**/*.spec.js', 37 | './tests/**/*.spec.js' 38 | ], 39 | 40 | env: { 41 | type: 'node', 42 | runner: 'node', 43 | params: { env: 'wallaby=true' } 44 | }, 45 | 46 | hints: { 47 | ignoreCoverage: /ignore coverage/ 48 | }, 49 | 50 | compilers: { 51 | '**/*.js': compiler, 52 | '**/*.vue': require('wallaby-vue-compiler')(compiler) 53 | }, 54 | 55 | preprocessors: { 56 | '**/*.vue': file => require('jest-vue-preprocessor').process(file.content, file.path) 57 | }, 58 | 59 | testFramework: 'jest', 60 | 61 | setup: function () { 62 | const jestConfig = require('./package.json').jest || require('./jest.config'); 63 | jestConfig.moduleNameMapper = { 64 | '^~/(.*)$': wallaby.projectCacheDir + '$1' 65 | }; 66 | jestConfig.transform = {}; 67 | wallaby.testFramework.configure(jestConfig); 68 | }, 69 | 70 | debug: true 71 | }; 72 | }; 73 | --------------------------------------------------------------------------------