diff --git a/.github/workflows/lint.yml b/.github/workflows/ci.yml similarity index 79% rename from .github/workflows/lint.yml rename to .github/workflows/ci.yml index 06352ab..0eb62c6 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Lint +name: CI on: push: @@ -9,40 +9,39 @@ on: - main jobs: - lint: + ci: runs-on: ubuntu-latest strategy: matrix: node-version: [20, 22, 24] steps: - # Checkout the repository - name: Checkout code uses: actions/checkout@v3 - # Set up Node.js - name: Set up Node.js uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - # Install pnpm - name: Install pnpm uses: pnpm/action-setup@v4 with: version: 10.18.0 - # Install dependencies - name: Install dependencies run: pnpm install - # Run lint - name: Run lint run: pnpm lint - # Run prettier - name: Run prettier run: pnpm prettier:check - # Run type checking - name: Run type checking run: pnpm tsc --noEmit + + - name: Run build + run: pnpm build + + - name: Run tests + run: pnpm test:all diff --git a/app/app.vue b/app/app.vue index ccb52d1..5acf3c1 100644 --- a/app/app.vue +++ b/app/app.vue @@ -1,5 +1,7 @@ diff --git a/app/assets/css/main.css b/app/assets/css/main.css index d4b5078..c917fb7 100644 --- a/app/assets/css/main.css +++ b/app/assets/css/main.css @@ -1 +1,6 @@ @import 'tailwindcss'; +@import '@nuxt/ui'; + +* { + font-family: Poppins, sans-serif; +} diff --git a/app/components/Header/index.vue b/app/components/Header/index.vue new file mode 100644 index 0000000..599e1bc --- /dev/null +++ b/app/components/Header/index.vue @@ -0,0 +1,57 @@ + + + diff --git a/app/layouts/default.vue b/app/layouts/default.vue new file mode 100644 index 0000000..781a35d --- /dev/null +++ b/app/layouts/default.vue @@ -0,0 +1,6 @@ + diff --git a/app/pages/index.vue b/app/pages/index.vue new file mode 100644 index 0000000..7b8b46c --- /dev/null +++ b/app/pages/index.vue @@ -0,0 +1,3 @@ + diff --git a/nuxt.config.ts b/nuxt.config.ts index d0de77a..fd3c009 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -7,15 +7,25 @@ export default defineNuxtConfig({ modules: [ '@nuxt/ui', + '@nuxt/fonts', '@nuxt/test-utils', + '@nuxt/test-utils/module', '@nuxt/image', '@nuxt/eslint', '@pinia/nuxt', - '@clerk/nuxt', + ...(process.env.NODE_ENV !== 'test' ? ['@clerk/nuxt'] : []), ], css: ['~/assets/css/main.css'], vite: { plugins: [tailwindcss()], }, + + app: { + head: { + htmlAttrs: { + style: 'background-color: #0E172B;', + }, + }, + }, }); diff --git a/package.json b/package.json index a2ddc3f..a9956f3 100644 --- a/package.json +++ b/package.json @@ -12,13 +12,18 @@ "prettier:write": "prettier --write .", "lint": "eslint .", "lint:fix": "eslint . --fix", + "test:watch": "vitest --watch", + "test:e2e": "vitest --project e2e", + "test:unit": "vitest --project nuxt", + "test:all": "vitest run", "prepare": "husky" }, "dependencies": { "@clerk/nuxt": "^1.9.3", + "@clerk/themes": "^2.4.24", "@nuxt/eslint": "1.9.0", + "@nuxt/fonts": "0.11.4", "@nuxt/image": "1.11.0", - "@nuxt/test-utils": "3.19.2", "@nuxt/ui": "4.0.0", "@pinia/nuxt": "0.11.2", "@tailwindcss/vite": "^4.1.14", @@ -33,9 +38,14 @@ "devDependencies": { "@commitlint/cli": "^20.1.0", "@commitlint/config-conventional": "^20.0.0", + "@nuxt/test-utils": "3.19.2", + "@vue/test-utils": "^2.4.6", + "happy-dom": "^19.0.2", "husky": "^9.1.7", "lint-staged": "^16.2.3", - "prettier": "3.6.2" + "playwright-core": "^1.55.1", + "prettier": "3.6.2", + "vitest": "^3.2.4" }, "lint-staged": { "*.{js,ts,vue}": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1f5c841..86571b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,15 +11,18 @@ importers: '@clerk/nuxt': specifier: ^1.9.3 version: 1.9.3(magicast@0.3.5)(react@19.1.1)(vue@3.5.22(typescript@5.9.3)) + '@clerk/themes': + specifier: ^2.4.24 + version: 2.4.24 '@nuxt/eslint': specifier: 1.9.0 version: 1.9.0(@typescript-eslint/utils@8.45.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(@vue/compiler-sfc@3.5.22)(eslint@9.37.0(jiti@2.6.1))(magicast@0.3.5)(typescript@5.9.3)(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)) + '@nuxt/fonts': + specifier: 0.11.4 + version: 0.11.4(db0@0.3.4)(ioredis@5.8.0)(magicast@0.3.5)(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)) '@nuxt/image': specifier: 1.11.0 version: 1.11.0(db0@0.3.4)(ioredis@5.8.0)(magicast@0.3.5) - '@nuxt/test-utils': - specifier: 3.19.2 - version: 3.19.2(magicast@0.3.5)(typescript@5.9.3) '@nuxt/ui': specifier: 4.0.0 version: 4.0.0(@babel/parser@7.28.4)(change-case@5.4.4)(db0@0.3.4)(embla-carousel@8.6.0)(ioredis@5.8.0)(magicast@0.3.5)(react@19.1.1)(typescript@5.9.3)(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))(vue-router@4.5.1(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))(zod@4.1.11) @@ -57,15 +60,30 @@ importers: '@commitlint/config-conventional': specifier: ^20.0.0 version: 20.0.0 + '@nuxt/test-utils': + specifier: 3.19.2 + version: 3.19.2(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)) + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.6 + happy-dom: + specifier: ^19.0.2 + version: 19.0.2 husky: specifier: ^9.1.7 version: 9.1.7 lint-staged: specifier: ^16.2.3 version: 16.2.3 + playwright-core: + specifier: ^1.55.1 + version: 1.55.1 prettier: specifier: 3.6.2 version: 3.6.2 + vitest: + specifier: ^3.2.4 + version: 3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1) packages: @@ -262,6 +280,10 @@ packages: react-dom: optional: true + '@clerk/themes@2.4.24': + resolution: {integrity: sha512-/uedLbpimSHcpkWTPmyqNiCe6otHtGDCNU0Ki++miX/sTN/hJFVU/3UpfNHgXGWWOe7Ql6fixE1pzxsdlrMlWw==} + engines: {node: '>=18.17.0'} + '@clerk/types@4.91.0': resolution: {integrity: sha512-wvumOakC1tVDeCjGE1AqhgorMiK0PEDx+knnjxVnRP/pB8eY+JuWvNoBPlqbj2jbQAmcoLN17SigUNdvO5PDLw==} engines: {node: '>=18.17.0'} @@ -855,6 +877,9 @@ packages: '@nuxtjs/color-mode@3.5.2': resolution: {integrity: sha512-cC6RfgZh3guHBMLLjrBB2Uti5eUoGM9KyauOaYS9ETmxNWBMTvpgjvSiSJp1OFljIXPIqVTJ3xtJpSNZiO3ZaA==} + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@opentelemetry/api@1.9.0': resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} @@ -1570,15 +1595,24 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/conventional-commits-parser@5.0.1': resolution: {integrity: sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/node@20.19.19': + resolution: {integrity: sha512-pb1Uqj5WJP7wrcbLU7Ru4QtA0+3kAXrkutGiD26wUKzSMgNNaPARTUDQmElUXp64kh3cWdou3Q0C7qwwxqSFmg==} + '@types/node@24.6.2': resolution: {integrity: sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==} @@ -1595,6 +1629,9 @@ packages: '@types/web-bluetooth@0.0.21': resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} + '@types/whatwg-mimetype@3.0.2': + resolution: {integrity: sha512-c2AKvDT8ToxLIOUlN51gTiHXflsfIFisS4pO7pDPoKouJCESkhZnEy623gwP9laCy5lnLDAw1vAzu2vM2YLOrA==} + '@typescript-eslint/eslint-plugin@8.45.0': resolution: {integrity: sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1777,6 +1814,35 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 vue: ^3.2.25 + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} + + '@vitest/mocker@3.2.4': + resolution: {integrity: sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} + + '@vitest/runner@3.2.4': + resolution: {integrity: sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==} + + '@vitest/snapshot@3.2.4': + resolution: {integrity: sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==} + + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} + + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} + '@volar/language-core@2.4.23': resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==} @@ -1862,6 +1928,9 @@ packages: '@vue/shared@3.5.22': resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==} + '@vue/test-utils@2.4.6': + resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} + '@vueuse/core@10.11.1': resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==} @@ -1939,6 +2008,10 @@ packages: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true + abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + abbrev@3.0.1: resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} engines: {node: ^18.17.0 || >=20.5.0} @@ -2031,6 +2104,10 @@ packages: array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-kit@2.1.2: resolution: {integrity: sha512-cl76xfBQM6pztbrFWRnxbrDm9EOqDr1BF6+qQnnDZG2Co2LjyUktkN9GTJfBAfdae+DbT2nJf2nCGAdDDN7W2g==} engines: {node: '>=20.18.0'} @@ -2189,6 +2266,10 @@ packages: caniuse-lite@1.0.30001748: resolution: {integrity: sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==} + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2200,6 +2281,10 @@ packages: change-case@5.4.4: resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} + check-error@2.1.1: + resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} + engines: {node: '>= 16'} + chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} @@ -2273,6 +2358,10 @@ packages: colortranslator@5.0.0: resolution: {integrity: sha512-Z3UPUKasUVDFCDYAjP2fmlVRf1jFHJv1izAmPjiOa0OCIw1W7iC8PZ2GsoDa8uZv+mKyWopxxStT9q05+27h7w==} + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -2314,6 +2403,9 @@ packages: confbox@0.2.2: resolution: {integrity: sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==} + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + consola@3.4.2: resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} engines: {node: ^14.18.0 || >=16.10.0} @@ -2492,6 +2584,10 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} @@ -2591,6 +2687,11 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -2875,6 +2976,10 @@ packages: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + exsolve@1.0.7: resolution: {integrity: sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw==} @@ -3083,6 +3188,10 @@ packages: h3@1.15.4: resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} + happy-dom@19.0.2: + resolution: {integrity: sha512-831CLbgDyjRbd2lApHZFsBDe56onuFcjsCBPodzWpzedTpeDr8CGZjs7iEIdNW1DVwSFRecfwzLpVyGBPamwGA==} + engines: {node: '>=20.0.0'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3293,6 +3402,11 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true + js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + js-cookie@3.0.5: resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} engines: {node: '>=14'} @@ -3530,6 +3644,9 @@ packages: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} @@ -3614,6 +3731,10 @@ packages: resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} engines: {node: '>=10'} + minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + minimatch@9.0.5: resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} engines: {node: '>=16 || 14 >=14.17'} @@ -3737,6 +3858,11 @@ packages: node-releases@2.0.23: resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==} + nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + nopt@8.1.0: resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} engines: {node: ^18.17.0 || >=20.5.0} @@ -3919,6 +4045,10 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + perfect-debounce@1.0.0: resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} @@ -3956,6 +4086,11 @@ packages: pkg-types@2.3.0: resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==} + playwright-core@1.55.1: + resolution: {integrity: sha512-Z6Mh9mkwX+zxSlHqdr5AOcJnfp+xUWLCt9uKV18fhzA8eyxUd8NUWzAjxUh55RZKSYwDGX0cfaySdhZJGMoJ+w==} + engines: {node: '>=18'} + hasBin: true + pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} @@ -4166,6 +4301,9 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + protocols@2.0.2: resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} @@ -4375,6 +4513,9 @@ packages: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} @@ -4445,6 +4586,9 @@ packages: resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} engines: {node: '>=12.0.0'} + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + standard-as-callback@2.1.0: resolution: {integrity: sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==} @@ -4625,6 +4769,12 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} @@ -4632,6 +4782,18 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -4687,6 +4849,9 @@ packages: unctx@2.4.1: resolution: {integrity: sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.13.0: resolution: {integrity: sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==} @@ -4973,12 +5138,43 @@ packages: vitest-environment-nuxt@1.0.1: resolution: {integrity: sha512-eBCwtIQriXW5/M49FjqNKfnlJYlG2LWMSNFsRVKomc8CaMqmhQPBS5LZ9DlgYL9T8xIVsiA6RZn2lk7vxov3Ow==} + vitest@3.2.4: + resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.4 + '@vitest/ui': 3.2.4 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + vscode-uri@3.1.0: resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==} vue-bundle-renderer@2.2.0: resolution: {integrity: sha512-sz/0WEdYH1KfaOm0XaBmRZOWgYTEvUDt6yPYaUzl4E52qzgWLlknaPPTTZmp6benaPTlQAI/hN1x3tAzZygycg==} + vue-component-type-helpers@2.2.12: + resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} + vue-component-type-helpers@3.1.0: resolution: {integrity: sha512-cC1pYNRZkSS1iCvdlaMbbg2sjDwxX098FucEjtz9Yig73zYjWzQsnMe5M9H8dRNv55hAIDGUI29hF2BEUA4FMQ==} @@ -5021,6 +5217,10 @@ packages: webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -5038,6 +5238,11 @@ packages: engines: {node: ^18.17.0 || >=20.5.0} hasBin: true + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -5395,6 +5600,11 @@ snapshots: optionalDependencies: react: 19.1.1 + '@clerk/themes@2.4.24': + dependencies: + '@clerk/types': 4.91.0 + tslib: 2.8.1 + '@clerk/types@4.91.0': dependencies: csstype: 3.1.3 @@ -6227,7 +6437,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/test-utils@3.19.2(magicast@0.3.5)(typescript@5.9.3)': + '@nuxt/test-utils@3.19.2(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))': dependencies: '@nuxt/kit': 3.19.2(magicast@0.3.5) c12: 3.3.0(magicast@0.3.5) @@ -6251,8 +6461,13 @@ snapshots: tinyexec: 1.0.1 ufo: 1.6.1 unplugin: 2.3.10 - vitest-environment-nuxt: 1.0.1(magicast@0.3.5)(typescript@5.9.3) + vitest-environment-nuxt: 1.0.1(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)) vue: 3.5.22(typescript@5.9.3) + optionalDependencies: + '@vue/test-utils': 2.4.6 + happy-dom: 19.0.2 + playwright-core: 1.55.1 + vitest: 3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1) transitivePeerDependencies: - magicast - typescript @@ -6416,6 +6631,8 @@ snapshots: transitivePeerDependencies: - magicast + '@one-ini/wasm@0.1.1': {} + '@opentelemetry/api@1.9.0': {} '@oxc-minify/binding-android-arm64@0.87.0': @@ -6908,14 +7125,24 @@ snapshots: tslib: 2.8.1 optional: true + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + '@types/conventional-commits-parser@5.0.1': dependencies: '@types/node': 24.6.2 + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} + '@types/node@20.19.19': + dependencies: + undici-types: 6.21.0 + '@types/node@24.6.2': dependencies: undici-types: 7.13.0 @@ -6930,6 +7157,8 @@ snapshots: '@types/web-bluetooth@0.0.21': {} + '@types/whatwg-mimetype@3.0.2': {} + '@typescript-eslint/eslint-plugin@8.45.0(@typescript-eslint/parser@8.45.0(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.37.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -7127,6 +7356,48 @@ snapshots: vite: 7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) + '@vitest/expect@3.2.4': + dependencies: + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.4(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1))': + dependencies: + '@vitest/spy': 3.2.4 + estree-walker: 3.0.3 + magic-string: 0.30.19 + optionalDependencies: + vite: 7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1) + + '@vitest/pretty-format@3.2.4': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.4': + dependencies: + '@vitest/utils': 3.2.4 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + magic-string: 0.30.19 + pathe: 2.0.3 + + '@vitest/spy@3.2.4': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.4': + dependencies: + '@vitest/pretty-format': 3.2.4 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + '@volar/language-core@2.4.23': dependencies: '@volar/source-map': 2.4.23 @@ -7270,6 +7541,11 @@ snapshots: '@vue/shared@3.5.22': {} + '@vue/test-utils@2.4.6': + dependencies: + js-beautify: 1.15.4 + vue-component-type-helpers: 2.2.12 + '@vueuse/core@10.11.1(vue@3.5.22(typescript@5.9.3))': dependencies: '@types/web-bluetooth': 0.0.20 @@ -7333,6 +7609,8 @@ snapshots: jsonparse: 1.3.1 through: 2.3.8 + abbrev@2.0.0: {} + abbrev@3.0.1: {} abort-controller@3.0.0: @@ -7428,6 +7706,8 @@ snapshots: array-ify@1.0.0: {} + assertion-error@2.0.1: {} + ast-kit@2.1.2: dependencies: '@babel/parser': 7.28.4 @@ -7595,6 +7875,14 @@ snapshots: caniuse-lite@1.0.30001748: {} + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.1 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -7604,6 +7892,8 @@ snapshots: change-case@5.4.4: {} + check-error@2.1.1: {} + chokidar@3.6.0: dependencies: anymatch: 3.1.3 @@ -7684,6 +7974,8 @@ snapshots: colortranslator@5.0.0: {} + commander@10.0.1: {} + commander@11.1.0: {} commander@14.0.1: {} @@ -7718,6 +8010,11 @@ snapshots: confbox@0.2.2: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + consola@3.4.2: {} conventional-changelog-angular@7.0.0: @@ -7892,6 +8189,8 @@ snapshots: mimic-response: 3.1.0 optional: true + deep-eql@5.0.2: {} + deep-extend@0.6.0: optional: true @@ -7964,6 +8263,13 @@ snapshots: eastasianwidth@0.2.0: {} + editorconfig@1.0.4: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.7.2 + ee-first@1.1.1: {} electron-to-chromium@1.5.230: {} @@ -8301,6 +8607,8 @@ snapshots: expand-template@2.0.3: optional: true + expect-type@1.2.2: {} + exsolve@1.0.7: {} fake-indexeddb@6.2.2: {} @@ -8516,6 +8824,12 @@ snapshots: ufo: 1.6.1 uncrypto: 0.1.3 + happy-dom@19.0.2: + dependencies: + '@types/node': 20.19.19 + '@types/whatwg-mimetype': 3.0.2 + whatwg-mimetype: 3.0.0 + has-flag@4.0.0: {} hasown@2.0.2: @@ -8578,8 +8892,7 @@ snapshots: inherits@2.0.4: {} - ini@1.3.8: - optional: true + ini@1.3.8: {} ini@4.1.1: {} @@ -8735,6 +9048,14 @@ snapshots: jiti@2.6.1: {} + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.4.5 + js-cookie: 3.0.5 + nopt: 7.2.1 + js-cookie@3.0.5: {} js-tokens@4.0.0: {} @@ -8941,6 +9262,8 @@ snapshots: strip-ansi: 7.1.2 wrap-ansi: 9.0.2 + loupe@3.2.1: {} + lru-cache@10.4.3: {} lru-cache@5.1.1: @@ -9018,6 +9341,10 @@ snapshots: dependencies: brace-expansion: 2.0.2 + minimatch@9.0.1: + dependencies: + brace-expansion: 2.0.2 + minimatch@9.0.5: dependencies: brace-expansion: 2.0.2 @@ -9208,6 +9535,10 @@ snapshots: node-releases@2.0.23: {} + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + nopt@8.1.0: dependencies: abbrev: 3.0.1 @@ -9544,6 +9875,8 @@ snapshots: pathe@2.0.3: {} + pathval@2.0.1: {} + perfect-debounce@1.0.0: {} perfect-debounce@2.0.0: {} @@ -9575,6 +9908,8 @@ snapshots: exsolve: 1.0.7 pathe: 2.0.3 + playwright-core@1.55.1: {} + pluralize@8.0.0: {} postcss-calc@10.1.1(postcss@8.5.6): @@ -9775,6 +10110,8 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 + proto-list@1.2.4: {} + protocols@2.0.2: {} pump@3.0.3: @@ -10031,6 +10368,8 @@ snapshots: shell-quote@1.8.3: {} + siginfo@2.0.0: {} + signal-exit@4.1.0: {} simple-concat@1.0.1: @@ -10099,6 +10438,8 @@ snapshots: stable-hash-x@0.2.0: {} + stackback@0.0.2: {} + standard-as-callback@2.1.0: {} standardwebhooks@1.0.0: @@ -10305,6 +10646,10 @@ snapshots: tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + tinyexec@1.0.1: {} tinyglobby@0.2.15: @@ -10312,6 +10657,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -10356,6 +10707,8 @@ snapshots: magic-string: 0.30.19 unplugin: 2.3.10 + undici-types@6.21.0: {} + undici-types@7.13.0: {} unenv@2.0.0-rc.21: @@ -10655,9 +11008,9 @@ snapshots: terser: 5.44.0 yaml: 2.8.1 - vitest-environment-nuxt@1.0.1(magicast@0.3.5)(typescript@5.9.3): + vitest-environment-nuxt@1.0.1(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)): dependencies: - '@nuxt/test-utils': 3.19.2(magicast@0.3.5)(typescript@5.9.3) + '@nuxt/test-utils': 3.19.2(@vue/test-utils@2.4.6)(happy-dom@19.0.2)(magicast@0.3.5)(playwright-core@1.55.1)(typescript@5.9.3)(vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)) transitivePeerDependencies: - '@cucumber/cucumber' - '@jest/globals' @@ -10672,12 +11025,56 @@ snapshots: - typescript - vitest + vitest@3.2.4(@types/node@24.6.2)(happy-dom@19.0.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1): + dependencies: + '@types/chai': 5.2.2 + '@vitest/expect': 3.2.4 + '@vitest/mocker': 3.2.4(vite@7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1)) + '@vitest/pretty-format': 3.2.4 + '@vitest/runner': 3.2.4 + '@vitest/snapshot': 3.2.4 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.2.2 + magic-string: 0.30.19 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.9.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 7.1.9(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1) + vite-node: 3.2.4(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(yaml@2.8.1) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 24.6.2 + happy-dom: 19.0.2 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + vscode-uri@3.1.0: {} vue-bundle-renderer@2.2.0: dependencies: ufo: 1.6.1 + vue-component-type-helpers@2.2.12: {} + vue-component-type-helpers@3.1.0: {} vue-demi@0.14.10(vue@3.5.22(typescript@5.9.3)): @@ -10717,6 +11114,8 @@ snapshots: webpack-virtual-modules@0.6.2: {} + whatwg-mimetype@3.0.0: {} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -10732,6 +11131,11 @@ snapshots: dependencies: isexe: 3.1.1 + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + word-wrap@1.2.5: {} wrap-ansi@7.0.0: diff --git a/public/favicon.ico b/public/favicon.ico index 18993ad..a50c3a3 100644 Binary files a/public/favicon.ico and b/public/favicon.ico differ diff --git a/public/lystra-logo-dark.svg b/public/lystra-logo-dark.svg new file mode 100644 index 0000000..f2af3d9 --- /dev/null +++ b/public/lystra-logo-dark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/lystra-logo-light.svg b/public/lystra-logo-light.svg new file mode 100644 index 0000000..b655e4d --- /dev/null +++ b/public/lystra-logo-light.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test/nuxt/__snapshots__/app.nuxt.spec.ts.snap b/test/nuxt/__snapshots__/app.nuxt.spec.ts.snap new file mode 100644 index 0000000..9f8d2ea --- /dev/null +++ b/test/nuxt/__snapshots__/app.nuxt.spec.ts.snap @@ -0,0 +1,22 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`App > should match snapshot 1`] = ` +"
+
+ Lystra logo +

Lystra

+ +
+
+ + +
+
+
+
+ +" +`; diff --git a/test/nuxt/app.nuxt.spec.ts b/test/nuxt/app.nuxt.spec.ts new file mode 100644 index 0000000..94c2f96 --- /dev/null +++ b/test/nuxt/app.nuxt.spec.ts @@ -0,0 +1,35 @@ +import { mountSuspended } from '@nuxt/test-utils/runtime'; +import App from '../../app/app.vue'; +import { describe, expect, it } from 'vitest'; + +describe('App', () => { + it('should match snapshot', async () => { + const wrapper = await mountSuspended(App, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('should render properly', async () => { + const wrapper = await mountSuspended(App, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + expect(wrapper.exists()).toBe(true); + }); + + // TODO: Add more tests as needed +}); diff --git a/test/nuxt/components/Header.nuxt.spec.ts b/test/nuxt/components/Header.nuxt.spec.ts new file mode 100644 index 0000000..e6d1c54 --- /dev/null +++ b/test/nuxt/components/Header.nuxt.spec.ts @@ -0,0 +1,142 @@ +import { mountSuspended } from '@nuxt/test-utils/runtime'; +import Header from '../../../app/components/Header/index.vue'; +import { describe, expect, it, vi } from 'vitest'; + +describe('Header', () => { + it('should match snapshot', async () => { + const wrapper = await mountSuspended(Header, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('should render properly', async () => { + const wrapper = await mountSuspended(Header, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + expect(wrapper.exists()).toBe(true); + }); + + it('should render the logo properly', async () => { + const wrapper = await mountSuspended(Header, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + const logo = wrapper.find('img'); + expect(logo.exists()).toBe(true); + expect(logo.attributes('src')).toBe( + '/_ipx/b_transparent&fit_contain&s_40x40/lystra-logo-light.svg' + ); + expect(logo.attributes('alt')).toBe('Lystra logo'); + expect(logo.attributes('width')).toBe('40'); + expect(logo.attributes('height')).toBe('40'); + }); + + it('should render signed in and signed out stubs', async () => { + const wrapper = await mountSuspended(Header, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + + const signedInButtonStub = wrapper.find('signed-in-stub'); + expect(signedInButtonStub.exists()).toBe(true); + const signedOutButtonStub = wrapper.find('signed-out-stub'); + expect(signedOutButtonStub.exists()).toBe(true); + }); + + it('should render the header properly', async () => { + const wrapper = await mountSuspended(Header, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + const headerElement = wrapper.find('h1'); + expect(headerElement.exists()).toBe(true); + expect(headerElement.attributes('class')).toContain( + 'text-gray-900 dark:text-white text-lg font-bold' + ); + expect(headerElement.text()).toBe('Lystra'); + }); + + it('should render the theme button properly', async () => { + const wrapper = await mountSuspended(Header, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + const themeButton = wrapper.find('button'); + expect(themeButton.exists()).toBe(true); + expect(themeButton.attributes('class')).toContain( + 'rounded-md font-medium inline-flex items-center disabled:cursor-not-allowed aria-disabled:cursor-not-allowed disabled:opacity-75 aria-disabled:opacity-75 transition-colors text-sm gap-1.5 text-default hover:bg-elevated active:bg-elevated focus:outline-none focus-visible:bg-elevated hover:disabled:bg-transparent dark:hover:disabled:bg-transparent hover:aria-disabled:bg-transparent dark:hover:aria-disabled:bg-transparent p-1.5 cursor-pointer' + ); + expect(themeButton.attributes('aria-label')).toBe('Switch to dark mode'); + expect(themeButton.attributes('type')).toBe('button'); + + const icon = themeButton.find('span'); + expect(icon.exists()).toBe(true); + expect(icon.attributes('class')).toContain('iconify i-lucide:sun shrink-0 size-5'); + expect(icon.attributes('aria-hidden')).toBe('true'); + }); + + it('should toggle colorMode.value when theme button is clicked', async () => { + const colorMode = { value: 'light' }; + vi.mock('nuxt/app', () => ({ + useColorMode: () => colorMode, + })); + + const HeaderWithMock = (await import('../../../app/components/Header/index.vue')).default; + const wrapper = await mountSuspended(HeaderWithMock, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + + const themeButton = wrapper.findAll('button')[0]; + expect(themeButton.exists()).toBe(true); + + colorMode.value = 'dark'; + wrapper.vm.$forceUpdate?.(); + expect(colorMode.value).toBe('dark'); + }); +}); diff --git a/test/nuxt/components/__snapshots__/Header.nuxt.spec.ts.snap b/test/nuxt/components/__snapshots__/Header.nuxt.spec.ts.snap new file mode 100644 index 0000000..45d1a25 --- /dev/null +++ b/test/nuxt/components/__snapshots__/Header.nuxt.spec.ts.snap @@ -0,0 +1,17 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`Header > should match snapshot 1`] = ` +"
+ Lystra logo +

Lystra

+ +
+
+ + +
+
" +`; diff --git a/test/nuxt/layouts/__snapshots__/default.nuxt.spec.ts.snap b/test/nuxt/layouts/__snapshots__/default.nuxt.spec.ts.snap new file mode 100644 index 0000000..83e1364 --- /dev/null +++ b/test/nuxt/layouts/__snapshots__/default.nuxt.spec.ts.snap @@ -0,0 +1,19 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`DefaultLayout > should match snapshot 1`] = ` +"
+
+ Lystra logo +

Lystra

+ +
+
+ + +
+
+
" +`; diff --git a/test/nuxt/layouts/default.nuxt.spec.ts b/test/nuxt/layouts/default.nuxt.spec.ts new file mode 100644 index 0000000..fb34def --- /dev/null +++ b/test/nuxt/layouts/default.nuxt.spec.ts @@ -0,0 +1,35 @@ +import { mountSuspended } from '@nuxt/test-utils/runtime'; +import DefaultLayout from '../../../app/layouts/default.vue'; +import { describe, expect, it } from 'vitest'; + +describe('DefaultLayout', () => { + it('should match snapshot', async () => { + const wrapper = await mountSuspended(DefaultLayout, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + expect(wrapper.html()).toMatchSnapshot(); + }); + + it('should render properly', async () => { + const wrapper = await mountSuspended(DefaultLayout, { + global: { + stubs: { + SignInButton: true, + SignedOut: true, + UserButton: true, + SignedIn: true, + }, + }, + }); + expect(wrapper.exists()).toBe(true); + }); + + // TODO: Add more tests as needed +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..5845597 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineVitestConfig } from '@nuxt/test-utils/config'; + +export default defineVitestConfig({ + test: { + environment: 'nuxt', + env: { + NODE_ENV: 'test', + NUXT_PUBLIC_CLERK_PUBLISHABLE_KEY: 'pk_test_fake_key', + NUXT_CLERK_SECRET_KEY: 'sk_test_fake_key', + }, + }, +});