├── .browserslistrc ├── test └── unit │ ├── globals │ ├── index.js │ ├── di.js │ └── console.js │ ├── utils │ ├── tid.js │ ├── di.js │ ├── axios.js │ └── store.js │ ├── index.js │ └── base.po.js ├── .postcssrc.js ├── .lintstagedrc ├── jsconfig.json ├── public ├── favicon.ico └── index.html ├── src ├── assets │ └── logo.png ├── store │ ├── auth │ │ ├── getters.js │ │ ├── mutations.js │ │ ├── index.js │ │ ├── actions.js │ │ ├── getters.spec.js │ │ ├── mutations.spec.js │ │ └── actions.spec.js │ └── index.js ├── components │ ├── MyCheckboxListContext.js │ ├── MyCheckboxListStatus.vue.po.js │ ├── MyButton.vue.po.js │ ├── MyCheckboxList.vue.po.js │ ├── MyCheckbox.vue.po.js │ ├── MyButton.vue │ ├── MyCheckboxListStatus.vue │ ├── MyCheckboxList.vue │ ├── Emails.vue.po.js │ ├── MyButton.vue.spec.js │ ├── MyCheckbox.vue │ ├── MyCheckboxListStatus.vue.spec.js │ ├── Emails.vue │ ├── MyCheckbox.vue.spec.js │ └── MyCheckboxList.vue.spec.js ├── services │ ├── api.js │ ├── profile.js │ ├── auth-interceptor.js │ ├── credentials.js │ ├── email.js │ ├── profile.stub.js │ ├── auth.js │ ├── credentials.spec.js │ ├── profile.spec.js │ ├── auth.stub.js │ ├── auth-interceptor.spec.js │ ├── email.spec.js │ ├── auth.spec.js │ └── email.stub.js ├── filters │ ├── full-name.js │ └── full-name.spec.js ├── bootstrap.js ├── directives │ ├── focus.js │ └── focus.spec.js ├── views │ ├── About.vue.po.js │ ├── Welcome.vue.po.js │ ├── About.vue │ ├── Login.vue.po.js │ ├── Welcome.vue │ ├── About.vue.spec.js │ ├── Login.vue │ ├── Welcome.vue.spec.js │ └── Login.vue.spec.js ├── main.js ├── App.vue.po.js ├── di.js ├── router.js ├── App.vue.spec.js ├── App.vue └── router.spec.js ├── .editorconfig ├── babel.config.js ├── .gitignore ├── .nycrc ├── vue.config.js ├── .eslintrc.js ├── TODO.md ├── LICENSE ├── package.json └── README.md /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not ie <= 8 4 | -------------------------------------------------------------------------------- /test/unit/globals/index.js: -------------------------------------------------------------------------------- 1 | import '..'; 2 | import './console'; 3 | import './di'; 4 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /.lintstagedrc: -------------------------------------------------------------------------------- 1 | { 2 | "*.{js,vue}": [ 3 | "npm run lint", 4 | "git add" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcibique/vue-testing-examples/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcibique/vue-testing-examples/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/store/auth/getters.js: -------------------------------------------------------------------------------- 1 | export function isAuthenticated (state) { 2 | return !!state.token; 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | -------------------------------------------------------------------------------- /src/components/MyCheckboxListContext.js: -------------------------------------------------------------------------------- 1 | const MY_CHECKBOX_LIST_CONTEXT = Symbol('MY_CHECKBOX_LIST_CONTEXT'); 2 | 3 | export default MY_CHECKBOX_LIST_CONTEXT; 4 | -------------------------------------------------------------------------------- /src/services/api.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { authRequestInterceptor } from './auth-interceptor'; 3 | 4 | axios.interceptors.request.use(authRequestInterceptor); 5 | -------------------------------------------------------------------------------- /src/store/auth/mutations.js: -------------------------------------------------------------------------------- 1 | export function resetToken (state) { 2 | state.token = null; 3 | } 4 | 5 | export function setToken (state, token) { 6 | state.token = token; 7 | } 8 | -------------------------------------------------------------------------------- /test/unit/globals/di.js: -------------------------------------------------------------------------------- 1 | import container from '@di'; 2 | 3 | beforeEach(function () { 4 | container.snapshot(); 5 | }); 6 | 7 | afterEach(function () { 8 | container.restore(); 9 | }); 10 | -------------------------------------------------------------------------------- /src/filters/full-name.js: -------------------------------------------------------------------------------- 1 | export function fullName (profile) { 2 | if (!profile) { 3 | return ''; 4 | } 5 | 6 | return `${profile.firstName || ''} ${profile.lastName || ''}`.trim() || profile.username || ''; 7 | } 8 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | ['@vue/app', { jsx: false, loose: true, useBuiltIns: 'entry' }] 4 | ], 5 | 'env': { 6 | 'test': { 7 | 'plugins': [ 8 | 'istanbul' 9 | ] 10 | } 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /src/components/MyCheckboxListStatus.vue.po.js: -------------------------------------------------------------------------------- 1 | import BasePageObj from '@unit/base.po'; 2 | 3 | export default class MyCheckboxListStatusPageObj extends BasePageObj { 4 | get defaultMessage () { 5 | return this.tid('c-checkbox-list-status__default-message'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/components/MyButton.vue.po.js: -------------------------------------------------------------------------------- 1 | import BasePageObj from '@unit/base.po'; 2 | 3 | export default class MyButtonPageObj extends BasePageObj { 4 | click () { 5 | return this.wrapper.trigger('click'); 6 | } 7 | 8 | isPrimary () { 9 | return this.wrapper.classes().includes('c-button--primary'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/unit/utils/tid.js: -------------------------------------------------------------------------------- 1 | import { Wrapper, WrapperArray } from '@vue/test-utils'; 2 | 3 | Wrapper.prototype.tid = function (selector) { 4 | return this.find(`[tid~="${selector}"]`); 5 | }; 6 | 7 | Wrapper.prototype.tids = WrapperArray.prototype = function (selector) { 8 | return this.findAll(`[tid~="${selector}"]`); 9 | }; 10 | -------------------------------------------------------------------------------- /test/unit/utils/di.js: -------------------------------------------------------------------------------- 1 | import sinon from 'sinon'; 2 | import container, { overrideWithConstantValue } from '@di'; 3 | 4 | export function mockService (identifier) { 5 | let service = container.get(identifier); 6 | let serviceMock = sinon.mock(service); 7 | overrideWithConstantValue(identifier, service); 8 | return serviceMock; 9 | } 10 | -------------------------------------------------------------------------------- /src/services/profile.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Register } from '@di'; 3 | 4 | export const PROFILE_SERVICE_ID = Symbol('profileService'); 5 | 6 | @Register(PROFILE_SERVICE_ID) 7 | export default class ProfileService { 8 | getProfile () { 9 | return axios.get('/api/profile').then(response => response.data); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | 23 | .nyc_output 24 | test/reports 25 | -------------------------------------------------------------------------------- /src/services/auth-interceptor.js: -------------------------------------------------------------------------------- 1 | import container from '@di'; 2 | import { STORE_ID } from '@/store'; 3 | 4 | export function authRequestInterceptor (config) { 5 | let store = container.get(STORE_ID); 6 | if (store.state.auth.token) { 7 | config.headers.Authorization = `Bearer ${store.state.auth.token}`; 8 | } 9 | 10 | return config; 11 | } 12 | -------------------------------------------------------------------------------- /src/bootstrap.js: -------------------------------------------------------------------------------- 1 | import 'core-js/stable'; 2 | import 'regenerator-runtime/runtime'; 3 | import 'reflect-metadata'; 4 | import { Component } from 'vue-property-decorator'; 5 | 6 | // do this before any VUE component is loaded, otherwise @Component() would ignore navigation guards 7 | Component.registerHooks(['beforeRouteEnter', 'beforeRouteLeave', 'beforeRouteUpdate']); 8 | -------------------------------------------------------------------------------- /src/store/auth/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions'; 2 | import * as getters from './getters'; 3 | import * as mutations from './mutations'; 4 | 5 | export default function createModule () { 6 | return { 7 | namespaced: true, 8 | state: { 9 | token: null 10 | }, 11 | actions, 12 | mutations, 13 | getters 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /src/services/credentials.js: -------------------------------------------------------------------------------- 1 | import { Register } from '@di'; 2 | 3 | export const CREDENTIALS_SERVICE_ID = Symbol('credentialsService'); 4 | 5 | @Register(CREDENTIALS_SERVICE_ID) 6 | export default class CredentialsService { 7 | sanitize (input) { 8 | if (input && input.trim) { 9 | return input.trim(); 10 | } else { 11 | return input; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "src/**/*.(js|vue)" 4 | ], 5 | "exclude": [ 6 | "src/**/*.spec.js", 7 | "src/**/*.po.js", 8 | "src/**/*.stub.js" 9 | ], 10 | "instrument": false, 11 | "sourceMap": false, 12 | "reporter": [ 13 | "html", 14 | "text" 15 | ], 16 | "report-dir": "./test/reports", 17 | "tempDirectory": "./test/reports/.nyc_output" 18 | } 19 | -------------------------------------------------------------------------------- /src/directives/focus.js: -------------------------------------------------------------------------------- 1 | export default { 2 | inserted (el, binding) { 3 | if (typeof binding.value === 'undefined' || !!binding.value) { 4 | el.focus(); 5 | } 6 | }, 7 | componentUpdated (el, binding) { 8 | if (binding.value !== binding.oldValue) { 9 | if (typeof binding.value === 'undefined' || !!binding.value) { 10 | el.focus(); 11 | } else { 12 | el.blur(); 13 | } 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/services/email.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Register } from '@di'; 3 | 4 | export const EMAIL_SERVICE_ID = Symbol('emailService'); 5 | 6 | @Register(EMAIL_SERVICE_ID) 7 | export default class EmailService { 8 | getEmails () { 9 | return axios.get('/api/emails').then(response => response.data); 10 | } 11 | markEmailAsRead (id) { 12 | return axios.put(`/api/emails/${id}`, { unread: false }).then(response => response.data); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/views/About.vue.po.js: -------------------------------------------------------------------------------- 1 | import BasePageObj from '@unit/base.po'; 2 | 3 | export default class AboutViewPageObj extends BasePageObj { 4 | get heading () { 5 | return this.tid('c-about__heading'); 6 | } 7 | 8 | get githubLink () { 9 | return this.tid('c-about__github-link'); 10 | } 11 | 12 | get backLink () { 13 | return this.tid('c-about__back-link'); 14 | } 15 | 16 | goBack () { 17 | return this.backLink.trigger('click'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | let path = require('path'); 2 | let useServiceStub = !!process.env.npm_config_stub; 3 | 4 | module.exports = { 5 | productionSourceMap: false, 6 | 7 | chainWebpack (config) { 8 | config.resolve.alias.set('@unit', path.resolve(__dirname, 'test', 'unit')); 9 | config.resolve.alias.set('@di', path.resolve(__dirname, 'src', 'di.js')); 10 | 11 | if (useServiceStub) { 12 | config.resolve.extensions.prepend('.stub.js'); 13 | } 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | VUE testing examples 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /test/unit/index.js: -------------------------------------------------------------------------------- 1 | import chai from 'chai'; 2 | import chaiAsPromised from 'chai-as-promised'; 3 | import sinon from 'sinon'; 4 | import sinonChai from 'sinon-chai'; 5 | import sinonStubPromise from 'sinon-stub-promise'; 6 | import { config } from '@vue/test-utils'; 7 | 8 | import './utils/axios'; 9 | import './utils/store'; 10 | import './utils/tid'; 11 | 12 | config.logModifiedComponents = false; 13 | 14 | chai.use(sinonChai); 15 | chai.use(chaiAsPromised); 16 | sinonStubPromise(sinon); 17 | -------------------------------------------------------------------------------- /test/unit/utils/axios.js: -------------------------------------------------------------------------------- 1 | import AxiosMockAdapter from 'axios-mock-adapter'; 2 | import { expect } from 'chai'; 3 | 4 | AxiosMockAdapter.prototype.verifyNoOutstandingExpectation = function () { 5 | for (let verb in this.handlers) { 6 | let expectations = this.handlers[verb]; 7 | if (expectations.length > 0) { 8 | for (let expectation of expectations) { 9 | expect.fail(1, 0, `Expected URL ${expectation[0]} to have been requested but it wasn't.`); 10 | } 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /test/unit/globals/console.js: -------------------------------------------------------------------------------- 1 | import sinon from 'sinon'; 2 | import { expect } from 'chai'; 3 | 4 | beforeEach(function () { 5 | if (console.error.restore) { 6 | console.error.restore(); 7 | } 8 | if (console.error.reset) { 9 | console.error.reset(); 10 | } 11 | sinon.spy(console, 'error'); 12 | }); 13 | 14 | afterEach(function () { 15 | expect(console.error, `console.error() has been called ${console.error.callCount} times.`).not.to.have.been.called; 16 | console.error.restore(); 17 | }); 18 | -------------------------------------------------------------------------------- /src/views/Welcome.vue.po.js: -------------------------------------------------------------------------------- 1 | import BasePageObj from '@unit/base.po'; 2 | import EmailsPageObj from '@/components/Emails.vue.po'; 3 | 4 | export default class WelcomeViewPageObj extends BasePageObj { 5 | get header () { 6 | return this.tid('welcome__header'); 7 | } 8 | 9 | get loading () { 10 | return this.tid('welcome__loading'); 11 | } 12 | 13 | get loadingError () { 14 | return this.tid('welcome_loading-error'); 15 | } 16 | 17 | get emails () { 18 | return new EmailsPageObj(this.tid('welcome__emails')); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | import { registerConstantValue } from './di'; 3 | import Vue from 'vue'; 4 | import App from './App.vue'; 5 | import { createRouter, ROUTER_ID } from './router'; 6 | import { createStore, STORE_ID } from './store'; 7 | import './services/api'; 8 | 9 | Vue.config.productionTip = false; 10 | 11 | let store = createStore(Vue); 12 | registerConstantValue(STORE_ID, store); 13 | 14 | let router = createRouter(Vue); 15 | registerConstantValue(ROUTER_ID, router); 16 | 17 | new Vue({ 18 | router, 19 | store, 20 | render: h => h(App) 21 | }).$mount('#app'); 22 | -------------------------------------------------------------------------------- /src/store/auth/actions.js: -------------------------------------------------------------------------------- 1 | import container from '@di'; 2 | import { AUTH_SERVICE_ID } from '@/services/auth'; 3 | 4 | export async function login ({ commit }, { username, password, rememberMe }) { 5 | let authService = container.get(AUTH_SERVICE_ID); 6 | let token = await authService.login(username, password, rememberMe); 7 | commit('setToken', token); 8 | return token; 9 | } 10 | 11 | export async function logout ({ commit }) { 12 | let authService = container.get(AUTH_SERVICE_ID); 13 | await authService.logout().finally(function () { 14 | commit('resetToken'); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /src/App.vue.po.js: -------------------------------------------------------------------------------- 1 | import BasePageObj from '@unit/base.po'; 2 | 3 | export default class AppPageObj extends BasePageObj { 4 | get header () { 5 | return this.tid('c-app__header'); 6 | } 7 | 8 | get main () { 9 | return this.tid('c-app__main'); 10 | } 11 | 12 | get footer () { 13 | return this.tid('c-app__footer'); 14 | } 15 | 16 | get logoutLink () { 17 | return this.tid('c-app__logout-link'); 18 | } 19 | 20 | get aboutLink () { 21 | return this.tid('c-app__about-link'); 22 | } 23 | 24 | logout () { 25 | this.logoutLink.trigger('click'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/store/auth/getters.spec.js: -------------------------------------------------------------------------------- 1 | import '@unit/globals'; 2 | import { expect } from 'chai'; 3 | 4 | import { isAuthenticated } from './getters'; 5 | 6 | describe('Store - Auth module getters', function () { 7 | describe('isAuthenticated', function () { 8 | it('should return true if authentication token exists', function () { 9 | expect(isAuthenticated({ token: 'random_token' })).to.be.true; 10 | }); 11 | 12 | it('should return false if authentication token doesn\'t exists', function () { 13 | expect(isAuthenticated({ token: null })).to.be.false; 14 | }); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | mocha: true 6 | }, 7 | extends: [ 8 | 'plugin:vue/essential', 9 | '@vue/standard' 10 | ], 11 | rules: { 12 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 13 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', 14 | 'semi': ['error', 'always'], 15 | 'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': true }], 16 | 'no-unused-expressions': 'off' 17 | }, 18 | parserOptions: { 19 | parser: 'babel-eslint', 20 | ecmaFeatures: { 21 | legacyDecorators: true 22 | } 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /src/components/MyCheckboxList.vue.po.js: -------------------------------------------------------------------------------- 1 | import BasePageObj from '@unit/base.po'; 2 | import MyCheckboxListStatusPageObj from './MyCheckboxListStatus.vue.po'; 3 | import MyCheckboxPageObj from './MyCheckbox.vue.po'; 4 | 5 | export default class MyCheckboxListPageObj extends BasePageObj { 6 | get statusElement () { 7 | return this.tid('c-checkbox-list-status'); 8 | } 9 | 10 | get status () { 11 | return new MyCheckboxListStatusPageObj(this.statusElement); 12 | } 13 | 14 | get checkboxElements () { 15 | return this.tids('c-checkbox'); 16 | } 17 | 18 | get checkboxes () { 19 | return this.checkboxElements.wrappers.map(checkboxElement => new MyCheckboxPageObj(checkboxElement)); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/services/profile.stub.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import AxiosMockAdapter from 'axios-mock-adapter'; 3 | import { Override } from '@di'; 4 | 5 | import ProfileService, { PROFILE_SERVICE_ID } from './profile.js'; // always use '.js' extension otherwise this module will be required. 6 | 7 | export { PROFILE_SERVICE_ID }; 8 | 9 | @Override(PROFILE_SERVICE_ID) 10 | export default class ProfileStubService extends ProfileService { 11 | getProfile () { 12 | let axiosMock = new AxiosMockAdapter(axios); 13 | axiosMock.onGet('/api/profile').replyOnce(200, { 14 | firstName: 'John', 15 | lastName: 'Doe', 16 | username: 'john.doe', 17 | id: '123' 18 | }); 19 | 20 | return super.getProfile().finally(() => axiosMock.restore()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/MyCheckbox.vue.po.js: -------------------------------------------------------------------------------- 1 | import BasePageObj from '@unit/base.po'; 2 | 3 | export default class MyCheckboxPageObj extends BasePageObj { 4 | get icon () { 5 | return this.wrapper.tid('c-checkbox__icon'); 6 | } 7 | 8 | get label () { 9 | return this.wrapper.tid('c-checkbox__label'); 10 | } 11 | 12 | text () { 13 | return this.label.text(); 14 | } 15 | 16 | check () { 17 | return this.icon.trigger('click'); 18 | } 19 | 20 | get isChecked () { 21 | return this.icon.classes().includes('c-checkbox__icon--checked'); 22 | } 23 | 24 | get isFocused () { 25 | return this.icon.classes().includes('c-checkbox__icon--focused'); 26 | } 27 | 28 | setChecked (newValue) { 29 | if (this.isChecked !== newValue) { 30 | this.check(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import { namespace } from 'vuex-class'; 4 | 5 | import createAuthModule from './auth'; 6 | 7 | export const AUTH_MODULE_ID = 'auth'; 8 | 9 | export function createStore (/* istanbul ignore next */ vueInstance = Vue) { 10 | vueInstance.use(Vuex); 11 | 12 | return new Vuex.Store({ 13 | namespaced: true, 14 | strict: process.env.NODE_ENV !== 'production', 15 | modules: { 16 | [AUTH_MODULE_ID]: createAuthModule() 17 | } 18 | }); 19 | } 20 | 21 | export const STORE_ID = Symbol('store'); 22 | 23 | let { Getter: AuthGetter, State: AuthState, Action: AuthAction, Mutation: AuthMutation } = namespace(AUTH_MODULE_ID); 24 | 25 | export { 26 | AuthGetter, 27 | AuthState, 28 | AuthAction, 29 | AuthMutation 30 | }; 31 | -------------------------------------------------------------------------------- /src/services/auth.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { Register } from '@di'; 3 | 4 | import { CREDENTIALS_SERVICE_ID } from './credentials'; 5 | 6 | export const AUTH_SERVICE_ID = Symbol('authService'); 7 | 8 | @Register(AUTH_SERVICE_ID, [CREDENTIALS_SERVICE_ID]) 9 | export default class AuthService { 10 | constructor (credentialsService) { 11 | this.credentialsService = credentialsService; 12 | } 13 | 14 | login (username, password, rememberMe) { 15 | username = this.credentialsService.sanitize(username); 16 | password = this.credentialsService.sanitize(password); 17 | return axios.post('/api/login', { username, password, rememberMe }).then(response => response.data); 18 | } 19 | 20 | logout () { 21 | return axios.post('/api/logout').then(response => response.data); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/components/MyButton.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 11 | 12 | 35 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # To document: 2 | 3 | 1. Testing navigation guards 4 | 1. Testing filter 5 | 1. Testing directive 6 | 1. Writing complex integration test for a component 7 | 1. Mocking router 8 | 1. Scoped slots 9 | 1. Inversify and mocking router/store in tests 10 | 1. Test coverage 11 | 12 | Introduce the dev stack: 13 | 14 | * vue, vuex, vue-router, vue-property-decorator, vuex-class, axios, lodash, inversify (vanilla js solution), sinon, mocha, sinon-chai, sinon-stub-promise flush-promises, lolex 15 | 16 | Provide examples for 17 | 18 | * ??? testing mutations, actions ??? 19 | * ??? testing `router.push()` to the route with async component using `import()` ??? 20 | * ??? broken test coverage (100% coverage but still not taking all paths: https://twitter.com/getify/status/955939257755021312) ??? 21 | * ??? 100% test coverage without need to test everything (https://labs.ig.com/code-coverage-100-percent-tragedy) ??? 22 | 23 | Issues: 24 | 25 | * !!! `trigger("click")` on `