├── .browserslistrc ├── .env.test ├── .eslintrc.js ├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .nvmrc ├── LICENSE.md ├── Makefile ├── README.md ├── babel.config.js ├── jest.config.js ├── package.json ├── postcss.config.js ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ └── logo.png ├── aws-exports.js ├── components │ ├── Footer.vue │ ├── Menu.vue │ └── auth │ │ └── Alert.vue ├── main.js ├── pages │ ├── Dashboard.vue │ ├── Home.vue │ └── auth │ │ ├── ChangePassword.vue │ │ ├── ConfirmPasswordReset.vue │ │ ├── ConfirmSignUp.vue │ │ ├── PasswordReset.vue │ │ ├── SignIn.vue │ │ ├── SignOut.vue │ │ └── SignUp.vue ├── router │ └── index.js └── store │ ├── index.js │ └── modules │ └── auth.js ├── tests └── unit │ └── footer.spec.js ├── vue.config.js └── yarn.lock /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | IDENTITY_POOL_ID="xx-xxxxxx-x:XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" 2 | AWS_REGION="xx-xxxxxx-x" 3 | USER_POOL_ID="xx-xxxxxx-x_XXXXXXXXX" 4 | USER_POOL_WEB_CLIENT_ID="XXXXXXXXXXXXXXXXXXXXXXXXXX" 5 | PINPOINT_APP_ID="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: ["plugin:vue/essential", "@vue/prettier"], 7 | rules: { 8 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off", 9 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off" 10 | }, 11 | parserOptions: { 12 | parser: "babel-eslint" 13 | }, 14 | overrides: [ 15 | { 16 | files: [ 17 | "**/__tests__/*.{j,t}s?(x)", 18 | "**/tests/unit/**/*.spec.{j,t}s?(x)" 19 | ], 20 | env: { 21 | jest: true 22 | } 23 | } 24 | ] 25 | }; 26 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [10.x, 12.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: npm install, build, and test 21 | run: | 22 | yarn 23 | yarn run build --if-present 24 | yarn jest 25 | env: 26 | CI: true 27 | -------------------------------------------------------------------------------- /.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 | # Don't commit your aws mobile configuration file 24 | .envrc -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 10 2 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2018 Mark Wolfe 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ci: clean build deploy 2 | 3 | clean: 4 | rm -rf dist 5 | 6 | build: 7 | yarn build 8 | 9 | deploy: 10 | aws s3 sync dist/ s3://$(S3_HOSTING_BUCKET)/ --delete --acl public-read 11 | aws cloudfront create-invalidation --distribution-id $(DISTRIBUTION_ID) --paths '/*' -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cognito-vue-bootstrap 2 | 3 | This application illustrates how to use the [Amazon Amplify](https://github.com/aws/aws-amplify) with [vue.js](https://vuejs.org/), it includes signup, signin, signout, recover password and account verification using email or sms codes. It uses [bootstrap-vue](https://bootstrap-vue.js.org/) for layout. 4 | 5 | # overview 6 | 7 | This example application currently illustrates the following: 8 | 9 | * Sign Up for an account 10 | * Verify your account by entering verification code which has been sent to you via emailed or SMS 11 | * Dashboard which requires authentication to access 12 | * Change Password 13 | * Recover Password using verification code which has been sent to you via emailed or SMS 14 | * Sign In to the application 15 | * Sign Out of the application 16 | 17 | Demo version is located at https://cognito-vue-bootstrapv2.wolfe.id.au/ 18 | 19 | # Build Setup 20 | 21 | Before you start have a read over [What is Amazon Cognito?](http://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html) 22 | 23 | To setup this project you first need to configure an aws mobile project using the following snapshot [CognitoVue](https://console.aws.amazon.com/mobilehub/home#/snapshot/ef9bu3t7nsa8uz). 24 | 25 | For more information on this process see [Exporting and Importing AWS Mobile Hub Projects](https://docs.aws.amazon.com/aws-mobile/latest/developerguide/project-import-export.html) 26 | 27 | Once you have imported the project you will have created: 28 | 29 | * An S3 bucket with Cloudfront for your web application. 30 | * A Cognito pool to store your users 31 | * An analytics project to capture metrics on your users login / failure ect. 32 | 33 | Click on the integrate button in your aws mobile project, the download and extract the cloud config zip file, find `aws-exports.js` inside, and replace this file in `src/` directory. 34 | 35 | I use [yarn](https://yarnpkg.com/) to build and run this project. 36 | 37 | ``` bash 38 | # install dependencies 39 | yarn install 40 | 41 | # serve with hot reload at localhost:8080 42 | yarn serve 43 | 44 | # build for production with minification 45 | yarn build 46 | ``` 47 | 48 | Sync all the files from your `dist` directory up to the S3 hosting bucket within your AWS Mobile project using the following command. 49 | 50 | ``` 51 | aws --region ap-southeast-2 s3 sync dist/ s3://cognitovuebootstrap-hosting-mobilehub-XXXXXXXXXX/ --delete --acl public-read 52 | ``` 53 | 54 | # hosting configuration notes 55 | 56 | To host a website on a custom URL using AWS mobile I have found some changes to the current setup. Navigating to the buckets and CDN configuration is done via the `Hosting And Streaming` panel in the mobile project UI. 57 | 58 | * disable website hosting on the hosting S3 bucket 59 | * add an Error Page which sends any 404 (not found) errors to `/index.html` in cloudfront 60 | * enable redirect http -> https on the origin 61 | * configure a route53 domain for your website 62 | * configure a AWS Certificate Manager (ACM) certificate for your domain 63 | * add an A record in route53 of type `alias` pointing to your Cloudfront distribution, then update the origin domain name to match the FQDN. 64 | 65 | For a more detailed explanation on how things work, checkout: 66 | 67 | * [AWS Amplify Documentation](https://aws.github.io/aws-amplify/) 68 | * [AWS Amplify Modularization](https://github.com/aws-amplify/amplify-js/wiki/Amplify-modularization) 69 | * [docs for vue-cli](https://cli.vuejs.org/) 70 | * [docs for vue-router](http://router.vuejs.org/en/) 71 | * [docs for vuex](https://vuex.vuejs.org/) 72 | 73 | # License 74 | 75 | This project is released under the Apache License, Version 2.0. 76 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"] 3 | }; 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: "@vue/cli-plugin-unit-jest" 3 | }; 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cognito-vue-bootstrap", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "test:unit": "vue-cli-service test:unit", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "@aws-amplify/auth": "^1.5.0", 13 | "bootstrap-vue": "^2.0.4", 14 | "core-js": "^3.3.2", 15 | "vue": "^2.6.10", 16 | "vue-awesome": "^3.5.4", 17 | "vue-resource": "^1.5.1", 18 | "vue-router": "^3.1.3", 19 | "vuex": "^3.0.1", 20 | "vuex-localstorage": "^1.0.0" 21 | }, 22 | "devDependencies": { 23 | "@testing-library/vue": "^4.1.0", 24 | "@vue/cli-plugin-babel": "^4.0.0", 25 | "@vue/cli-plugin-eslint": "^4.0.0", 26 | "@vue/cli-plugin-router": "^4.0.0", 27 | "@vue/cli-plugin-unit-jest": "^4.0.0", 28 | "@vue/cli-plugin-vuex": "^4.0.0", 29 | "@vue/cli-service": "^4.0.0", 30 | "@vue/eslint-config-prettier": "^5.0.0", 31 | "@vue/test-utils": "1.0.0-beta.29", 32 | "babel-eslint": "^10.0.3", 33 | "eslint": "^5.16.0", 34 | "eslint-plugin-prettier": "^3.1.1", 35 | "eslint-plugin-vue": "^5.0.0", 36 | "prettier": "^1.18.2", 37 | "vue-template-compiler": "^2.6.10" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {} 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wolfeidau/cognito-vue-bootstrap/84801f7fa56327c92fc500c2622e7b818b2ce9c1/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | cognito-vue-bootstrapv3 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 25 | 26 | 58 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wolfeidau/cognito-vue-bootstrap/84801f7fa56327c92fc500c2622e7b818b2ce9c1/src/assets/logo.png -------------------------------------------------------------------------------- /src/aws-exports.js: -------------------------------------------------------------------------------- 1 | const config = { 2 | // To get the AWS Credentials, you need to configure 3 | // the Auth module with your Cognito Federated Identity Pool 4 | Auth: { 5 | identityPoolId: process.env.IDENTITY_POOL_ID, 6 | region: process.env.AWS_REGION, 7 | userPoolId: process.env.USER_POOL_ID, 8 | userPoolWebClientId: process.env.USER_POOL_WEB_CLIENT_ID, 9 | mandatorySignIn: false, 10 | clientMetadata: { app: "cognito-vue-bootstrap" } 11 | }, 12 | Analytics: { 13 | disabled: false, 14 | autoSessionRecord: true, 15 | AWSPinpoint: { 16 | appId: process.env.PINPOINT_APP_ID, 17 | region: process.env.AWS_REGION 18 | } 19 | } 20 | }; 21 | 22 | export default config; 23 | -------------------------------------------------------------------------------- /src/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 14 | 15 | 26 | -------------------------------------------------------------------------------- /src/components/Menu.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 64 | 65 | 77 | -------------------------------------------------------------------------------- /src/components/auth/Alert.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 22 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import BootstrapVue from "bootstrap-vue"; 3 | import App from "@/App.vue"; 4 | import router from "@/router"; 5 | import store from "@/store"; 6 | import Auth from "@aws-amplify/auth"; 7 | import AuthConfig from "@/aws-exports"; 8 | 9 | Auth.configure(AuthConfig); 10 | 11 | Vue.use(BootstrapVue); 12 | 13 | Vue.config.productionTip = false; 14 | 15 | new Vue({ 16 | router, 17 | store, 18 | el: "#app", 19 | render: h => h(App) 20 | }); 21 | -------------------------------------------------------------------------------- /src/pages/Dashboard.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 21 | -------------------------------------------------------------------------------- /src/pages/Home.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /src/pages/auth/ChangePassword.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 77 | -------------------------------------------------------------------------------- /src/pages/auth/ConfirmPasswordReset.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 99 | -------------------------------------------------------------------------------- /src/pages/auth/ConfirmSignUp.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 82 | -------------------------------------------------------------------------------- /src/pages/auth/PasswordReset.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 64 | -------------------------------------------------------------------------------- /src/pages/auth/SignIn.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/pages/auth/SignOut.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 19 | -------------------------------------------------------------------------------- /src/pages/auth/SignUp.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 106 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Router from "vue-router"; 3 | import Home from "@/pages/Home.vue"; 4 | import Dashboard from "@/pages/Dashboard.vue"; 5 | import SignIn from "@/pages/auth/SignIn.vue"; 6 | import SignUp from "@/pages/auth/SignUp.vue"; 7 | import SignOut from "@/pages/auth/SignOut.vue"; 8 | import ConfirmSignUp from "@/pages/auth/ConfirmSignUp.vue"; 9 | import PasswordReset from "@/pages/auth/PasswordReset.vue"; 10 | import ChangePassword from "@/pages/auth/ChangePassword.vue"; 11 | import ConfirmPasswordReset from "@/pages/auth/ConfirmPasswordReset.vue"; 12 | 13 | import store from "@/store"; 14 | 15 | Vue.use(Router); 16 | 17 | const routes = [ 18 | { 19 | path: "/", 20 | name: "home", 21 | component: Home, 22 | meta: { title: "Home", auth: false } 23 | }, 24 | { 25 | path: "/dashboard", 26 | name: "dashboard", 27 | component: Dashboard, 28 | meta: { title: "Dashboard", auth: true } 29 | }, 30 | { 31 | path: "/signIn", 32 | name: "signIn", 33 | component: SignIn, 34 | meta: { title: "Sign In", auth: false } 35 | }, 36 | { 37 | path: "/signOut", 38 | name: "signOut", 39 | component: SignOut, 40 | meta: { title: "Sign Out", auth: true } 41 | }, 42 | { 43 | path: "/signUp", 44 | name: "signUp", 45 | component: SignUp, 46 | meta: { title: "Sign Up", auth: false } 47 | }, 48 | { 49 | path: "/confirmSignUp", 50 | name: "confirmSignUp", 51 | component: ConfirmSignUp, 52 | meta: { title: "Confirm SignUp", auth: false } 53 | }, 54 | { 55 | path: "/changePassword", 56 | name: "changePassword", 57 | component: ChangePassword, 58 | meta: { title: "Change Password", auth: true } 59 | }, 60 | { 61 | path: "/passwordReset", 62 | name: "passwordReset", 63 | component: PasswordReset, 64 | meta: { title: "Password Reset", auth: false } 65 | }, 66 | { 67 | path: "/confirmPasswordReset", 68 | name: "confirmPasswordReset", 69 | component: ConfirmPasswordReset, 70 | meta: { title: "Confirm Password Reset", auth: false } 71 | } 72 | ]; 73 | 74 | const router = new Router({ mode: "history", routes }); 75 | 76 | // this routine will ensure that any pages marked as `auth` in the `meta` section are 77 | // protected from access by unauthenticated users. 78 | router.beforeEach((to, from, next) => { 79 | // Use the page's router title to name the page 80 | if (to.meta && to.meta.title) { 81 | document.title = to.meta.title; 82 | } 83 | 84 | // is there a meta and auth attribute? 85 | if (to.meta && to.meta.auth !== undefined) { 86 | // if the page requires auth 87 | if (to.meta.auth) { 88 | // and we are authenticated? 89 | if (store.getters["auth/isAuthenticated"]) { 90 | next(); // route normally 91 | return; 92 | } 93 | // otherwise off to the sign in page 94 | router.push({ name: "signIn" }); 95 | return; 96 | } 97 | // otherwise are we already authenticated? 98 | if (store.getters["auth/isAuthenticated"]) { 99 | // yes we are, so off to dashboard 100 | router.push({ name: "dashboard" }); 101 | return; 102 | } 103 | next(); // route normally 104 | return; 105 | } 106 | next(); // route normally 107 | return; 108 | }); 109 | 110 | export default router; 111 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | import createPersist from "vuex-localstorage"; 4 | 5 | Vue.use(Vuex); 6 | 7 | // Modules 8 | import auth from "./modules/auth"; 9 | 10 | const debug = process.env.NODE_ENV !== "production"; 11 | 12 | const store = new Vuex.Store({ 13 | modules: { 14 | auth 15 | }, 16 | strict: debug, 17 | plugins: [ 18 | createPersist({ 19 | namespace: "cognito-vue-bootstrap", 20 | initialState: {}, 21 | // ONE_WEEK 22 | expires: 7 * 24 * 60 * 60 * 1e3 23 | }) 24 | ] 25 | }); 26 | 27 | export default store; 28 | -------------------------------------------------------------------------------- /src/store/modules/auth.js: -------------------------------------------------------------------------------- 1 | import Auth from "@aws-amplify/auth"; 2 | import Amplify from "@aws-amplify/core"; 3 | 4 | const Logger = Amplify.Logger; 5 | Logger.LOG_LEVEL = "DEBUG"; // to show detailed logs from Amplify library 6 | const logger = new Logger("store:auth"); 7 | 8 | // initial state 9 | const state = { 10 | user: null, 11 | isAuthenticated: false, 12 | authenticationStatus: null 13 | }; 14 | 15 | const getters = { 16 | authenticatedUser: state => state.user, 17 | isAuthenticated: state => state.isAuthenticated, 18 | authenticationStatus: state => { 19 | return state.authenticationStatus 20 | ? state.authenticationStatus 21 | : { variant: "secondary" }; 22 | }, 23 | hasAuthenticationStatus: state => { 24 | return !!state.authenticationStatus; 25 | } 26 | }; 27 | 28 | const mutations = { 29 | setAuthenticationError(state, err) { 30 | logger.debug("auth error: {}", err); 31 | state.authenticationStatus = { 32 | state: "failed", 33 | message: err.message, 34 | variant: "danger" 35 | }; 36 | }, 37 | clearAuthenticationStatus: state => { 38 | state.authenticationStatus = null; 39 | }, 40 | setUserAuthenticated(state, user) { 41 | state.user = user; 42 | state.isAuthenticated = true; 43 | }, 44 | clearAuthentication(state) { 45 | state.user = null; 46 | state.userId = null; 47 | state.isAuthenticated = false; 48 | } 49 | }; 50 | 51 | const actions = { 52 | clearAuthenticationStatus: context => { 53 | context.commit("clearAuthenticationStatus", null); 54 | }, 55 | signIn: async (context, params) => { 56 | logger.debug("signIn for {}", params.username); 57 | context.commit("auth/clearAuthenticationStatus", null, { root: true }); 58 | try { 59 | const user = await Auth.signIn(params.username, params.password); 60 | context.commit("setUserAuthenticated", user); 61 | } catch (err) { 62 | context.commit("auth/setAuthenticationError", err, { root: true }); 63 | } 64 | }, 65 | signOut: async context => { 66 | try { 67 | await Auth.signOut(); 68 | } catch (err) { 69 | logger.error("error during sign out: {}", err); 70 | } 71 | context.commit("auth/clearAuthentication", null, { root: true }); 72 | }, 73 | signUp: async (context, params) => { 74 | context.commit("auth/clearAuthenticationStatus", null, { root: true }); 75 | try { 76 | await Auth.signUp(params); 77 | context.commit("auth/clearAuthentication", null, { root: true }); 78 | } catch (err) { 79 | context.commit("auth/setAuthenticationError", err, { root: true }); 80 | } 81 | }, 82 | confirmSignUp: async (context, params) => { 83 | logger.debug("confirm signup for {}", params.username); 84 | context.commit("auth/clearAuthenticationStatus", null, { root: true }); 85 | try { 86 | await Auth.confirmSignUp(params.username, params.code); 87 | } catch (err) { 88 | context.commit("auth/setAuthenticationError", err, { root: true }); 89 | } 90 | }, 91 | confirmResend: async (context, params) => { 92 | context.commit("auth/clearAuthenticationStatus", null, { root: true }); 93 | try { 94 | await Auth.resendSignUp(params.username); 95 | } catch (err) { 96 | context.commit("auth/setAuthenticationError", err, { root: true }); 97 | } 98 | }, 99 | passwordReset: async (context, params) => { 100 | context.commit("auth/clearAuthenticationStatus", null, { root: true }); 101 | try { 102 | await Auth.forgotPassword(params.username); 103 | } catch (err) { 104 | context.commit("auth/setAuthenticationError", err, { root: true }); 105 | } 106 | }, 107 | confirmPasswordReset: async (context, params) => { 108 | context.commit("auth/clearAuthenticationStatus", null, { root: true }); 109 | try { 110 | await Auth.forgotPasswordSubmit( 111 | params.username, 112 | params.code, 113 | params.password 114 | ); 115 | } catch (err) { 116 | context.commit("auth/setAuthenticationError", err, { root: true }); 117 | } 118 | }, 119 | passwordResetResend: async (context, params) => { 120 | context.commit("auth/clearAuthenticationStatus", null, { root: true }); 121 | try { 122 | await Auth.passwordResetResend(params.username); 123 | } catch (err) { 124 | context.commit("auth/setAuthenticationError", err, { root: true }); 125 | } 126 | }, 127 | passwordChange: async (context, params) => { 128 | logger.debug("password change for {}", context.state.user.username); 129 | context.commit("auth/clearAuthenticationStatus", null, { root: true }); 130 | try { 131 | const user = await Auth.currentAuthenticatedUser(); 132 | await Auth.changePassword( 133 | user, 134 | params.currentPassword, 135 | params.newPassword 136 | ); 137 | } catch (err) { 138 | context.commit("auth/setAuthenticationError", err, { root: true }); 139 | } 140 | } 141 | }; 142 | 143 | export default { 144 | namespaced: true, 145 | state, 146 | getters, 147 | actions, 148 | mutations 149 | }; 150 | -------------------------------------------------------------------------------- /tests/unit/footer.spec.js: -------------------------------------------------------------------------------- 1 | import { render } from '@testing-library/vue' 2 | import Footer from "@/components/Footer.vue"; 3 | 4 | describe("Footer.vue", () => { 5 | it("renders props.msg when passed", () => { 6 | const { getByText } = render(Footer) 7 | getByText('© 2018 Company, Inc.') 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | // vue.config.js 2 | module.exports = { 3 | transpileDependencies: [/\bvue-awesome\b/] 4 | }; 5 | --------------------------------------------------------------------------------