From c0201aed28a2a4a47ac267dae9e8eeca3d6fbdf4 Mon Sep 17 00:00:00 2001 From: Anthony Fu Date: Sun, 26 Dec 2021 13:16:12 +0800 Subject: [PATCH] feat: integrate vitest --- .eslintrc | 4 +- .github/workflows/test.yml | 3 + README.md | 5 +- package.json | 8 +- pnpm-lock.yaml | 98 +++++++++++++++++++++++ src/auto-imports.d.ts | 13 +++ src/components.d.ts | 8 +- src/components/Counter.vue | 19 +++++ test/__snapshots__/component.test.ts.snap | 3 + test/basic.test.ts | 5 ++ test/component.test.ts | 21 +++++ vite.config.ts | 10 +++ 12 files changed, 183 insertions(+), 14 deletions(-) create mode 100644 src/components/Counter.vue create mode 100644 test/__snapshots__/component.test.ts.snap create mode 100644 test/basic.test.ts create mode 100644 test/component.test.ts diff --git a/.eslintrc b/.eslintrc index 09dcda3..09c2e42 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,5 +1,3 @@ { - "extends": [ - "@antfu" - ] + "extends": "@antfu" } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d7fc2d2..5e3f413 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,6 +48,9 @@ jobs: - name: Type Check run: pnpm run typecheck + - name: Unit Test + run: pnpm run test:unit + - name: Cypress PNPM Patch run: cp pnpm-lock.yaml package-lock.json diff --git a/README.md b/README.md index b1483e3..be15fe2 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Mocking up web app with Vitesse(speed)
- 🦾 TypeScript, of course -- ⚙️ E2E Testing with [Cypress](https://cypress.io/) on [GitHub Actions](https://github.com/features/actions) +- ⚙️ Unit Testing with [Vitest](https://github.com/vitest-dev/vitest), E2E Testing with [Cypress](https://cypress.io/) on [GitHub Actions](https://github.com/features/actions) - ☁️ Deploy on Netlify, zero-config @@ -90,7 +90,8 @@ Mocking up web app with Vitesse(speed)
### Dev tools - [TypeScript](https://www.typescriptlang.org/) -- [Cypress](https://cypress.io/) - E2E Testing +- [Vitest](https://github.com/vitest-dev/vitest) - Unit testing powered by Vite +- [Cypress](https://cypress.io/) - E2E testing - [pnpm](https://pnpm.js.org/) - fast, disk space efficient package manager - [`vite-ssg`](https://github.com/antfu/vite-ssg) - Server-side generation - [critters](https://github.com/GoogleChromeLabs/critters) - Critical CSS diff --git a/package.json b/package.json index bef290a..c227a6b 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,14 @@ { "private": true, "scripts": { - "build": "cross-env NODE_ENV=production vite-ssg build", + "build": "vite-ssg build", "dev": "vite --port 3333 --open", "lint": "eslint \"**/*.{vue,ts,js}\"", - "test": "cypress open", "preview": "vite preview", "preview-https": "serve dist", + "test": "vitest", + "test:e2e": "cypress open", + "test:unit": "vitest", "typecheck": "vue-tsc --noEmit" }, "dependencies": { @@ -29,6 +31,7 @@ "@vitejs/plugin-vue": "^2.0.1", "@vue/compiler-sfc": "^3.2.26", "@vue/server-renderer": "^3.2.26", + "@vue/test-utils": "^2.0.0-rc.18", "critters": "^0.0.15", "cross-env": "^7.0.3", "cypress": "^9.1.1", @@ -50,6 +53,7 @@ "vite-plugin-vue-layouts": "^0.5.0", "vite-plugin-windicss": "^1.6.1", "vite-ssg": "^0.17.2", + "vitest": "^0.0.113", "vue-tsc": "^0.30.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19a79c3..39e72c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,6 +9,7 @@ specifiers: '@vitejs/plugin-vue': ^2.0.1 '@vue/compiler-sfc': ^3.2.26 '@vue/server-renderer': ^3.2.26 + '@vue/test-utils': ^2.0.0-rc.18 '@vueuse/core': ^7.4.1 '@vueuse/head': ^0.7.4 critters: ^0.0.15 @@ -35,6 +36,7 @@ specifiers: vite-plugin-vue-layouts: ^0.5.0 vite-plugin-windicss: ^1.6.1 vite-ssg: ^0.17.2 + vitest: ^0.0.113 vue: ^3.2.26 vue-demi: ^0.12.1 vue-i18n: ^9.1.9 @@ -61,6 +63,7 @@ devDependencies: '@vitejs/plugin-vue': 2.0.1_vite@2.7.6+vue@3.2.26 '@vue/compiler-sfc': 3.2.26 '@vue/server-renderer': 3.2.26_vue@3.2.26 + '@vue/test-utils': 2.0.0-rc.18_vue@3.2.26 critters: 0.0.15 cross-env: 7.0.3 cypress: 9.1.1 @@ -82,6 +85,7 @@ devDependencies: vite-plugin-vue-layouts: 0.5.0_8d12181306819baa2f4c8b15fe034bbb vite-plugin-windicss: 1.6.1_vite@2.7.6 vite-ssg: 0.17.2_0f81590d5699f4ef820dd76648bd0938 + vitest: 0.0.113_vite@2.7.6 vue-tsc: 0.30.0_typescript@4.5.4 packages: @@ -1643,6 +1647,16 @@ packages: engines: {node: '>= 10'} dev: true + /@types/chai-subset/1.3.3: + resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + dependencies: + '@types/chai': 4.3.0 + dev: true + + /@types/chai/4.3.0: + resolution: {integrity: sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==} + dev: true + /@types/estree/0.0.39: resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==} dev: true @@ -1994,6 +2008,14 @@ packages: /@vue/shared/3.2.26: resolution: {integrity: sha512-vPV6Cq+NIWbH5pZu+V+2QHE9y1qfuTq49uNWw4f7FDEeZaDU2H2cx5jcUZOAKW7qTrUS4k6qZPbMy1x4N96nbA==} + /@vue/test-utils/2.0.0-rc.18_vue@3.2.26: + resolution: {integrity: sha512-aifolXjVdsogjaLmDoZ0FU8vN+R67aWmg9OuVeED4w5Ij5GFQLrlhM19uhWe/r5xXUL4fXMk3pX5wW6FJP1NcQ==} + peerDependencies: + vue: ^3.0.1 + dependencies: + vue: 3.2.26 + dev: true + /@vueuse/core/7.4.1_vue@3.2.26: resolution: {integrity: sha512-8UeLPCAieeQLXFHF1/28SIEK6ILLPb/4hp03hR+xkXF00gB/YUp0CEVcRAL9uQ8HTZa3S2T/jTISMc1ZjilM0A==} peerDependencies: @@ -2258,6 +2280,10 @@ packages: engines: {node: '>=0.8'} dev: true + /assertion-error/1.1.0: + resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} + dev: true + /astral-regex/2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} engines: {node: '>=8'} @@ -2476,6 +2502,18 @@ packages: resolution: {integrity: sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=} dev: true + /chai/4.3.4: + resolution: {integrity: sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==} + engines: {node: '>=4'} + dependencies: + assertion-error: 1.1.0 + check-error: 1.0.2 + deep-eql: 3.0.1 + get-func-name: 2.0.0 + pathval: 1.1.1 + type-detect: 4.0.8 + dev: true + /chalk/2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -2499,6 +2537,10 @@ packages: is-regex: 1.1.4 dev: true + /check-error/1.0.2: + resolution: {integrity: sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=} + dev: true + /check-more-types/2.24.0: resolution: {integrity: sha1-FCD/sQ/URNz8ebQ4kbv//TKoRgA=} engines: {node: '>= 0.8.0'} @@ -2902,6 +2944,13 @@ packages: resolution: {integrity: sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==} dev: true + /deep-eql/3.0.1: + resolution: {integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==} + engines: {node: '>=0.12'} + dependencies: + type-detect: 4.0.8 + dev: true + /deep-equal/2.0.5: resolution: {integrity: sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==} dependencies: @@ -4024,6 +4073,10 @@ packages: engines: {node: 6.* || 8.* || >= 10.*} dev: true + /get-func-name/2.0.0: + resolution: {integrity: sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=} + dev: true + /get-intrinsic/1.1.1: resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} dependencies: @@ -5450,6 +5503,10 @@ packages: engines: {node: '>=8'} dev: true + /pathval/1.1.1: + resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} + dev: true + /pend/1.2.0: resolution: {integrity: sha1-elfrVQpng/kRUzH89GY9XI4AelA=} dev: true @@ -6398,6 +6455,16 @@ packages: resolution: {integrity: sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=} dev: true + /tinypool/0.0.6: + resolution: {integrity: sha512-E9vY6eq/Q7fUlSt69cY3y7qXNbVOFaxP+lAEDMKMObLrmO0MmEdUPyjOMgepoMDw/ps/sazl7WIQglnUrunnLg==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy/0.2.6: + resolution: {integrity: sha512-HXNA1PZ9p95rWK7h3DvWaDK06XmsjN3ldZjacW/1F0viVfPanTGSQ6l2iQyjNElD0Nd5ogfq/hT0sx2Hdyz4wQ==} + engines: {node: '>=14.0.0'} + dev: true + /tmp/0.2.1: resolution: {integrity: sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==} engines: {node: '>=8.17.0'} @@ -6508,6 +6575,11 @@ packages: prelude-ls: 1.2.1 dev: true + /type-detect/4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + dev: true + /type-fest/0.16.0: resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==} engines: {node: '>=10'} @@ -6940,6 +7012,32 @@ packages: fsevents: 2.3.2 dev: true + /vitest/0.0.113_vite@2.7.6: + resolution: {integrity: sha512-GRIZqFEkR0uGddoSp9uyevkS6GEMRZtIX1laCvsnsnrz3tAKR/RTLPjXBqXhFZwjueUDRyJEwD4KGD+1KXOsgw==} + engines: {node: '>=14.14.0'} + hasBin: true + peerDependencies: + c8: '*' + happy-dom: '*' + jsdom: '*' + vite: ^2.7.1 + peerDependenciesMeta: + c8: + optional: true + happy-dom: + optional: true + jsdom: + optional: true + dependencies: + '@types/chai': 4.3.0 + '@types/chai-subset': 1.3.3 + chai: 4.3.4 + local-pkg: 0.4.0 + tinypool: 0.0.6 + tinyspy: 0.2.6 + vite: 2.7.6 + dev: true + /void-elements/3.1.0: resolution: {integrity: sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=} engines: {node: '>=0.10.0'} diff --git a/src/auto-imports.d.ts b/src/auto-imports.d.ts index 95156db..d28bc94 100644 --- a/src/auto-imports.d.ts +++ b/src/auto-imports.d.ts @@ -1,9 +1,15 @@ // Generated by 'unplugin-auto-import' // We suggest you to commit this file into source control declare global { + const afterAll: typeof import('vitest')['afterAll'] + const afterEach: typeof import('vitest')['afterEach'] + const assert: typeof import('vitest')['assert'] const asyncComputed: typeof import('@vueuse/core')['asyncComputed'] const autoResetRef: typeof import('@vueuse/core')['autoResetRef'] + const beforeAll: typeof import('vitest')['beforeAll'] + const beforeEach: typeof import('vitest')['beforeEach'] const biSyncRef: typeof import('@vueuse/core')['biSyncRef'] + const chai: typeof import('vitest')['chai'] const computed: typeof import('vue')['computed'] const computedInject: typeof import('@vueuse/core')['computedInject'] const controlledComputed: typeof import('@vueuse/core')['controlledComputed'] @@ -19,9 +25,11 @@ declare global { const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch'] const defineAsyncComponent: typeof import('vue')['defineAsyncComponent'] const defineComponent: typeof import('vue')['defineComponent'] + const describe: typeof import('vitest')['describe'] const eagerComputed: typeof import('@vueuse/core')['eagerComputed'] const effectScope: typeof import('vue')['effectScope'] const EffectScope: typeof import('vue')['EffectScope'] + const expect: typeof import('vitest')['expect'] const extendRef: typeof import('@vueuse/core')['extendRef'] const getCurrentInstance: typeof import('vue')['getCurrentInstance'] const getCurrentScope: typeof import('vue')['getCurrentScope'] @@ -31,6 +39,7 @@ declare global { const isDefined: typeof import('@vueuse/core')['isDefined'] const isReadonly: typeof import('vue')['isReadonly'] const isRef: typeof import('vue')['isRef'] + const it: typeof import('vitest')['it'] const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable'] const markRaw: typeof import('vue')['markRaw'] const nextTick: typeof import('vue')['nextTick'] @@ -63,8 +72,10 @@ declare global { const shallowReactive: typeof import('vue')['shallowReactive'] const shallowReadonly: typeof import('vue')['shallowReadonly'] const shallowRef: typeof import('vue')['shallowRef'] + const suite: typeof import('vitest')['suite'] const syncRef: typeof import('@vueuse/core')['syncRef'] const templateRef: typeof import('@vueuse/core')['templateRef'] + const test: typeof import('vitest')['test'] const throttledRef: typeof import('@vueuse/core')['throttledRef'] const throttledWatch: typeof import('@vueuse/core')['throttledWatch'] const toRaw: typeof import('vue')['toRaw'] @@ -194,6 +205,8 @@ declare global { const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus'] const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll'] const useWindowSize: typeof import('@vueuse/core')['useWindowSize'] + const vi: typeof import('vitest')['vi'] + const vitest: typeof import('vitest')['vitest'] const watch: typeof import('vue')['watch'] const watchAtMost: typeof import('@vueuse/core')['watchAtMost'] const watchEffect: typeof import('vue')['watchEffect'] diff --git a/src/components.d.ts b/src/components.d.ts index 6dda6bb..876bfad 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -4,13 +4,7 @@ declare module 'vue' { export interface GlobalComponents { - CarbonCampsite: typeof import('~icons/carbon/campsite')['default'] - CarbonDicomOverlay: typeof import('~icons/carbon/dicom-overlay')['default'] - CarbonLanguage: typeof import('~icons/carbon/language')['default'] - CarbonLogoGithub: typeof import('~icons/carbon/logo-github')['default'] - CarbonMoon: typeof import('~icons/carbon/moon')['default'] - CarbonPedestrian: typeof import('~icons/carbon/pedestrian')['default'] - CarbonSun: typeof import('~icons/carbon/sun')['default'] + Counter: typeof import('./components/Counter.vue')['default'] Footer: typeof import('./components/Footer.vue')['default'] README: typeof import('./components/README.md')['default'] } diff --git a/src/components/Counter.vue b/src/components/Counter.vue new file mode 100644 index 0000000..6086a45 --- /dev/null +++ b/src/components/Counter.vue @@ -0,0 +1,19 @@ + + + diff --git a/test/__snapshots__/component.test.ts.snap b/test/__snapshots__/component.test.ts.snap new file mode 100644 index 0000000..f5a07c8 --- /dev/null +++ b/test/__snapshots__/component.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1 + +exports[`Counter.vue > should render 1`] = `"
10
"`; diff --git a/test/basic.test.ts b/test/basic.test.ts new file mode 100644 index 0000000..f97593f --- /dev/null +++ b/test/basic.test.ts @@ -0,0 +1,5 @@ +describe('tests', () => { + it('should works', () => { + expect(1 + 1).toEqual(2) + }) +}) diff --git a/test/component.test.ts b/test/component.test.ts new file mode 100644 index 0000000..cefc3b0 --- /dev/null +++ b/test/component.test.ts @@ -0,0 +1,21 @@ +import { mount } from '@vue/test-utils' +import Counter from '../src/components/Counter.vue' + +describe('Counter.vue', () => { + it('should render', () => { + const wrapper = mount(Counter, { props: { initial: 10 } }) + expect(wrapper.text()).toContain('10') + expect(wrapper.html()).toMatchSnapshot() + }) + + it('should be interactive', async() => { + const wrapper = mount(Counter, { props: { initial: 0 } }) + expect(wrapper.text()).toContain('0') + + expect(wrapper.find('.inc').exists()).toBe(true) + + await wrapper.get('button').trigger('click') + + expect(wrapper.text()).toContain('1') + }) +}) diff --git a/vite.config.ts b/vite.config.ts index 4fb6c19..90d09f7 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -44,6 +44,7 @@ export default defineConfig({ 'vue-i18n', '@vueuse/head', '@vueuse/core', + 'vitest', ], dts: 'src/auto-imports.d.ts', }), @@ -165,4 +166,13 @@ export default defineConfig({ 'vue-demi', ], }, + + // https://github.com/vitest-dev/vitest + test: { + include: ['test/**/*.test.ts'], + environment: 'jsdom', + deps: { + inline: ['@vue', '@vueuse', 'vue-demi'], + }, + }, })