├── .browserslistrc ├── screenshot.gif ├── jest.config.js ├── public ├── favicon.ico ├── assets │ └── logo.png ├── index.html └── css │ └── app.css ├── babel.config.js ├── .editorconfig ├── types ├── vue.d.ts └── index.d.ts ├── dist ├── demo.html ├── ym-vue-tour.css └── ym-vue-tour.umd.min.js ├── .gitignore ├── src ├── main.js ├── shared │ └── constants.js └── components │ ├── VTour.vue │ └── VStep.vue ├── .eslintrc.js ├── tests └── unit │ ├── VStep.spec.js │ └── VTour.spec.js ├── README.md ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── LICENCE ├── package.json ├── .circleci └── config.yml ├── CONTRIBUTING.md └── CHANGELOG.md /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /screenshot.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GzhiYi/vue-tour/master/screenshot.gif -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: '@vue/cli-plugin-unit-jest' 3 | } 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GzhiYi/vue-tour/master/public/favicon.ico -------------------------------------------------------------------------------- /public/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GzhiYi/vue-tour/master/public/assets/logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*.{js,jsx,ts,tsx,vue}] 2 | indent_style = space 3 | indent_size = 2 4 | trim_trailing_whitespace = true 5 | insert_final_newline = true 6 | -------------------------------------------------------------------------------- /types/vue.d.ts: -------------------------------------------------------------------------------- 1 | import {Tour} from './index'; 2 | 3 | declare module 'vue/types/vue' { 4 | 5 | interface Vue { 6 | $tours: Record; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /dist/demo.html: -------------------------------------------------------------------------------- 1 | 2 | ym-vue-tour demo 3 | 4 | 5 | 6 | 7 | 8 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | 4 | # local env files 5 | .env.local 6 | .env.*.local 7 | 8 | # Log files 9 | npm-debug.log* 10 | yarn-debug.log* 11 | yarn-error.log* 12 | yarn.lock 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | 23 | .npmrc 24 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | import './vue'; 2 | import Vue from 'vue'; 3 | 4 | export function install (vue: typeof Vue): void 5 | 6 | export interface Tour { 7 | // Methods 8 | start(startStep?: string): void 9 | previousStep(): void 10 | nextStep(): void 11 | stop(): void 12 | skip(): void 13 | finish(): void 14 | currentStep: number 15 | 16 | // Computed 17 | isRunning: boolean 18 | isFirst: boolean 19 | isLast: boolean 20 | numberOfSteps: number 21 | } 22 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import VTour from './components/VTour' 2 | import VStep from './components/VStep' 3 | 4 | const YmVueTour = { 5 | install (Vue, options) { 6 | Vue.component(VTour.name, VTour) 7 | Vue.component(VStep.name, VStep) 8 | 9 | // Object containing Tour objects (see VTour.vue) where the tour name is used as key 10 | Vue.prototype.$tours = {} 11 | } 12 | } 13 | 14 | export default YmVueTour 15 | 16 | if (typeof window !== 'undefined' && window.Vue) { 17 | window.Vue.use(YmVueTour) 18 | } 19 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | 'extends': [ 7 | 'plugin:vue/essential', 8 | '@vue/standard' 9 | ], 10 | rules: { 11 | 'no-console': 'off', 12 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 13 | }, 14 | parserOptions: { 15 | parser: 'babel-eslint' 16 | }, 17 | overrides: [ 18 | { 19 | files: [ 20 | '**/__tests__/*.{j,t}s?(x)', 21 | '**/tests/unit/**/*.spec.{j,t}s?(x)' 22 | ], 23 | env: { 24 | jest: true 25 | } 26 | } 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /tests/unit/VStep.spec.js: -------------------------------------------------------------------------------- 1 | import { shallowMount } from '@vue/test-utils' 2 | import VStep from '@/components/VStep.vue' 3 | import { DEFAULT_OPTIONS } from '@/shared/constants' 4 | 5 | describe('VStep.vue', () => { 6 | it('renders props.step.content', () => { 7 | const step = { 8 | target: 'v-step-0', 9 | content: 'This is a demo step!' 10 | } 11 | 12 | const wrapper = shallowMount(VStep, { 13 | propsData: { 14 | step, 15 | stop: () => {}, 16 | labels: DEFAULT_OPTIONS.labels 17 | } 18 | }) 19 | 20 | expect(wrapper.text()).toContain(step.content) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vue Tour 2 | 3 | # Fork from 4 | 5 | [Vue Tour](https://github.com/pulsardev/vue-tour) 6 | 7 | > Vue Tour is a lightweight, simple and customizable tour plugin for use with Vue.js. 8 | > It provides a quick and easy way to guide your users through your application. 9 | 10 | # Added features 11 | 12 | 1. Custom themes. 13 | 14 | ```javascript 15 | // ... 16 | data() { 17 | return { 18 | tourOptions: { 19 | theme: '#506eff' 20 | } 21 | } 22 | } 23 | // ... 24 | ``` 25 | 26 | 2. Only tips mode.Shorten padding and margin event you set some buttons. 27 | 28 | ```javascript 29 | // ... 30 | data() { 31 | return { 32 | tourOptions: { 33 | onlyTips: true 34 | } 35 | } 36 | } 37 | // ... 38 | ``` 39 | 40 | 3. Partial style modification. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | 34 | You can also use this CodePen as a starter point to reproduce your issue: https://codepen.io/Eligius/pen/wvMXrVY. 35 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2018 Pulsar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ym-vue-tour", 3 | "version": "1.0.5", 4 | "description": "Vue Tour is a lightweight, simple and customizable tour plugin for use with Vue.js. It provides a quick and easy way to guide your users through your application.", 5 | "author": "zhiyi.gong@outlook.com", 6 | "license": "MIT", 7 | "main": "dist/ym-vue-tour.common.js", 8 | "module": "dist/ym-vue-tour.umd.js", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/GzhiYi/vue-tour" 12 | }, 13 | "typings": "types/index.d.ts", 14 | "keywords": [ 15 | "vue", 16 | "tour" 17 | ], 18 | "homepage": "https://pulsardev.github.io/vue-tour", 19 | "scripts": { 20 | "serve": "vue-cli-service serve", 21 | "build": "vue-cli-service build --target lib src/main.js", 22 | "test:unit": "vue-cli-service test:unit", 23 | "lint": "vue-cli-service lint" 24 | }, 25 | "dependencies": { 26 | "@popperjs/core": "^2.9.1", 27 | "hash-sum": "^2.0.0", 28 | "jump.js": "^1.0.2", 29 | "vue": "^2.6.12" 30 | }, 31 | "devDependencies": { 32 | "@vue/cli-plugin-babel": "^4.5.12", 33 | "@vue/cli-plugin-eslint": "^4.5.12", 34 | "@vue/cli-plugin-unit-jest": "^4.5.12", 35 | "@vue/cli-service": "^4.5.12", 36 | "@vue/eslint-config-standard": "^4.0.0", 37 | "@vue/test-utils": "^1.1.3", 38 | "babel-eslint": "^10.1.0", 39 | "core-js": "^3.9.1", 40 | "eslint": "^5.16.0", 41 | "eslint-plugin-vue": "^5.0.0", 42 | "sass": "^1.32.8", 43 | "sass-loader": "^8.0.2", 44 | "vue-template-compiler": "^2.6.12", 45 | "regenerator-runtime": "^0.13.9" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/shared/constants.js: -------------------------------------------------------------------------------- 1 | export const DEFAULT_CALLBACKS = { 2 | onStart: () => {}, 3 | onPreviousStep: (currentStep) => {}, 4 | onNextStep: (currentStep) => {}, 5 | onStop: () => {}, 6 | onSkip: () => {}, 7 | onFinish: () => {} 8 | } 9 | 10 | export const DEFAULT_OPTIONS = { 11 | highlight: false, 12 | labels: { 13 | buttonSkip: 'Skip tour', 14 | buttonPrevious: 'Previous', 15 | buttonNext: 'Next', 16 | buttonStop: 'Finish' 17 | }, 18 | enabledButtons: { 19 | buttonSkip: true, 20 | buttonPrevious: true, 21 | buttonNext: true, 22 | buttonStop: true 23 | }, 24 | startTimeout: 0, 25 | stopOnTargetNotFound: true, 26 | useKeyboardNavigation: true, 27 | enabledNavigationKeys: { 28 | escape: true, 29 | arrowRight: true, 30 | arrowLeft: true 31 | }, 32 | debug: false, 33 | theme: '#30BF83', 34 | onlyTips: false 35 | } 36 | 37 | export const HIGHLIGHT = { 38 | classes: { 39 | active: 'v-tour--active', 40 | targetHighlighted: 'v-tour__target--highlighted', 41 | targetRelative: 'v-tour__target--relative' 42 | }, 43 | transition: 'box-shadow 0s ease-in-out 0s' 44 | } 45 | 46 | export const DEFAULT_STEP_OPTIONS = { 47 | enableScrolling: true, 48 | highlight: DEFAULT_OPTIONS.highlight, // By default use the global tour setting 49 | enabledButtons: DEFAULT_OPTIONS.enabledButtons, 50 | modifiers: [ 51 | { 52 | name: 'arrow', 53 | options: { 54 | element: '.v-step__arrow', 55 | padding: 10 56 | } 57 | }, 58 | { 59 | name: 'preventOverflow', 60 | options: { 61 | rootBoundary: 'window' 62 | } 63 | }, 64 | { 65 | name: 'offset', 66 | options: { 67 | offset: [0, 10] 68 | } 69 | } 70 | ], 71 | placement: 'bottom' 72 | } 73 | 74 | export const KEYS = { 75 | ARROW_RIGHT: 39, 76 | ARROW_LEFT: 37, 77 | ESCAPE: 27 78 | } 79 | -------------------------------------------------------------------------------- /dist/ym-vue-tour.css: -------------------------------------------------------------------------------- 1 | body.v-tour--active{pointer-events:none}.v-tour{pointer-events:auto}.v-tour__target--highlighted{-webkit-box-shadow:0 0 0 4px rgba(0,0,0,.4);box-shadow:0 0 0 4px rgba(0,0,0,.4);pointer-events:auto;z-index:9999}.v-tour__target--relative{position:relative}.v-step[data-v-8142a986]{background:var(--theme);color:#fff;max-width:320px;border-radius:4px;-webkit-box-shadow:0 4px 8px rgba(33,35,41,.08);box-shadow:0 4px 8px rgba(33,35,41,.08);pointer-events:auto;text-align:center;z-index:10000}.v-step--sticky[data-v-8142a986]{position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.v-step--sticky .v-step__arrow[data-v-8142a986]{display:none}.v-step__arrow[data-v-8142a986],.v-step__arrow[data-v-8142a986]:before{position:absolute;width:10px;height:10px;background:inherit}.v-step__arrow[data-v-8142a986]{position:relative;visibility:hidden}.v-step__arrow--dark[data-v-8142a986]:before{background:var(--theme)}.spin[data-v-8142a986]{visibility:visible;width:16px;height:16px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border-radius:9999px;position:relative;-webkit-animation:spining-data-v-8142a986 1s infinite;animation:spining-data-v-8142a986 1s infinite}.spin .in[data-v-8142a986]{width:8px;height:8px;border-radius:9999px;background-color:var(--theme)}@-webkit-keyframes spining-data-v-8142a986{0%{width:8px;height:8px;margin-left:4px;margin-top:4px;background-color:var(--theme)}to{width:24px;height:24px;margin-left:-4px;margin-top:-4px;background-color:rgba(136,204,0,0)}}@keyframes spining-data-v-8142a986{0%{width:8px;height:8px;margin-left:4px;margin-top:4px;background-color:var(--theme)}to{width:24px;height:24px;margin-left:-4px;margin-top:-4px;background-color:rgba(136,204,0,0)}}.v-step__arrow[data-v-8142a986]:before{visibility:visible;content:"";-webkit-transform:rotate(45deg);transform:rotate(45deg);margin-left:-5px}.v-step[data-popper-placement^=top]>.v-step__arrow[data-v-8142a986]{bottom:-5px}.v-step[data-popper-placement^=top]>.v-step__arrow .spin[data-v-8142a986]{bottom:-20px;left:-3px}.v-step[data-popper-placement^=bottom]>.v-step__arrow[data-v-8142a986]{top:-5px}.v-step[data-popper-placement^=bottom]>.v-step__arrow .spin[data-v-8142a986]{top:-20px;left:-3px}.v-step[data-popper-placement^=right]>.v-step__arrow[data-v-8142a986]{left:-5px}.v-step[data-popper-placement^=right]>.v-step__arrow .spin[data-v-8142a986]{left:-20px;top:-3px}.v-step[data-popper-placement^=left]>.v-step__arrow[data-v-8142a986]{right:-5px}.v-step[data-popper-placement^=left]>.v-step__arrow .spin[data-v-8142a986]{right:-20px;top:-3px}.v-step__header[data-v-8142a986]{background-color:var(--theme);text-align:left;font-size:16px;font-weight:600;border-top-left-radius:4px;border-top-right-radius:4px}.v-step__content[data-v-8142a986]{font-size:14px;text-align:left}.v-step__buttons[data-v-8142a986]{text-align:right}.v-step__button[data-v-8142a986]{background:#fff;border-radius:4px;color:var(--theme);cursor:pointer;display:inline-block;font-size:12px;height:28px;line-height:20px;outline:none;text-align:right;text-decoration:none;-webkit-transition:all .2s ease;transition:all .2s ease;vertical-align:middle;white-space:nowrap;border:none;padding:0 14px}.v-step__button[data-v-8142a986]:hover{background-color:hsla(0,0%,100%,.9);color:var(--theme)}.v-step__button[data-v-8142a986]:not(:last-child){margin-right:8px} -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Javascript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | 7 | defaults: &defaults 8 | docker: 9 | - image: circleci/node:10.16.3 10 | working_directory: ~/vue-tour 11 | 12 | jobs: 13 | build: 14 | <<: *defaults 15 | steps: 16 | - checkout 17 | 18 | # install Cypress dependencies 19 | - run: sudo apt-get update && sudo apt-get install -y libgtk2.0-0 libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 xvfb 20 | 21 | # Download and cache dependencies 22 | - restore_cache: 23 | keys: 24 | - v1-dependencies-{{ checksum "package.json" }} 25 | # fallback to using the latest cache if no exact match is found 26 | - v1-dependencies- 27 | 28 | - run: npm ci 29 | 30 | - save_cache: 31 | paths: 32 | - node_modules 33 | key: v1-dependencies-{{ checksum "package.json" }} 34 | 35 | # run unit tests! 36 | - run: npm run test:unit 37 | - run: npm run build 38 | 39 | - run: git worktree add ../vue-tour-landing origin/landing 40 | 41 | # Download and cache dependencies 42 | # - restore_cache: 43 | # keys: 44 | # - v1-dependencies-{{ checksum "../vue-tour-landing/package.json" }} 45 | # # fallback to using the latest cache if no exact match is found 46 | # - v1-dependencies- 47 | 48 | - run: 49 | command: npm ci 50 | working_directory: ~/vue-tour-landing 51 | 52 | # - save_cache: 53 | # paths: 54 | # - ../vue-tour-landing/node_modules 55 | # - /home/circleci/.cache/Cypress 56 | # key: v1-dependencies-{{ checksum "../vue-tour-landing/package.json" }} 57 | 58 | # run e2e tests! 59 | - run: 60 | command: npm run test:e2e -- --headless 61 | working_directory: ~/vue-tour-landing 62 | 63 | - persist_to_workspace: 64 | root: ~/ 65 | paths: 66 | - vue-tour 67 | - vue-tour-landing 68 | 69 | publish: 70 | <<: *defaults 71 | steps: 72 | - attach_workspace: 73 | at: ~/ 74 | 75 | - add_ssh_keys: 76 | fingerprints: 77 | - "eb:09:ae:75:b5:8e:66:63:27:9a:c0:cb:64:6e:79:d5" 78 | 79 | # generate a new landing build and commit it to the gh-pages branch 80 | - run: 81 | command: npm run build 82 | working_directory: ~/vue-tour-landing 83 | 84 | - run: 85 | command: cp -R ./dist ../ 86 | working_directory: ~/vue-tour-landing 87 | 88 | - run: 89 | command: | 90 | ssh-keyscan github.com >> ~/.ssh/known_hosts 91 | git checkout --track origin/gh-pages 92 | rm -rf * 93 | cp -R ../dist/. ./ 94 | git config user.email "mathieumorainville@hotmail.com" 95 | git config user.name "Mathieu Morainville" 96 | git status 97 | git add . 98 | git commit -m "chore(ci): generate a new build" 99 | git push origin gh-pages 100 | working_directory: ~/vue-tour-landing 101 | 102 | # we could use standard-version here to generating a changelog, bump the version with a tag and commit it 103 | 104 | - run: 105 | name: Authenticate with registry 106 | command: echo "//registry.npmjs.org/:_authToken=$NPM_TOKEN" > .npmrc 107 | when: always 108 | 109 | - run: 110 | name: Publish package 111 | command: cat .npmrc 112 | # command: npm publish 113 | 114 | # use workflows to publish only on tagged commits (see: https://circleci.com/blog/publishing-npm-packages-using-circleci-2-0/) 115 | workflows: 116 | version: 2 117 | build-publish: 118 | jobs: 119 | - build: 120 | filters: 121 | branches: 122 | only: 123 | - master 124 | - staging 125 | - publish: 126 | requires: 127 | - build 128 | filters: 129 | tags: 130 | only: /^v.*/ 131 | branches: 132 | only: 133 | - master 134 | -------------------------------------------------------------------------------- /tests/unit/VTour.spec.js: -------------------------------------------------------------------------------- 1 | import { mount, shallowMount } from '@vue/test-utils' 2 | import Vue from 'vue' 3 | import VueTour from '@/main' 4 | import VTour from '@/components/VTour.vue' 5 | 6 | Vue.use(VueTour) 7 | 8 | const steps = [ 9 | { 10 | target: '#v-step-0', 11 | content: `Discover Vue Tour!` 12 | }, 13 | { 14 | target: '#v-step-1', 15 | content: 'An awesome plugin made with Vue.js!' 16 | } 17 | ] 18 | 19 | describe('VTour.vue', () => { 20 | it('has the correct number of steps', () => { 21 | const wrapper = mount(VTour, { 22 | propsData: { 23 | name: 'myTestTour', 24 | steps 25 | } 26 | }) 27 | 28 | expect(wrapper.vm.steps.length).toEqual(2) 29 | }) 30 | 31 | it('registers itself in the global Vue instance', () => { 32 | const wrapper = mount(VTour, { 33 | propsData: { 34 | name: 'myTestTour', 35 | steps: [] 36 | } 37 | }) 38 | 39 | expect(typeof wrapper.vm.$tours).toBe('object') 40 | expect(wrapper.vm.$tours).toHaveProperty('myTestTour') 41 | }) 42 | 43 | it('stays within the boundaries of the number of steps', async () => { 44 | const wrapper = shallowMount(VTour, { 45 | propsData: { 46 | name: 'myTestTour', 47 | steps 48 | } 49 | }) 50 | 51 | expect(wrapper.vm.currentStep).toEqual(-1) 52 | 53 | await wrapper.vm.start() 54 | expect(wrapper.vm.currentStep).toEqual(0) 55 | 56 | // We call nextStep one more time than needed 57 | for (let i = 0; i < steps.length; i++) { 58 | await wrapper.vm.nextStep() 59 | } 60 | 61 | expect(wrapper.vm.currentStep).toEqual(1) 62 | 63 | // We call previousStep one more time than needed 64 | for (let i = 0; i < steps.length; i++) { 65 | await wrapper.vm.previousStep() 66 | } 67 | 68 | expect(wrapper.vm.currentStep).toEqual(0) 69 | 70 | wrapper.vm.stop() 71 | 72 | expect(wrapper.vm.currentStep).toEqual(-1) 73 | }) 74 | 75 | describe('#before', () => { 76 | let step0 = false 77 | let step1 = false 78 | const beforeSteps = [ 79 | { 80 | target: '#v-step-0', 81 | content: `Discover Vue Tour!`, 82 | before: () => { 83 | step0 = true 84 | return Promise.resolve() 85 | } 86 | }, 87 | { 88 | target: '#v-step-1', 89 | content: 'An awesome plugin made with Vue.js!', 90 | before: () => { 91 | step1 = true 92 | return Promise.resolve() 93 | } 94 | }, 95 | { 96 | target: '#v-step-2', 97 | content: 'An awesome plugin made with Vue.js!', 98 | before: () => { 99 | return Promise.reject(new Error('testing')) 100 | } 101 | }, 102 | { 103 | target: '#v-step-3', 104 | content: 'An awesome plugin made with Vue.js!', 105 | before: () => { 106 | return Promise.resolve() 107 | } 108 | } 109 | ] 110 | 111 | it('invokes before() on start()', async () => { 112 | const wrapper = shallowMount(VTour, { 113 | propsData: { 114 | name: 'myTestTour', 115 | steps: beforeSteps 116 | } 117 | }) 118 | 119 | await wrapper.vm.start() 120 | expect(wrapper.vm.currentStep).toEqual(0) 121 | expect(step0).toEqual(true) 122 | 123 | step0 = false 124 | step1 = false 125 | }) 126 | 127 | it('invokes before() on nextStep()', async () => { 128 | const wrapper = shallowMount(VTour, { 129 | propsData: { 130 | name: 'myTestTour', 131 | steps: beforeSteps 132 | } 133 | }) 134 | 135 | await wrapper.vm.start() 136 | expect(wrapper.vm.currentStep).toEqual(0) 137 | 138 | await wrapper.vm.nextStep() 139 | expect(wrapper.vm.currentStep).toEqual(1) 140 | expect(step1).toEqual(true) 141 | 142 | step0 = false 143 | step1 = false 144 | }) 145 | 146 | it('handles before() promise rejection on start', async () => { 147 | const wrapper = mount(VTour, { 148 | propsData: { 149 | name: 'myTestTour', 150 | steps: beforeSteps 151 | } 152 | }) 153 | 154 | try { 155 | await wrapper.vm.start(2) 156 | expect(true).toEqual(false) // dead code 157 | } catch (e) { 158 | expect(e.message).toEqual('testing') 159 | expect(wrapper.vm.currentStep).toEqual(-1) 160 | } 161 | }) 162 | 163 | it('handles before() promise rejection on nextStep()', async () => { 164 | const wrapper = shallowMount(VTour, { 165 | propsData: { 166 | name: 'myTestTour', 167 | steps: beforeSteps 168 | } 169 | }) 170 | 171 | await wrapper.vm.start(1) 172 | 173 | try { 174 | await wrapper.vm.nextStep() 175 | expect(true).toEqual(false) // dead code 176 | } catch (e) { 177 | expect(e.message).toEqual('testing') 178 | expect(wrapper.vm.currentStep).toEqual(1) 179 | } 180 | }) 181 | 182 | it('handles before() promise rejection on previousStep()', async () => { 183 | const wrapper = shallowMount(VTour, { 184 | propsData: { 185 | name: 'myTestTour', 186 | steps: beforeSteps 187 | } 188 | }) 189 | 190 | await wrapper.vm.start(3) 191 | 192 | try { 193 | await wrapper.vm.previousStep() 194 | expect(true).toEqual(false) // dead code 195 | } catch (e) { 196 | expect(e.message).toEqual('testing') 197 | expect(wrapper.vm.currentStep).toEqual(3) 198 | } 199 | }) 200 | }) 201 | }) 202 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Table of Contents 4 | 5 | - [Developing Vue Tour](#developing-vue-tour) 6 | - [Generating a Changelog](#generating-a-changelog) 7 | - [Git Commit Guidelines](#git-commit-guidelines) 8 | 9 | ## Developing Vue Tour 10 | 11 | Vue Tour is a library which means that in order to be tested it has to be used in a project. 12 | For this you have two options: 13 | - Use `public/index.html` but then you're limited to a built version 14 | - Use the `landing` branch as a git worktree: 15 | ``` 16 | git worktree add ../vue-tour-landing landing 17 | ``` 18 | 19 | ### Pull Requests 20 | 21 | All pull request must target `staging`. 22 | 23 | 24 | ### Merging PRs 25 | 26 | If a PR has a lot of conflicts and you want to make sure it's working or you want to cherry-pick some commits, you can checkout the PR branch locally: 27 | ``` 28 | git fetch origin pull/:ID/head:pr/:ID 29 | ``` 30 | Where `:ID` is the ID of the PR. The previous command will create a new branch `pr/:ID` containing the changes and commits of the PR. 31 | 32 | ### New release 33 | 34 | Go on `staging` branch. 35 | 36 | ``` 37 | git checkout staging 38 | ``` 39 | 40 | Check result of Standard Version. 41 | 42 | ``` 43 | standard-version --dry-run 44 | ``` 45 | 46 | For a better control of the version number, use `--release-as`. corresponds to semver levels: major, minor or patch. 47 | 48 | ``` 49 | standard-version --release-as --dry-run 50 | ``` 51 | 52 | If result is ok, run command without `--dry-run` flag. 53 | 54 | ``` 55 | standard-version --release-as 56 | ``` 57 | 58 | Push version on `staging`. 59 | 60 | ``` 61 | git push --follow-tags origin staging 62 | ``` 63 | 64 | Do a Pull Request from `staging` to `master`. 65 | 66 | Once merged, publish on NPM from `master`. 67 | 68 | ``` 69 | git checkout master 70 | npm publish 71 | ``` 72 | 73 | ## Generating a Changelog 74 | 75 | By using "standard" guidelines we are able to automatically generate a changelog from our git commit messages. 76 | The tool used to do that is [standard-version](https://github.com/conventional-changelog/standard-version). 77 | 78 | Here is an excerpt from their documentation. 79 | 80 | ### First Release 81 | 82 | To generate your changelog for your first release, simply do: 83 | 84 | ```sh 85 | # npm run script 86 | npm run release -- --first-release 87 | # or global bin 88 | standard-version --first-release 89 | ``` 90 | 91 | This will tag a release **without bumping the version in package.json (_et al._)**. 92 | 93 | When ready, push the git tag and `npm publish` your first release. \o/ 94 | 95 | ### Release as a pre-release 96 | 97 | Use the flag `--prerelease` to generate pre-releases: 98 | 99 | Suppose the last version of your code is `1.0.0`, and your code to be committed has patched changes. Run: 100 | 101 | ```bash 102 | # npm run script 103 | npm run release -- --prerelease 104 | ``` 105 | you will get version `1.0.1-0`. 106 | 107 | If you want to name the pre-release, you specify the name via `--prerelease `. 108 | 109 | For example, suppose your pre-release should contain the `alpha` prefix: 110 | 111 | ```bash 112 | # npm run script 113 | npm run release -- --prerelease alpha 114 | ``` 115 | 116 | this will tag the version `1.0.1-alpha.0` 117 | 118 | ### Release as a target type imperatively like `npm version` 119 | 120 | To forgo the automated version bump use `--release-as` with the argument `major`, `minor` or `patch`: 121 | 122 | Suppose the last version of your code is `1.0.0`, you've only landed `fix:` commits, but 123 | you would like your next release to be a `minor`. Simply do: 124 | 125 | ```bash 126 | # npm run script 127 | npm run release -- --release-as minor 128 | # Or 129 | npm run release -- --release-as 1.1.0 130 | ``` 131 | 132 | you will get version `1.1.0` rather than the auto generated version `1.0.1`. 133 | 134 | > **NOTE:** you can combine `--release-as` and `--prerelease` to generate a release. This is useful when publishing experimental feature(s). 135 | 136 | ## Git Commit Guidelines 137 | 138 | We use the Git Commit Guidelines that Google uses for AngularJS to format our git commit messages. Here is an excerpt of those guidelines. 139 | 140 | We have very precise rules over how our git commit messages can be formatted. This leads to **more readable messages** that are easy to follow when looking through the **project history**. 141 | 142 | ### Commit Message Format 143 | Each commit message consists of a **header**, a **body** and a **footer**. The header has a special 144 | format that includes a **type**, a **scope** and a **subject**: 145 | 146 | ``` 147 | (): 148 | 149 | 150 | 151 |