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 @@
+
+
+
+