├── commitlint.config.js ├── .huskyrc ├── tea.yaml ├── packages ├── vue-composition-test-utils │ ├── .npmignore │ ├── babel.config.js │ ├── jest.config.js │ ├── src │ │ ├── __tests__ │ │ │ ├── watch.test.ts │ │ │ └── simple.test.ts │ │ └── index.ts │ ├── tsconfig.json │ ├── build │ │ ├── configs.js │ │ └── build.js │ ├── package.json │ └── README.md ├── test-vue2 │ ├── jest.config.js │ ├── package.json │ └── test │ │ ├── watch.test.js │ │ └── simple.test.js └── test-vue3 │ ├── jest.config.js │ ├── package.json │ └── test │ ├── watch.test.js │ └── simple.test.js ├── .gitignore ├── package.json ├── .github └── workflows │ └── nodejs.yml ├── LICENSE └── README.md /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = {extends: ['@commitlint/config-conventional']} 2 | -------------------------------------------------------------------------------- /.huskyrc: -------------------------------------------------------------------------------- 1 | { 2 | "hooks": { 3 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS", 4 | "pre-push": "npm test" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x2632C2229C2Ee8ad065aF7D739483C0383dc3f38' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | TODO.md 4 | *.log 5 | .idea 6 | src 7 | .babelrc 8 | .huskyrc 9 | .travis.yml 10 | commitlint.config.js 11 | .storybook 12 | test 13 | *.tgz 14 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | [ 4 | "@babel/env", 5 | { 6 | spec: true, 7 | useBuiltIns: false 8 | } 9 | ], 10 | "@babel/preset-typescript", 11 | "@vue/babel-preset-app" 12 | ], 13 | plugins: [ 14 | ] 15 | }; 16 | -------------------------------------------------------------------------------- /packages/test-vue2/jest.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | process.env.NODE_ENV = "test"; 4 | 5 | module.exports = { 6 | rootDir: path.resolve(__dirname), 7 | roots: [""], 8 | moduleFileExtensions: ["js", "jsx", "json"], 9 | moduleNameMapper: { 10 | }, 11 | testRegex: "(/test/.*|(\\.|/)(test|spec))\\.js?$", 12 | transform: { 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /packages/test-vue3/jest.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | process.env.NODE_ENV = "test"; 4 | 5 | module.exports = { 6 | rootDir: path.resolve(__dirname), 7 | roots: [""], 8 | moduleFileExtensions: ["js", "jsx", "json"], 9 | moduleNameMapper: { 10 | }, 11 | testRegex: "(/test/.*|(\\.|/)(test|spec))\\.js?$", 12 | transform: { 13 | }, 14 | }; 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | .idea 8 | 9 | # dist 10 | lib 11 | dist 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | *.tgz 19 | .pnp.js 20 | 21 | lib-cov 22 | */**/test/unit/coverage/ 23 | test-report.xml 24 | 25 | node_modules/ 26 | .yarn-integrity 27 | .DS_Store 28 | .cache/ 29 | .puppet-master/ 30 | storybook-static/ 31 | -------------------------------------------------------------------------------- /packages/test-vue3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-vue3", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm install ../vue-composition-test-utils/vue-composition-test-utils-0.1.0.tgz --no-save && jest" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | }, 13 | "devDependencies": { 14 | "@vue/test-utils": "^2.0.0-beta.7", 15 | "vue": "3.0.2", 16 | "vue-composition-test-utils": "*", 17 | "vue-demi": "^0.4.1", 18 | "vue-template-compiler": "^2.6.12" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/test-vue2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "test-vue2", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm install ../vue-composition-test-utils/vue-composition-test-utils-0.1.0.tgz --no-save && jest" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | }, 13 | "devDependencies": { 14 | "@vue/composition-api": "1.0.0-beta.18", 15 | "@vue/test-utils": "^1.1.0", 16 | "vue": "2.6.12", 17 | "vue-composition-test-utils": "*", 18 | "vue-demi": "^0.4.1", 19 | "vue-template-compiler": "^2.6.12" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/jest.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | process.env.NODE_ENV = "test"; 4 | 5 | module.exports = { 6 | rootDir: path.resolve(__dirname), 7 | roots: ["/src"], 8 | moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json"], 9 | moduleNameMapper: { 10 | }, 11 | testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.tsx?$", 12 | transform: { 13 | "^.+\\.tsx?$": "ts-jest" 14 | }, 15 | collectCoverage: false, 16 | coverageReporters: ["html", "text-summary", "lcov"], 17 | coverageDirectory: "/test/unit/coverage", 18 | collectCoverageFrom: ["src/**/*.{ts, js}"] 19 | }; 20 | -------------------------------------------------------------------------------- /packages/test-vue3/test/watch.test.js: -------------------------------------------------------------------------------- 1 | const { ref, watch } = require('vue') 2 | const {mountComposition, nextTick} = require('vue-composition-test-utils') 3 | 4 | function useCounter(changed, initialValue = 0, ) { 5 | const count = ref(initialValue) 6 | const inc = (delta = 1) => (count.value += delta) 7 | watch( 8 | count, 9 | changed 10 | ) 11 | return { count, inc } 12 | } 13 | 14 | test('should trigger watch when watch source changed', async function() { 15 | const fn = jest.fn() 16 | const wrapper = mountComposition(()=>useCounter(fn)) 17 | expect(fn).not.toHaveBeenCalled() 18 | await nextTick(() => { 19 | wrapper.result.current.inc() 20 | }) 21 | expect(fn).toHaveBeenCalled() 22 | }); 23 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/src/__tests__/watch.test.ts: -------------------------------------------------------------------------------- 1 | import { mountComposition, nextTick } from "../index"; 2 | import { ref, watch } from 'vue' 3 | 4 | function useCounter(changed, initialValue = 0, ) { 5 | const count = ref(initialValue) 6 | const inc = (delta = 1) => (count.value += delta) 7 | watch( 8 | count, 9 | changed 10 | ) 11 | return { count, inc } 12 | } 13 | 14 | test('should trigger watch when watch source changed', async function() { 15 | const fn = jest.fn() 16 | const wrapper = mountComposition(()=>useCounter(fn)) 17 | expect(fn).not.toHaveBeenCalled() 18 | await nextTick(() => { 19 | wrapper.result.current.inc() 20 | }) 21 | expect(fn).toHaveBeenCalled() 22 | }); 23 | -------------------------------------------------------------------------------- /packages/test-vue2/test/watch.test.js: -------------------------------------------------------------------------------- 1 | const { ref, watch } = require('@vue/composition-api') 2 | const {mountComposition, nextTick} = require('vue-composition-test-utils') 3 | 4 | function useCounter(changed, initialValue = 0, ) { 5 | const count = ref(initialValue) 6 | const inc = (delta = 1) => (count.value += delta) 7 | watch( 8 | count, 9 | changed 10 | ) 11 | return { count, inc } 12 | } 13 | 14 | test('should trigger watch when watch source changed', async function() { 15 | const fn = jest.fn() 16 | const wrapper = mountComposition(()=>useCounter(fn)) 17 | expect(fn).not.toHaveBeenCalled() 18 | await nextTick(() => { 19 | wrapper.result.current.inc() 20 | }) 21 | expect(fn).toHaveBeenCalled() 22 | }); 23 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2015", 4 | "lib": ["ESNext", "dom"], 5 | "module": "ES2015", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "pretty": true, 9 | "rootDir": "src", 10 | "esModuleInterop": true, 11 | "sourceMap": false, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noImplicitAny": false, 16 | "noFallthroughCasesInSwitch": true, 17 | "outDir": "lib", 18 | "types": ["jest", "node"] 19 | }, 20 | "exclude": [ 21 | "node_modules", 22 | "lib", 23 | "**/__tests__/**/*", 24 | "**/__stories__/**/*", 25 | "*.test.ts", 26 | "*.test.tsx" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "yarn workspace vue-composition-test-utils run build && cd packages/vue-composition-test-utils && npm pack", 5 | "test": "yarn workspaces run test", 6 | "commit": "npx git-cz" 7 | }, 8 | "workspaces": { 9 | "packages": [ 10 | "packages/vue-composition-test-utils", 11 | "packages/test-vue2", 12 | "packages/test-vue3" 13 | ], 14 | "nohoist": [ 15 | "**/vue", 16 | "**/vue-demi", 17 | "**/@vue/test-utils", 18 | "**/vue-template-compiler" 19 | ] 20 | }, 21 | "author": "ariesjia", 22 | "license": "MIT", 23 | "devDependencies": { 24 | "commitlint": "^8.3.5", 25 | "husky": "^4.2.1" 26 | }, 27 | "peerDependencies": { 28 | }, 29 | "dependencies": { 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /.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: [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 workspace vue-composition-test-utils install 23 | yarn build 24 | yarn 25 | yarn test 26 | env: 27 | CI: true 28 | - name: Release 29 | id: semantic 30 | run: | 31 | cd packages/vue-composition-test-utils 32 | npx semantic-release@16 33 | env: 34 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 35 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Chenjia 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 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | import { mount } from '@vue/test-utils' 2 | // @ts-ignore 3 | import { nextTick as tick } from 'vue-demi' 4 | 5 | interface MountingOptions { 6 | data?: () => {} extends Data ? any : Data extends object ? Partial : any; 7 | props?: Props; 8 | attrs?: Record; 9 | slots?: any; 10 | global?: any; 11 | attachTo?: HTMLElement | string; 12 | shallow?: boolean; 13 | component?: any; 14 | localVue?: any; 15 | } 16 | 17 | interface MountingResult { 18 | current: R | null, 19 | error: Error | null 20 | } 21 | 22 | export const mountComposition = (callback: () => R, options: MountingOptions = {}) => { 23 | let result: MountingResult 24 | const { component = {}, ...other }= options 25 | const Wrap = { 26 | template: '
', 27 | ...component, 28 | setup() { 29 | try { 30 | result = { 31 | current: callback(), 32 | error: null 33 | } 34 | }catch (e) { 35 | result = { 36 | current: null, 37 | error: e 38 | } 39 | } 40 | return { 41 | result , 42 | } 43 | } 44 | } 45 | 46 | const vueWrapper = mount(Wrap, other); 47 | 48 | return Object.assign(vueWrapper, {result}) 49 | } 50 | 51 | export const nextTick = async (callback) => { 52 | callback && callback() 53 | await tick(); 54 | } 55 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/build/configs.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const typescript = require('rollup-plugin-typescript2') 3 | const commonjs = require('rollup-plugin-commonjs') 4 | const { uglify } = require('rollup-plugin-uglify') 5 | const pkg = require('../package.json') 6 | 7 | const resolve = _path => path.resolve(__dirname, '../', _path) 8 | 9 | const configs = { 10 | es: { 11 | input: resolve('src/index.ts'), 12 | file: pkg.main, 13 | format: 'cjs', 14 | env: 'production' 15 | }, 16 | } 17 | 18 | const compressPlugins = { 19 | es: uglify 20 | } 21 | 22 | function genConfig (opts) { 23 | const config = { 24 | input: { 25 | input: opts.input, 26 | plugins: [ 27 | commonjs({ 28 | include: 'node_modules/**', 29 | }), 30 | typescript({ 31 | }), 32 | ], 33 | external: [ 34 | '@vue/test-utils' 35 | ], 36 | }, 37 | output: { 38 | file: opts.file, 39 | format: opts.format, 40 | exports: 'named', 41 | } 42 | } 43 | const method = compressPlugins[opts.format] 44 | if ( opts.env === 'production' && method ) { 45 | config.input.plugins.push( 46 | method() 47 | ) 48 | } 49 | return config 50 | } 51 | 52 | function mapValues (obj, fn) { 53 | const res = {} 54 | Object.keys(obj).forEach(key => { 55 | res[key] = fn(obj[key], key) 56 | }) 57 | return res 58 | } 59 | 60 | module.exports = mapValues(configs, genConfig) 61 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/src/__tests__/simple.test.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue' 2 | import { mountComposition, nextTick } from "../index"; 3 | 4 | function useCounter(initialValue = 0) { 5 | const count = ref(initialValue) 6 | const inc = (delta = 1) => (count.value += delta) 7 | return { count, inc } 8 | } 9 | 10 | test('should get current composition result', function() { 11 | const wrapper = mountComposition(useCounter) 12 | expect(wrapper.result.current.count.value).toEqual(0) 13 | }); 14 | 15 | test('should get current value when trigger method', function() { 16 | const wrapper = mountComposition(()=>useCounter(1)) 17 | expect(wrapper.result.current.count.value).toEqual(1) 18 | wrapper.result.current.inc() 19 | expect(wrapper.result.current.count.value).toEqual(2) 20 | }); 21 | 22 | test('should render template though template option', async function() { 23 | const wrapper = mountComposition(useCounter, { 24 | component: { 25 | template: 'hello world {{result.current.count.value}}', 26 | } 27 | }) 28 | expect(wrapper.html()).toEqual('hello world 0') 29 | await nextTick(() => { 30 | wrapper.result.current.inc() 31 | }) 32 | expect(wrapper.html()).toEqual('hello world 1') 33 | }); 34 | 35 | test('should render template though render option', async function() { 36 | const wrapper = mountComposition(useCounter, { 37 | component: { 38 | render({ result}) { 39 | return `hello world ${result.current.count.value}` 40 | } 41 | } 42 | }) 43 | expect(wrapper.html()).toEqual('hello world 0') 44 | await nextTick(() => { 45 | wrapper.result.current.inc() 46 | }) 47 | expect(wrapper.html()).toEqual('hello world 1') 48 | }); 49 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/build/build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs') 2 | const path = require('path') 3 | const zlib = require('zlib') 4 | const rollup = require('rollup') 5 | const configs = require('./configs') 6 | 7 | if (!fs.existsSync('dist')) { 8 | fs.mkdirSync('dist') 9 | } 10 | 11 | build(Object.keys(configs).map(key => configs[key])) 12 | 13 | 14 | function build (builds) { 15 | let built = 0 16 | const total = builds.length 17 | const next = () => { 18 | buildEntry(builds[built]).then(() => { 19 | built++ 20 | if (built < total) { 21 | next() 22 | } 23 | }).catch(logError) 24 | } 25 | 26 | next() 27 | } 28 | 29 | 30 | async function buildEntry({ input, output }) { 31 | // create a bundle 32 | const bundle = await rollup.rollup(input) 33 | await bundle.write(output); 34 | const res = await bundle.generate(output) 35 | await write(output.file, res.output[0].code) 36 | } 37 | 38 | function write (dest, code, zip) { 39 | return new Promise((resolve, reject) => { 40 | function report (extra) { 41 | console.log(blue(path.relative(process.cwd(), dest)) + ' ' + getSize(code) + (extra || '')) 42 | resolve() 43 | } 44 | fs.writeFile(dest, code, err => { 45 | if (err) return reject(err) 46 | zlib.gzip(code, (err, zipped) => { 47 | if (err) return reject(err) 48 | report(' (gzipped: ' + getSize(zipped) + ')') 49 | }) 50 | }) 51 | }) 52 | } 53 | 54 | function getSize (code) { 55 | return (code.length / 1024).toFixed(2) + 'kb' 56 | } 57 | 58 | function logError (e) { 59 | console.log(e) 60 | } 61 | 62 | function blue (str) { 63 | return '\x1b[1m\x1b[34m' + str + '\x1b[39m\x1b[22m' 64 | } -------------------------------------------------------------------------------- /packages/test-vue3/test/simple.test.js: -------------------------------------------------------------------------------- 1 | const {ref} = require('vue') 2 | const {mountComposition, nextTick} = require('vue-composition-test-utils') 3 | 4 | function useCounter(initialValue = 0) { 5 | const count = ref(initialValue) 6 | const inc = (delta = 1) => (count.value += delta) 7 | return {count, inc} 8 | } 9 | 10 | test('should get current composition result', function () { 11 | const wrapper = mountComposition(useCounter) 12 | expect(wrapper.result.current.count.value).toEqual(0) 13 | }); 14 | 15 | test('should get current value when trigger method', function () { 16 | const wrapper = mountComposition(() => useCounter(1)) 17 | expect(wrapper.result.current.count.value).toEqual(1) 18 | wrapper.result.current.inc() 19 | expect(wrapper.result.current.count.value).toEqual(2) 20 | }); 21 | 22 | test('should render template though template option', async function () { 23 | const wrapper = mountComposition(useCounter, { 24 | component: { 25 | template: 'hello world {{result.current.count.value}}', 26 | } 27 | }) 28 | expect(wrapper.html()).toEqual('hello world 0') 29 | wrapper.result.current.inc() 30 | await nextTick() 31 | expect(wrapper.html()).toEqual('hello world 1') 32 | }); 33 | 34 | test('should render template though render option', async function () { 35 | const wrapper = mountComposition(useCounter, { 36 | component: { 37 | render() { 38 | return `hello world ${this.result.current.count.value}` 39 | } 40 | } 41 | }) 42 | expect(wrapper.html()).toEqual('hello world 0') 43 | await nextTick(() => { 44 | wrapper.result.current.inc() 45 | }) 46 | expect(wrapper.html()).toEqual('hello world 1') 47 | }); 48 | -------------------------------------------------------------------------------- /packages/test-vue2/test/simple.test.js: -------------------------------------------------------------------------------- 1 | const {ref} = require('@vue/composition-api') 2 | const {createLocalVue} = require('@vue/test-utils') 3 | const {mountComposition, nextTick} = require('vue-composition-test-utils') 4 | 5 | function useCounter(initialValue = 0) { 6 | const count = ref(initialValue) 7 | const inc = (delta = 1) => (count.value += delta) 8 | return { count, inc } 9 | } 10 | 11 | test('should get current composition result', function() { 12 | const localVue =createLocalVue() 13 | const wrapper = mountComposition(useCounter, {localVue}) 14 | expect(wrapper.result.current.count.value).toEqual(0) 15 | }); 16 | 17 | test('should get current value when trigger method', function() { 18 | const wrapper = mountComposition(()=>useCounter(1)) 19 | expect(wrapper.result.current.count.value).toEqual(1) 20 | wrapper.result.current.inc() 21 | expect(wrapper.result.current.count.value).toEqual(2) 22 | }); 23 | 24 | test('should render template though template option', async function() { 25 | const wrapper = mountComposition(useCounter, { 26 | component: { 27 | template: 'hello world {{result.current.count.value}}', 28 | } 29 | }) 30 | expect(wrapper.html()).toEqual('hello world 0') 31 | wrapper.result.current.inc() 32 | await nextTick() 33 | expect(wrapper.html()).toEqual('hello world 1') 34 | }); 35 | 36 | test('should render template though render option', async function() { 37 | const wrapper = mountComposition(useCounter, { 38 | component: { 39 | render(h) { 40 | return h('span', {}, [ 41 | `hello world ${this.result.current.count.value}` 42 | ]) 43 | } 44 | } 45 | }) 46 | expect(wrapper.html()).toEqual('hello world 0') 47 | await nextTick(() => { 48 | wrapper.result.current.inc() 49 | }) 50 | expect(wrapper.html()).toEqual('hello world 1') 51 | }); 52 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-composition-test-utils", 3 | "version": "0.1.0", 4 | "description": "Simple vue composition api testing utilities", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "scripts": { 8 | "build": "node ./build/build.js", 9 | "commit": "npx git-cz", 10 | "test": "jest --config jest.config.js --no-cache" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/ariesjia/vue-composition-test-utils.git" 15 | }, 16 | "author": "", 17 | "license": "MIT", 18 | "bugs": { 19 | "url": "https://github.com/ariesjia/vue-composition-test-utils/issues" 20 | }, 21 | "homepage": "https://github.com/ariesjia/vue-composition-test-utils#readme", 22 | "devDependencies": { 23 | "@babel/core": "^7.7.7", 24 | "@babel/plugin-proposal-class-properties": "^7.7.4", 25 | "@babel/plugin-proposal-decorators": "^7.8.3", 26 | "@babel/plugin-proposal-object-rest-spread": "^7.7.7", 27 | "@babel/preset-env": "^7.7.7", 28 | "@babel/preset-typescript": "^7.7.7", 29 | "@commitlint/config-conventional": "^8.3.4", 30 | "@rollup/plugin-typescript": "^2.1.0", 31 | "@types/jest": "26.0.15", 32 | "@vue/babel-preset-app": "^4.5.7", 33 | "@vue/test-utils": "2.0.0-beta.7", 34 | "babel-core": "^7.0.0-bridge.0", 35 | "jest": "26.6.1", 36 | "rollup": "^1.23.1", 37 | "rollup-plugin-commonjs": "^10.1.0", 38 | "rollup-plugin-terser": "^5.1.2", 39 | "rollup-plugin-typescript2": "^0.24.3", 40 | "rollup-plugin-uglify": "^6.0.3", 41 | "ts-jest": "^26.4.1", 42 | "typescript": "3.9.7", 43 | "vue": "3.0.2", 44 | "@vue/composition-api": "^1.0.0-beta.18" 45 | }, 46 | "peerDependenciesMeta": { 47 | "@vue/composition-api": { 48 | "optional": true 49 | }, 50 | "@vue/test-utils": { 51 | "optional": false 52 | }, 53 | "vue": { 54 | "optional": false 55 | } 56 | }, 57 | "dependencies": { 58 | "vue-demi": "^0.4.0" 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-composition-test-utils 2 | Simple vue composition api testing utilities 3 | 4 | [![Actions Status](https://github.com/ariesjia/vue-composition-test-utils/workflows/Node%20CI/badge.svg)](https://github.com/ariesjia/vue-composition-test-utils/actions) 5 | [![NPM](https://img.shields.io/npm/v/vue-composition-test-utils.svg)](https://www.npmjs.com/package/vue-composition-test-utils) 6 | [![license](https://badgen.net/badge/license/MIT/blue)](https://github.com/ariesjia/vue-composition-test-utils/blob/master/LICENSE) 7 | 8 | 9 | ## Install 10 | ```bash 11 | // use yarn 12 | yarn add vue-composition-test-utils -D 13 | // use npm 14 | npm install vue-composition-test-utils -D 15 | ``` 16 | 17 | ## Demo 18 | 19 | #### Code 20 | 21 | ```js 22 | import { ref } from 'vue' 23 | 24 | export function useCounter(initialValue = 0) { 25 | const count = ref(initialValue) 26 | const inc = (delta = 1) => (count.value += delta) 27 | return { count, inc } 28 | } 29 | ``` 30 | 31 | #### Test 32 | 33 | ```js 34 | import { mountComposition, nextTick } from 'vue-composition-test-utils' 35 | 36 | test('should get current composition result', function() { 37 | const wrapper = mountComposition(useCounter) 38 | expect(wrapper.result.current.count.value).toEqual(0) 39 | }); 40 | 41 | test('should render template though template option', async function() { 42 | const wrapper = mountComposition(useCounter, { 43 | component: { 44 | template: 'hello world {{result.current.count.value}}', 45 | } 46 | }) 47 | expect(wrapper.html()).toEqual('hello world 0') 48 | await nextTick(() => { 49 | wrapper.result.current.inc() 50 | }) 51 | expect(wrapper.result.current.count.value).toEqual(1) 52 | expect(wrapper.html()).toEqual('hello world 1') 53 | }); 54 | 55 | ``` 56 | 57 | 58 | #### vue2 + @vue/composition-api demo 59 | [https://github.com/ariesjia/vue-composition-test-utils/blob/master/packages/test-vue2/test/simple.test.js](https://github.com/ariesjia/vue-composition-test-utils/blob/master/packages/test-vue2/test/simple.test.js) 60 | 61 | 62 | #### vue3 demo 63 | [https://github.com/ariesjia/vue-composition-test-utils/blob/master/packages/test-vue3/test/simple.test.js](https://github.com/ariesjia/vue-composition-test-utils/blob/master/packages/test-vue3/test/simple.test.js) 64 | 65 | ## API 66 | 67 | ```typescript 68 | import {GlobalMountOptions} from "@vue/test-utils/dist/types"; 69 | import {ComponentOptionsWithoutProps} from "vue"; 70 | 71 | interface MountingOptions { 72 | data?: () => {} extends Data ? any : Data extends object ? Partial : any; 73 | props?: Props; 74 | attrs?: Record; 75 | slots?: SlotDictionary & { 76 | default?: Slot; 77 | }; 78 | global?: GlobalMountOptions; 79 | attachTo?: HTMLElement | string; 80 | shallow?: boolean; 81 | component?: ComponentOptionsWithoutProps; 82 | } 83 | 84 | interface MountingResult { 85 | current: R | null; 86 | error: Error | null; 87 | } 88 | 89 | export declare const mountComposition: (callback: () => R, options?: MountingOptions) => import("@vue/test-utils").VueWrapper, import("vue").VNodeProps & Props, {}, false, import("vue").ComponentOptionsBase>> & { 90 | result: MountingResult; 91 | }; 92 | 93 | export const nextTick: (fn?: () => void) => Promise 94 | ``` 95 | 96 | 97 | ### Thanks 98 | 99 | This project is inspired by [vue-demi](https://github.com/antfu/vue-demi) 100 | 101 | -------------------------------------------------------------------------------- /packages/vue-composition-test-utils/README.md: -------------------------------------------------------------------------------- 1 | # vue-composition-test-utils 2 | Simple vue composition api testing utilities 3 | 4 | [![Actions Status](https://github.com/ariesjia/vue-composition-test-utils/workflows/Node%20CI/badge.svg)](https://github.com/ariesjia/vue-composition-test-utils/actions) 5 | [![NPM](https://img.shields.io/npm/v/vue-composition-test-utils.svg)](https://www.npmjs.com/package/vue-composition-test-utils) 6 | [![license](https://badgen.net/badge/license/MIT/blue)](https://github.com/ariesjia/vue-composition-test-utils/blob/master/LICENSE) 7 | 8 | 9 | ## Install 10 | ```bash 11 | // use yarn 12 | yarn add vue-composition-test-utils -D 13 | // use npm 14 | npm install vue-composition-test-utils -D 15 | ``` 16 | 17 | ## Demo 18 | 19 | #### Code 20 | 21 | ```js 22 | import { ref } from 'vue' 23 | 24 | export function useCounter(initialValue = 0) { 25 | const count = ref(initialValue) 26 | const inc = (delta = 1) => (count.value += delta) 27 | return { count, inc } 28 | } 29 | ``` 30 | 31 | #### Test 32 | 33 | ```js 34 | import { mountComposition, nextTick } from 'vue-composition-test-utils' 35 | 36 | test('should get current composition result', function() { 37 | const wrapper = mountComposition(useCounter) 38 | expect(wrapper.result.current.count.value).toEqual(0) 39 | }); 40 | 41 | test('should get current value when trigger method', function() { 42 | const wrapper = mountComposition(()=>useCounter(1)) 43 | expect(wrapper.result.current.count.value).toEqual(1) 44 | wrapper.result.current.inc() 45 | expect(wrapper.result.current.count.value).toEqual(2) 46 | }); 47 | 48 | test('should render template though template option', async function() { 49 | const wrapper = mountComposition(useCounter, { 50 | component: { 51 | template: 'hello world {{result.current.count.value}}', 52 | } 53 | }) 54 | expect(wrapper.html()).toEqual('hello world 0') 55 | await nextTick(() => { 56 | wrapper.result.current.inc() 57 | }) 58 | expect(wrapper.html()).toEqual('hello world 1') 59 | }); 60 | 61 | test('should render template though render option', async function() { 62 | const wrapper = mountComposition(useCounter, { 63 | component: { 64 | render({ result}) { 65 | return `hello world ${result.current.count.value}` 66 | } 67 | } 68 | }) 69 | expect(wrapper.html()).toEqual('hello world 0') 70 | await nextTick(() => { 71 | wrapper.result.current.inc() 72 | }) 73 | expect(wrapper.html()).toEqual('hello world 1') 74 | }); 75 | ``` 76 | 77 | ## API 78 | 79 | ```typescript 80 | import {GlobalMountOptions} from "@vue/test-utils/dist/types"; 81 | import {ComponentOptionsWithoutProps} from "vue"; 82 | 83 | interface MountingOptions { 84 | data?: () => {} extends Data ? any : Data extends object ? Partial : any; 85 | props?: Props; 86 | attrs?: Record; 87 | slots?: SlotDictionary & { 88 | default?: Slot; 89 | }; 90 | global?: GlobalMountOptions; 91 | attachTo?: HTMLElement | string; 92 | shallow?: boolean; 93 | component?: ComponentOptionsWithoutProps; 94 | } 95 | 96 | interface MountingResult { 97 | current: R | null; 98 | error: Error | null; 99 | } 100 | 101 | export declare const mountComposition: (callback: () => R, options?: MountingOptions) => import("@vue/test-utils").VueWrapper, import("vue").VNodeProps & Props, {}, false, import("vue").ComponentOptionsBase>> & { 102 | result: MountingResult; 103 | }; 104 | 105 | export const nextTick: (fn?: () => void) => Promise 106 | ``` 107 | --------------------------------------------------------------------------------