diff --git a/legalconsenthub/nuxt.config.ts b/legalconsenthub/nuxt.config.ts index e463069..e847366 100644 --- a/legalconsenthub/nuxt.config.ts +++ b/legalconsenthub/nuxt.config.ts @@ -1,6 +1,6 @@ export default defineNuxtConfig({ sourcemap: true, - modules: ['@nuxt/ui', '@nuxt/eslint', '@pinia/nuxt', '@nuxtjs/i18n', 'nuxt-auth-utils'], + modules: ['@nuxt/ui', '@nuxt/eslint', '@pinia/nuxt', '@nuxtjs/i18n', 'nuxt-auth-utils', '@nuxt/test-utils/module'], css: ['~/assets/css/main.css'], runtimeConfig: { public: { diff --git a/legalconsenthub/package.json b/legalconsenthub/package.json index 020248a..079c2e1 100644 --- a/legalconsenthub/package.json +++ b/legalconsenthub/package.json @@ -12,7 +12,8 @@ "type-check": "nuxi typecheck", "lint": "eslint .", "lint:fix": "eslint . --fix", - "check": "pnpm run lint && pnpm run type-check && pnpm run format", + "test": "vitest run", + "check": "pnpm run lint && pnpm run type-check && pnpm run format && pnpm run test", "api:generate": "openapi-generator-cli generate -i ../api/legalconsenthub.yml -g typescript-fetch -o .api-client" }, "dependencies": { @@ -33,10 +34,14 @@ }, "devDependencies": { "@nuxt/eslint": "1.1.0", + "@nuxt/test-utils": "^3.21.0", "@openapitools/openapi-generator-cli": "2.16.3", + "@vue/test-utils": "^2.4.6", "eslint": "9.20.1", + "happy-dom": "^20.0.11", "prettier": "3.5.1", "typescript": "5.7.3", + "vitest": "^4.0.16", "vue-tsc": "2.2.2" }, "volta": { diff --git a/legalconsenthub/pnpm-lock.yaml b/legalconsenthub/pnpm-lock.yaml index 1fb6652..22008ec 100644 --- a/legalconsenthub/pnpm-lock.yaml +++ b/legalconsenthub/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 1.6.0(monaco-editor@0.55.1)(vue@3.5.26(typescript@5.7.3)) '@nuxt/ui': specifier: 4.3.0 - version: 4.3.0(a99739665d1a539f6ce9c0f71b447aee) + version: 4.3.0(ffcd66d9b4ad7c3211d52c6a563c2be7) '@nuxtjs/i18n': specifier: 10.0.3 version: 10.0.3(@vue/compiler-dom@3.5.26)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(magicast@0.5.1)(rollup@4.54.0)(vue@3.5.26(typescript@5.7.3)) @@ -34,7 +34,7 @@ importers: version: 4.0.0 nuxt: specifier: 4.2.0 - version: 4.2.0(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2) + version: 4.2.0(@parcel/watcher@2.5.1)(@types/node@20.19.27)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2) nuxt-auth-utils: specifier: 0.5.25 version: 0.5.25(magicast@0.5.1) @@ -53,19 +53,31 @@ importers: devDependencies: '@nuxt/eslint': specifier: 1.1.0 - version: 1.1.0(@typescript-eslint/utils@8.50.0(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3))(@vue/compiler-sfc@3.5.26)(eslint@9.20.1(jiti@2.6.1))(magicast@0.5.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + version: 1.1.0(@typescript-eslint/utils@8.50.0(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3))(@vue/compiler-sfc@3.5.26)(eslint@9.20.1(jiti@2.6.1))(magicast@0.5.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@nuxt/test-utils': + specifier: ^3.21.0 + version: 3.21.0(@vue/test-utils@2.4.6)(happy-dom@20.0.11)(magicast@0.5.1)(typescript@5.7.3)(vitest@4.0.16(@types/node@20.19.27)(happy-dom@20.0.11)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) '@openapitools/openapi-generator-cli': specifier: 2.16.3 version: 2.16.3 + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.6 eslint: specifier: 9.20.1 version: 9.20.1(jiti@2.6.1) + happy-dom: + specifier: ^20.0.11 + version: 20.0.11 prettier: specifier: 3.5.1 version: 3.5.1 typescript: specifier: 5.7.3 version: 5.7.3 + vitest: + specifier: ^4.0.16 + version: 4.0.16(@types/node@20.19.27)(happy-dom@20.0.11)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) vue-tsc: specifier: 2.2.2 version: 2.2.2(typescript@5.7.3) @@ -978,6 +990,42 @@ packages: engines: {node: '>=18.12.0'} hasBin: true + '@nuxt/test-utils@3.21.0': + resolution: {integrity: sha512-A6XExfgHq88+XuXAU4MMr5QBHS2mWA5qRVSvsMPP2U+YSsnk+Vt7P7dxbvJPE4+n6LHbC1IM0QjTVteo+VCxOA==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@cucumber/cucumber': ^10.3.1 || >=11.0.0 + '@jest/globals': ^29.5.0 || >=30.0.0 + '@playwright/test': ^1.43.1 + '@testing-library/vue': ^7.0.0 || ^8.0.1 + '@vitest/ui': '*' + '@vue/test-utils': ^2.4.2 + happy-dom: '*' + jsdom: '*' + playwright-core: ^1.43.1 + vitest: ^3.2.0 + peerDependenciesMeta: + '@cucumber/cucumber': + optional: true + '@jest/globals': + optional: true + '@playwright/test': + optional: true + '@testing-library/vue': + optional: true + '@vitest/ui': + optional: true + '@vue/test-utils': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright-core: + optional: true + vitest: + optional: true + '@nuxt/ui@4.3.0': resolution: {integrity: sha512-zhOIba3roiqNwV/hXXkKBlv9RA01/Gd2Okydpgps2zM4KGx6RM+ED5JGUOSd41bmTeBRO7v7Lg4w3Vyj9hQPiA==} hasBin: true @@ -1032,6 +1080,9 @@ packages: engines: {node: '>=8.0.0', npm: '>=5.0.0'} hasBin: true + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@openapitools/openapi-generator-cli@2.16.3': resolution: {integrity: sha512-HUpxQW45MLoWruXPvwnS2p6PkbnEIeWuDq4AembALRNGbIbg158k3peBCIbnn4PzGura9TnhaPPjOl3BF5PinQ==} engines: {node: '>=16'} @@ -2084,6 +2135,12 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -2099,6 +2156,9 @@ packages: '@types/mdurl@2.0.0': resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==} + '@types/node@20.19.27': + resolution: {integrity: sha512-N2clP5pJhB2YnZJ3PIHFk5RkygRX5WO/5f0WC08tp0wd+sv0rsJk3MqWn3CbNmT2J505a5336jaQj4ph1AdMug==} + '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -2121,6 +2181,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.50.0': resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2299,6 +2362,35 @@ packages: vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 vue: ^3.2.25 + '@vitest/expect@4.0.16': + resolution: {integrity: sha512-eshqULT2It7McaJkQGLkPjPjNph+uevROGuIMJdG3V+0BSR2w9u6J9Lwu+E8cK5TETlfou8GRijhafIMhXsimA==} + + '@vitest/mocker@4.0.16': + resolution: {integrity: sha512-yb6k4AZxJTB+q9ycAvsoxGn+j/po0UaPgajllBgt1PzoMAAmJGYFdDk0uCcRcxb3BrME34I6u8gHZTQlkqSZpg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.16': + resolution: {integrity: sha512-eNCYNsSty9xJKi/UdVD8Ou16alu7AYiS2fCPRs0b1OdhJiV89buAXQLpTbe+X8V9L6qrs9CqyvU7OaAopJYPsA==} + + '@vitest/runner@4.0.16': + resolution: {integrity: sha512-VWEDm5Wv9xEo80ctjORcTQRJ539EGPB3Pb9ApvVRAY1U/WkHXmmYISqU5E79uCwcW7xYUV38gwZD+RV755fu3Q==} + + '@vitest/snapshot@4.0.16': + resolution: {integrity: sha512-sf6NcrYhYBsSYefxnry+DR8n3UV4xWZwWxYbCJUt2YdvtqzSPR7VfGrY0zsv090DAbjFZsi7ZaMi1KnSRyK1XA==} + + '@vitest/spy@4.0.16': + resolution: {integrity: sha512-4jIOWjKP0ZUaEmJm00E0cOBLU+5WE0BpeNr3XN6TEF05ltro6NJqHWxXD0kA8/Zc8Nh23AT8WQxwNG+WeROupw==} + + '@vitest/utils@4.0.16': + resolution: {integrity: sha512-h8z9yYhV3e1LEfaQ3zdypIrnAg/9hguReGZoS7Gl0aBG5xgA410zBqECqmaF/+RkTggRsfnzc1XaAHA6bmUufA==} + '@volar/language-core@2.4.27': resolution: {integrity: sha512-DjmjBWZ4tJKxfNC1F6HyYERNHPYS7L7OPFyCrestykNdUZMFYzI9WTyvwPcaNaHlrEUwESHYsfEw3isInncZxQ==} @@ -2402,6 +2494,9 @@ packages: '@vue/shared@3.5.26': resolution: {integrity: sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A==} + '@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==} @@ -2488,6 +2583,10 @@ packages: peerDependencies: vue: ^3.5.0 + 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} @@ -2571,6 +2670,10 @@ packages: resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-kit@1.4.3: resolution: {integrity: sha512-MdJqjpodkS5J149zN0Po+HPshkTdUyrvF7CKTafUgv69vBSPtncrj+3IiUgqdd7ElIEkbeXCsEouBUwLrw9Ilg==} engines: {node: '>=16.14.0'} @@ -2727,6 +2830,10 @@ packages: resolution: {integrity: sha512-wljhAjDDIv/hM2FzgJnYQg90AWmZMNtESCjTeLH680qTzdo0nErlCxOmgzgX4ZsZAtIvqHyD87ES8QyriXB+BQ==} engines: {node: '>=18'} + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -2809,6 +2916,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + 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'} @@ -2851,6 +2962,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@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} @@ -3109,6 +3223,11 @@ packages: easy-table@1.1.0: resolution: {integrity: sha512-oq33hWOSSnl2Hoh00tZWaIPi1ievrD9aFG82/IgjlycAnW9hHx5PkJiXpxPsgEE+H7BsbVQXFVFST8TEXS6/pA==} + 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==} @@ -3390,6 +3509,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + expect-type@1.3.0: + resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} + engines: {node: '>=12.0.0'} + exsolve@1.0.8: resolution: {integrity: sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==} @@ -3397,6 +3520,10 @@ packages: resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} engines: {node: '>=4'} + fake-indexeddb@6.2.5: + resolution: {integrity: sha512-CGnyrvbhPlWYMngksqrSSUT1BAVP49dZocrHuK0SvtR0D5TMs5wP0o3j7jexDJW01KSadjBp1M/71o/KR3nD1w==} + engines: {node: '>=18'} + fast-deep-equal@2.0.1: resolution: {integrity: sha512-bCK/2Z4zLidyB4ReuIsvALH6w31YfAQDmXMqMx6FyfHqvBxtjC0eRumeSu4Bs3XtXwpyIywtSTrVT99BxY1f9w==} @@ -3635,6 +3762,10 @@ packages: h3@1.15.4: resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} + happy-dom@20.0.11: + resolution: {integrity: sha512-QsCdAUHAmiDeKeaNojb1OHOPF7NjcWPBR7obdu3NwH2a/oyQaLg5d0aaCy/9My6CdPChYF07dvz5chaXBGaD4g==} + engines: {node: '>=20.0.0'} + has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} @@ -3735,6 +3866,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + ini@4.1.1: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -3869,6 +4003,15 @@ packages: jose@6.1.3: resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + 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'} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -4201,6 +4344,10 @@ packages: resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==} engines: {node: '>=16 || 14 >=14.17'} + 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'} @@ -4318,6 +4465,11 @@ packages: node-releases@2.0.27: resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + 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} @@ -4383,6 +4535,9 @@ packages: oauth4webapi@3.8.3: resolution: {integrity: sha512-pQ5BsX3QRTgnt5HxgHwgunIRaDXBdkT23tf8dfzmtTIL2LTpdmxgbpbBm0VgFWAIDlezQvQCTgnVIUmHupXHxw==} + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + ofetch@1.5.1: resolution: {integrity: sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA==} @@ -4858,6 +5013,9 @@ packages: prosemirror-view@1.41.4: resolution: {integrity: sha512-WkKgnyjNncri03Gjaz3IFWvCAE94XoiEgvtr0/r2Xw7R8/IjK3sKLSiDoCHWcsXSAinVaKlGRZDvMCsF1kbzjA==} + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + protocols@2.0.2: resolution: {integrity: sha512-hHVTzba3wboROl0/aWRRG9dMytgH6ow//STBZh43l/wQgmMhYhOFi0EHWAPtoCz9IAUymsyP0TSBHkhgMEGNnQ==} @@ -5120,6 +5278,9 @@ packages: resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} engines: {node: '>= 0.4'} + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -5206,6 +5367,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==} @@ -5345,6 +5509,9 @@ 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@1.0.2: resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} engines: {node: '>=18'} @@ -5353,6 +5520,10 @@ packages: resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} engines: {node: '>=12.0.0'} + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -5444,6 +5615,9 @@ packages: unctx@2.5.0: resolution: {integrity: sha512-p+Rz9x0R7X+CYDkT+Xg8/GhpcShTlU8n+cf9OtOEf7zEQsNcCZO1dPKNRDqvUTaq+P32PMMkxWHwfrxkqfqAYg==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + unenv@2.0.0-rc.24: resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} @@ -5745,12 +5919,52 @@ packages: yaml: optional: true + vitest-environment-nuxt@1.0.1: + resolution: {integrity: sha512-eBCwtIQriXW5/M49FjqNKfnlJYlG2LWMSNFsRVKomc8CaMqmhQPBS5LZ9DlgYL9T8xIVsiA6RZn2lk7vxov3Ow==} + + vitest@4.0.16: + resolution: {integrity: sha512-E4t7DJ9pESL6E3I8nFjPa4xGUd3PmiWDLsDztS2qXSJWfHtbQnwAWylaBvSNY48I3vr8PTqIZlyK8TE3V3CA4Q==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.16 + '@vitest/browser-preview': 4.0.16 + '@vitest/browser-webdriverio': 4.0.16 + '@vitest/ui': 4.0.16 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + 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.2.1: resolution: {integrity: sha512-gKV7XOkQl4urSuLHNY1tnVQf7wVgtb/mKbRyxSLWGZUY9RK7aDPhBenTjm+i8ZFe0zC2PZeHMPtOZXZfyaFOzQ==} @@ -5811,6 +6025,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==} @@ -5828,6 +6046,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'} @@ -6725,27 +6948,27 @@ snapshots: '@nuxt/devalue@2.0.2': {} - '@nuxt/devtools-kit@2.7.0(magicast@0.3.5)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + '@nuxt/devtools-kit@2.7.0(magicast@0.3.5)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@nuxt/kit': 3.20.2(magicast@0.3.5) execa: 8.0.1 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - magicast - '@nuxt/devtools-kit@2.7.0(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + '@nuxt/devtools-kit@2.7.0(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@nuxt/kit': 3.20.2(magicast@0.5.1) execa: 8.0.1 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - magicast - '@nuxt/devtools-kit@3.1.1(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + '@nuxt/devtools-kit@3.1.1(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@nuxt/kit': 4.2.2(magicast@0.5.1) execa: 8.0.1 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - magicast @@ -6760,12 +6983,12 @@ snapshots: prompts: 2.4.2 semver: 7.7.3 - '@nuxt/devtools@2.7.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': + '@nuxt/devtools@2.7.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': dependencies: - '@nuxt/devtools-kit': 2.7.0(magicast@0.3.5)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@nuxt/devtools-kit': 2.7.0(magicast@0.3.5)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) '@nuxt/devtools-wizard': 2.7.0 '@nuxt/kit': 3.20.2(magicast@0.3.5) - '@vue/devtools-core': 7.7.9(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) + '@vue/devtools-core': 7.7.9(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) '@vue/devtools-kit': 7.7.9 birpc: 2.9.0 consola: 3.4.2 @@ -6790,9 +7013,9 @@ snapshots: sirv: 3.0.2 structured-clone-es: 1.0.0 tinyglobby: 0.2.15 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) - vite-plugin-inspect: 11.3.3(@nuxt/kit@3.20.2(magicast@0.3.5))(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) - vite-plugin-vue-tracer: 1.2.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite-plugin-inspect: 11.3.3(@nuxt/kit@3.20.2(magicast@0.3.5))(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + vite-plugin-vue-tracer: 1.2.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) which: 5.0.0 ws: 8.18.3 transitivePeerDependencies: @@ -6840,10 +7063,10 @@ snapshots: - supports-color - typescript - '@nuxt/eslint@1.1.0(@typescript-eslint/utils@8.50.0(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3))(@vue/compiler-sfc@3.5.26)(eslint@9.20.1(jiti@2.6.1))(magicast@0.5.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + '@nuxt/eslint@1.1.0(@typescript-eslint/utils@8.50.0(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3))(@vue/compiler-sfc@3.5.26)(eslint@9.20.1(jiti@2.6.1))(magicast@0.5.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@eslint/config-inspector': 1.4.2(eslint@9.20.1(jiti@2.6.1)) - '@nuxt/devtools-kit': 2.7.0(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@nuxt/devtools-kit': 2.7.0(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) '@nuxt/eslint-config': 1.1.0(@typescript-eslint/utils@8.50.0(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3))(@vue/compiler-sfc@3.5.26)(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3) '@nuxt/eslint-plugin': 1.1.0(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3) '@nuxt/kit': 3.20.2(magicast@0.5.1) @@ -6868,16 +7091,16 @@ snapshots: - utf-8-validate - vite - '@nuxt/fonts@0.12.1(db0@0.3.4)(ioredis@5.8.2)(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + '@nuxt/fonts@0.12.1(db0@0.3.4)(ioredis@5.8.2)(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': dependencies: - '@nuxt/devtools-kit': 3.1.1(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@nuxt/devtools-kit': 3.1.1(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) '@nuxt/kit': 4.2.2(magicast@0.5.1) consola: 3.4.2 css-tree: 3.1.0 defu: 6.1.4 esbuild: 0.25.12 fontaine: 0.7.0 - fontless: 0.1.0(db0@0.3.4)(ioredis@5.8.2)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + fontless: 0.1.0(db0@0.3.4)(ioredis@5.8.2)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) h3: 1.15.4 jiti: 2.6.1 magic-regexp: 0.10.0 @@ -6914,13 +7137,13 @@ snapshots: - uploadthing - vite - '@nuxt/icon@2.1.1(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': + '@nuxt/icon@2.1.1(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': dependencies: '@iconify/collections': 1.0.631 '@iconify/types': 2.0.0 '@iconify/utils': 3.1.0 '@iconify/vue': 5.0.0(vue@3.5.26(typescript@5.7.3)) - '@nuxt/devtools-kit': 3.1.1(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@nuxt/devtools-kit': 3.1.1(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) '@nuxt/kit': 4.2.2(magicast@0.5.1) consola: 3.4.2 local-pkg: 1.1.2 @@ -7037,7 +7260,7 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/nitro-server@4.2.0(db0@0.3.4)(ioredis@5.8.2)(magicast@0.5.1)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2))(typescript@5.7.3)': + '@nuxt/nitro-server@4.2.0(db0@0.3.4)(ioredis@5.8.2)(magicast@0.5.1)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@20.19.27)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2))(typescript@5.7.3)': dependencies: '@nuxt/devalue': 2.0.2 '@nuxt/kit': 4.2.0(magicast@0.5.1) @@ -7055,7 +7278,7 @@ snapshots: klona: 2.0.6 mocked-exports: 0.1.1 nitropack: 2.12.9 - nuxt: 4.2.0(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2) + nuxt: 4.2.0(@parcel/watcher@2.5.1)(@types/node@20.19.27)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2) pathe: 2.0.3 pkg-types: 2.3.0 radix3: 1.1.2 @@ -7134,19 +7357,54 @@ snapshots: transitivePeerDependencies: - magicast - '@nuxt/ui@4.3.0(a99739665d1a539f6ce9c0f71b447aee)': + '@nuxt/test-utils@3.21.0(@vue/test-utils@2.4.6)(happy-dom@20.0.11)(magicast@0.5.1)(typescript@5.7.3)(vitest@4.0.16(@types/node@20.19.27)(happy-dom@20.0.11)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + dependencies: + '@nuxt/kit': 3.20.2(magicast@0.5.1) + c12: 3.3.3(magicast@0.5.1) + consola: 3.4.2 + defu: 6.1.4 + destr: 2.0.5 + estree-walker: 3.0.3 + exsolve: 1.0.8 + fake-indexeddb: 6.2.5 + get-port-please: 3.2.0 + h3: 1.15.4 + local-pkg: 1.1.2 + magic-string: 0.30.21 + node-fetch-native: 1.6.7 + node-mock-http: 1.0.4 + ofetch: 1.5.1 + pathe: 2.0.3 + perfect-debounce: 2.0.0 + radix3: 1.1.2 + scule: 1.3.0 + std-env: 3.10.0 + tinyexec: 1.0.2 + ufo: 1.6.1 + unplugin: 2.3.11 + vitest-environment-nuxt: 1.0.1(@vue/test-utils@2.4.6)(happy-dom@20.0.11)(magicast@0.5.1)(typescript@5.7.3)(vitest@4.0.16(@types/node@20.19.27)(happy-dom@20.0.11)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + vue: 3.5.26(typescript@5.7.3) + optionalDependencies: + '@vue/test-utils': 2.4.6 + happy-dom: 20.0.11 + vitest: 4.0.16(@types/node@20.19.27)(happy-dom@20.0.11)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + transitivePeerDependencies: + - magicast + - typescript + + '@nuxt/ui@4.3.0(ffcd66d9b4ad7c3211d52c6a563c2be7)': dependencies: '@iconify/vue': 5.0.0(vue@3.5.26(typescript@5.7.3)) '@internationalized/date': 3.10.1 '@internationalized/number': 3.6.5 - '@nuxt/fonts': 0.12.1(db0@0.3.4)(ioredis@5.8.2)(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) - '@nuxt/icon': 2.1.1(magicast@0.5.1)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) + '@nuxt/fonts': 0.12.1(db0@0.3.4)(ioredis@5.8.2)(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@nuxt/icon': 2.1.1(magicast@0.5.1)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) '@nuxt/kit': 4.2.2(magicast@0.5.1) '@nuxt/schema': 4.2.2 '@nuxtjs/color-mode': 3.5.2(magicast@0.5.1) '@standard-schema/spec': 1.1.0 '@tailwindcss/postcss': 4.1.18 - '@tailwindcss/vite': 4.1.18(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@tailwindcss/vite': 4.1.18(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) '@tanstack/vue-table': 8.21.3(vue@3.5.26(typescript@5.7.3)) '@tanstack/vue-virtual': 3.13.13(vue@3.5.26(typescript@5.7.3)) '@tiptap/core': 3.13.0(@tiptap/pm@3.13.0) @@ -7241,12 +7499,12 @@ snapshots: - vite - vue - '@nuxt/vite-builder@4.2.0(eslint@9.20.1(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2))(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vue-tsc@2.2.2(typescript@5.7.3))(vue@3.5.26(typescript@5.7.3))(yaml@2.8.2)': + '@nuxt/vite-builder@4.2.0(@types/node@20.19.27)(eslint@9.20.1(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@20.19.27)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2))(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vue-tsc@2.2.2(typescript@5.7.3))(vue@3.5.26(typescript@5.7.3))(yaml@2.8.2)': dependencies: '@nuxt/kit': 4.2.0(magicast@0.5.1) '@rollup/plugin-replace': 6.0.3(rollup@4.54.0) - '@vitejs/plugin-vue': 6.0.3(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) - '@vitejs/plugin-vue-jsx': 5.1.2(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) + '@vitejs/plugin-vue': 6.0.3(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) + '@vitejs/plugin-vue-jsx': 5.1.2(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) autoprefixer: 10.4.23(postcss@8.5.6) consola: 3.4.2 cssnano: 7.1.2(postcss@8.5.6) @@ -7261,7 +7519,7 @@ snapshots: magic-string: 0.30.21 mlly: 1.8.0 mocked-exports: 0.1.1 - nuxt: 4.2.0(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2) + nuxt: 4.2.0(@parcel/watcher@2.5.1)(@types/node@20.19.27)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2) pathe: 2.0.3 pkg-types: 2.3.0 postcss: 8.5.6 @@ -7270,9 +7528,9 @@ snapshots: std-env: 3.10.0 ufo: 1.6.1 unenv: 2.0.0-rc.24 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) - vite-node: 3.2.4(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) - vite-plugin-checker: 0.11.0(eslint@9.20.1(jiti@2.6.1))(optionator@0.9.4)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3)) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite-node: 3.2.4(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite-plugin-checker: 0.11.0(eslint@9.20.1(jiti@2.6.1))(optionator@0.9.4)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3)) vue: 3.5.26(typescript@5.7.3) vue-bundle-renderer: 2.2.0 transitivePeerDependencies: @@ -7375,6 +7633,8 @@ snapshots: transitivePeerDependencies: - encoding + '@one-ini/wasm@0.1.1': {} + '@openapitools/openapi-generator-cli@2.16.3': dependencies: '@nestjs/axios': 3.1.3(@nestjs/common@10.4.15(reflect-metadata@0.1.13)(rxjs@7.8.1))(axios@1.7.9)(rxjs@7.8.1) @@ -7952,12 +8212,12 @@ snapshots: postcss: 8.5.6 tailwindcss: 4.1.18 - '@tailwindcss/vite@4.1.18(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + '@tailwindcss/vite@4.1.18(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': dependencies: '@tailwindcss/node': 4.1.18 '@tailwindcss/oxide': 4.1.18 tailwindcss: 4.1.18 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) '@tanstack/table-core@8.21.3': {} @@ -8210,6 +8470,13 @@ snapshots: tslib: 2.8.1 optional: true + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/json-schema@7.0.15': {} @@ -8223,6 +8490,10 @@ snapshots: '@types/mdurl@2.0.0': {} + '@types/node@20.19.27': + dependencies: + undici-types: 6.21.0 + '@types/normalize-package-data@2.4.4': {} '@types/parse-path@7.1.0': @@ -8240,6 +8511,8 @@ snapshots: '@types/web-bluetooth@0.0.21': {} + '@types/whatwg-mimetype@3.0.2': {} + '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3))(eslint@9.20.1(jiti@2.6.1))(typescript@5.7.3)': dependencies: '@eslint-community/regexpp': 4.12.2 @@ -8443,24 +8716,63 @@ snapshots: - rollup - supports-color - '@vitejs/plugin-vue-jsx@5.1.2(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': + '@vitejs/plugin-vue-jsx@5.1.2(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': dependencies: '@babel/core': 7.28.5 '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.5) '@babel/plugin-transform-typescript': 7.28.5(@babel/core@7.28.5) '@rolldown/pluginutils': 1.0.0-beta.55 '@vue/babel-plugin-jsx': 2.0.1(@babel/core@7.28.5) - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) vue: 3.5.26(typescript@5.7.3) transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@6.0.3(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': + '@vitejs/plugin-vue@6.0.3(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.53 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) vue: 3.5.26(typescript@5.7.3) + '@vitest/expect@4.0.16': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.16 + '@vitest/utils': 4.0.16 + chai: 6.2.2 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.16(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))': + dependencies: + '@vitest/spy': 4.0.16 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + + '@vitest/pretty-format@4.0.16': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.16': + dependencies: + '@vitest/utils': 4.0.16 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.16': + dependencies: + '@vitest/pretty-format': 4.0.16 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.16': {} + + '@vitest/utils@4.0.16': + dependencies: + '@vitest/pretty-format': 4.0.16 + tinyrainbow: 3.0.3 + '@volar/language-core@2.4.27': dependencies: '@volar/source-map': 2.4.27 @@ -8564,14 +8876,14 @@ snapshots: dependencies: '@vue/devtools-kit': 7.7.9 - '@vue/devtools-core@7.7.9(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': + '@vue/devtools-core@7.7.9(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3))': dependencies: '@vue/devtools-kit': 7.7.9 '@vue/devtools-shared': 7.7.9 mitt: 3.0.1 nanoid: 5.1.6 pathe: 2.0.3 - vite-hot-client: 2.1.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + vite-hot-client: 2.1.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) vue: 3.5.26(typescript@5.7.3) transitivePeerDependencies: - vite @@ -8637,6 +8949,11 @@ snapshots: '@vue/shared@3.5.26': {} + '@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.26(typescript@5.7.3))': dependencies: '@types/web-bluetooth': 0.0.20 @@ -8709,6 +9026,8 @@ snapshots: dependencies: vue: 3.5.26(typescript@5.7.3) + abbrev@2.0.0: {} + abbrev@3.0.1: {} abort-controller@3.0.0: @@ -8790,6 +9109,8 @@ snapshots: dependencies: tslib: 2.8.1 + assertion-error@2.0.1: {} + ast-kit@1.4.3: dependencies: '@babel/parser': 7.28.5 @@ -8967,6 +9288,8 @@ snapshots: case-anything@3.1.2: {} + chai@6.2.2: {} + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -9040,6 +9363,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@10.0.1: {} + commander@11.1.0: {} commander@2.20.3: {} @@ -9079,6 +9404,11 @@ snapshots: confbox@0.2.2: {} + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + consola@2.15.3: {} consola@3.4.2: {} @@ -9307,6 +9637,13 @@ snapshots: optionalDependencies: wcwidth: 1.0.1 + editorconfig@1.0.4: + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.7.3 + ee-first@1.1.1: {} electron-to-chromium@1.5.267: {} @@ -9680,6 +10017,8 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + expect-type@1.3.0: {} + exsolve@1.0.8: {} external-editor@3.1.0: @@ -9688,6 +10027,8 @@ snapshots: iconv-lite: 0.4.24 tmp: 0.0.33 + fake-indexeddb@6.2.5: {} + fast-deep-equal@2.0.1: {} fast-deep-equal@3.1.3: {} @@ -9778,7 +10119,7 @@ snapshots: unicode-properties: 1.4.1 unicode-trie: 2.0.0 - fontless@0.1.0(db0@0.3.4)(ioredis@5.8.2)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): + fontless@0.1.0(db0@0.3.4)(ioredis@5.8.2)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): dependencies: consola: 3.4.2 css-tree: 3.1.0 @@ -9794,7 +10135,7 @@ snapshots: unifont: 0.6.0 unstorage: 1.17.3(db0@0.3.4)(ioredis@5.8.2) optionalDependencies: - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9978,6 +10319,12 @@ snapshots: ufo: 1.6.1 uncrypto: 0.1.3 + happy-dom@20.0.11: + dependencies: + '@types/node': 20.19.27 + '@types/whatwg-mimetype': 3.0.2 + whatwg-mimetype: 3.0.0 + has-flag@4.0.0: {} has-symbols@1.1.0: {} @@ -10076,6 +10423,8 @@ snapshots: inherits@2.0.4: {} + ini@1.3.8: {} + ini@4.1.1: {} inquirer@8.2.6: @@ -10199,6 +10548,16 @@ snapshots: jose@6.1.3: {} + js-beautify@1.15.4: + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.5.0 + js-cookie: 3.0.5 + nopt: 7.2.1 + + js-cookie@3.0.5: {} + js-tokens@4.0.0: {} js-tokens@9.0.1: {} @@ -10494,6 +10853,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 @@ -10678,6 +11041,10 @@ snapshots: node-releases@2.0.27: {} + nopt@7.2.1: + dependencies: + abbrev: 2.0.0 + nopt@8.1.0: dependencies: abbrev: 3.0.1 @@ -10723,16 +11090,16 @@ snapshots: nuxt-define@1.0.0: {} - nuxt@4.2.0(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2): + nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@20.19.27)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2): dependencies: '@dxup/nuxt': 0.2.2(magicast@0.5.1) '@nuxt/cli': 3.31.3(cac@6.7.14)(magicast@0.5.1) - '@nuxt/devtools': 2.7.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) + '@nuxt/devtools': 2.7.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)) '@nuxt/kit': 4.2.0(magicast@0.5.1) - '@nuxt/nitro-server': 4.2.0(db0@0.3.4)(ioredis@5.8.2)(magicast@0.5.1)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2))(typescript@5.7.3) + '@nuxt/nitro-server': 4.2.0(db0@0.3.4)(ioredis@5.8.2)(magicast@0.5.1)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@20.19.27)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2))(typescript@5.7.3) '@nuxt/schema': 4.2.0 '@nuxt/telemetry': 2.6.6(magicast@0.5.1) - '@nuxt/vite-builder': 4.2.0(eslint@9.20.1(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2))(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vue-tsc@2.2.2(typescript@5.7.3))(vue@3.5.26(typescript@5.7.3))(yaml@2.8.2) + '@nuxt/vite-builder': 4.2.0(@types/node@20.19.27)(eslint@9.20.1(jiti@2.6.1))(lightningcss@1.30.2)(magicast@0.5.1)(nuxt@4.2.0(@parcel/watcher@2.5.1)(@types/node@20.19.27)(@vue/compiler-sfc@3.5.26)(cac@6.7.14)(db0@0.3.4)(eslint@9.20.1(jiti@2.6.1))(ioredis@5.8.2)(lightningcss@1.30.2)(magicast@0.5.1)(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3))(yaml@2.8.2))(optionator@0.9.4)(rollup@4.54.0)(terser@5.44.1)(typescript@5.7.3)(vue-tsc@2.2.2(typescript@5.7.3))(vue@3.5.26(typescript@5.7.3))(yaml@2.8.2) '@unhead/vue': 2.0.19(vue@3.5.26(typescript@5.7.3)) '@vue/shared': 3.5.26 c12: 3.3.3(magicast@0.5.1) @@ -10784,6 +11151,7 @@ snapshots: vue-router: 4.6.4(vue@3.5.26(typescript@5.7.3)) optionalDependencies: '@parcel/watcher': 2.5.1 + '@types/node': 20.19.27 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -10854,6 +11222,8 @@ snapshots: oauth4webapi@3.8.3: {} + obug@2.1.1: {} + ofetch@1.5.1: dependencies: destr: 2.0.5 @@ -11418,6 +11788,8 @@ snapshots: prosemirror-state: 1.4.4 prosemirror-transform: 1.10.5 + proto-list@1.2.4: {} + protocols@2.0.2: {} proxy-agent@6.5.0: @@ -11715,6 +12087,8 @@ snapshots: shell-quote@1.8.3: {} + siginfo@2.0.0: {} + signal-exit@3.0.7: {} signal-exit@4.1.0: {} @@ -11794,6 +12168,8 @@ snapshots: stable-hash-x@0.2.0: {} + stackback@0.0.2: {} + standard-as-callback@2.1.0: {} state-local@1.0.7: {} @@ -11935,6 +12311,8 @@ snapshots: tiny-invariant@1.3.3: {} + tinybench@2.9.0: {} + tinyexec@1.0.2: {} tinyglobby@0.2.15: @@ -11942,6 +12320,8 @@ snapshots: fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 + tinyrainbow@3.0.3: {} + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 @@ -12011,6 +12391,8 @@ snapshots: magic-string: 0.30.21 unplugin: 2.3.11 + undici-types@6.21.0: {} + unenv@2.0.0-rc.24: dependencies: pathe: 2.0.3 @@ -12261,23 +12643,23 @@ snapshots: transitivePeerDependencies: - '@vue/composition-api' - vite-dev-rpc@1.1.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): + vite-dev-rpc@1.1.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): dependencies: birpc: 2.9.0 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) - vite-hot-client: 2.1.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite-hot-client: 2.1.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) - vite-hot-client@2.1.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): + vite-hot-client@2.1.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): dependencies: - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) - vite-node@3.2.4(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): + vite-node@3.2.4(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): dependencies: cac: 6.7.14 debug: 4.4.3 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - jiti @@ -12292,7 +12674,7 @@ snapshots: - tsx - yaml - vite-plugin-checker@0.11.0(eslint@9.20.1(jiti@2.6.1))(optionator@0.9.4)(typescript@5.7.3)(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3)): + vite-plugin-checker@0.11.0(eslint@9.20.1(jiti@2.6.1))(optionator@0.9.4)(typescript@5.7.3)(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue-tsc@2.2.2(typescript@5.7.3)): dependencies: '@babel/code-frame': 7.27.1 chokidar: 4.0.3 @@ -12301,7 +12683,7 @@ snapshots: picomatch: 4.0.3 tiny-invariant: 1.3.3 tinyglobby: 0.2.15 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) vscode-uri: 3.1.0 optionalDependencies: eslint: 9.20.1(jiti@2.6.1) @@ -12309,7 +12691,7 @@ snapshots: typescript: 5.7.3 vue-tsc: 2.2.2(typescript@5.7.3) - vite-plugin-inspect@11.3.3(@nuxt/kit@3.20.2(magicast@0.3.5))(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): + vite-plugin-inspect@11.3.3(@nuxt/kit@3.20.2(magicast@0.3.5))(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): dependencies: ansis: 4.2.0 debug: 4.4.3 @@ -12319,24 +12701,24 @@ snapshots: perfect-debounce: 2.0.0 sirv: 3.0.2 unplugin-utils: 0.3.1 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) - vite-dev-rpc: 1.1.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite-dev-rpc: 1.1.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) optionalDependencies: '@nuxt/kit': 3.20.2(magicast@0.3.5) transitivePeerDependencies: - supports-color - vite-plugin-vue-tracer@1.2.0(vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)): + vite-plugin-vue-tracer@1.2.0(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2))(vue@3.5.26(typescript@5.7.3)): dependencies: estree-walker: 3.0.3 exsolve: 1.0.8 magic-string: 0.30.21 pathe: 2.0.3 source-map-js: 1.2.1 - vite: 7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) vue: 3.5.26(typescript@5.7.3) - vite@7.3.0(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): + vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): dependencies: esbuild: 0.27.2 fdir: 6.5.0(picomatch@4.0.3) @@ -12345,18 +12727,76 @@ snapshots: rollup: 4.54.0 tinyglobby: 0.2.15 optionalDependencies: + '@types/node': 20.19.27 fsevents: 2.3.3 jiti: 2.6.1 lightningcss: 1.30.2 terser: 5.44.1 yaml: 2.8.2 + vitest-environment-nuxt@1.0.1(@vue/test-utils@2.4.6)(happy-dom@20.0.11)(magicast@0.5.1)(typescript@5.7.3)(vitest@4.0.16(@types/node@20.19.27)(happy-dom@20.0.11)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)): + dependencies: + '@nuxt/test-utils': 3.21.0(@vue/test-utils@2.4.6)(happy-dom@20.0.11)(magicast@0.5.1)(typescript@5.7.3)(vitest@4.0.16(@types/node@20.19.27)(happy-dom@20.0.11)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + transitivePeerDependencies: + - '@cucumber/cucumber' + - '@jest/globals' + - '@playwright/test' + - '@testing-library/vue' + - '@vitest/ui' + - '@vue/test-utils' + - happy-dom + - jsdom + - magicast + - playwright-core + - typescript + - vitest + + vitest@4.0.16(@types/node@20.19.27)(happy-dom@20.0.11)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2): + dependencies: + '@vitest/expect': 4.0.16 + '@vitest/mocker': 4.0.16(vite@7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2)) + '@vitest/pretty-format': 4.0.16 + '@vitest/runner': 4.0.16 + '@vitest/snapshot': 4.0.16 + '@vitest/spy': 4.0.16 + '@vitest/utils': 4.0.16 + es-module-lexer: 1.7.0 + expect-type: 1.3.0 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.3.0(@types/node@20.19.27)(jiti@2.6.1)(lightningcss@1.30.2)(terser@5.44.1)(yaml@2.8.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.19.27 + happy-dom: 20.0.11 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - 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.2.1: {} vue-demi@0.14.10(vue@3.5.26(typescript@5.7.3)): @@ -12416,6 +12856,8 @@ snapshots: webpack-virtual-modules@0.6.2: {} + whatwg-mimetype@3.0.0: {} + whatwg-url@5.0.0: dependencies: tr46: 0.0.3 @@ -12431,6 +12873,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@6.2.0: diff --git a/legalconsenthub/test/unit/formDiff.spec.ts b/legalconsenthub/test/unit/formDiff.spec.ts new file mode 100644 index 0000000..729cc07 --- /dev/null +++ b/legalconsenthub/test/unit/formDiff.spec.ts @@ -0,0 +1,1074 @@ +import { describe, it, expect } from 'vitest' +import { compareApplicationFormValues, groupChangesBySection } from '../../app/utils/formDiff' +import type { + ApplicationFormDto, + ApplicationFormSnapshotDto, + FormElementDto, + FormElementSnapshotDto, + FormOptionDto, + FormElementSectionDto, + FormElementSubSectionDto, + FormElementSectionSnapshotDto, + FormElementSubSectionSnapshotDto, + FormElementType +} from '../../.api-client' + +// Helper to create a minimal FormOptionDto +function createOption(value: string, label: string): FormOptionDto { + return { + value, + label, + processingPurpose: 'NONE', + employeeDataCategory: 'NONE' + } +} + +// Helper to create a FormElementDto +function createFormElement( + reference: string, + title: string, + type: FormElementType, + options: FormOptionDto[] +): FormElementDto { + return { + id: `id-${reference}`, + reference, + title, + type, + options + } +} + +// Helper to create a FormElementSnapshotDto +function createSnapshotElement( + reference: string, + title: string, + type: FormElementType, + options: FormOptionDto[] +): FormElementSnapshotDto { + return { + reference, + title, + type, + options + } +} + +// Helper to create an ApplicationFormDto with a single element +function createForm(elements: FormElementDto[], sectionTitle = 'Test Section'): ApplicationFormDto { + const subSection: FormElementSubSectionDto = { + id: 'subsection-1', + title: 'Subsection', + formElements: elements + } + + const section: FormElementSectionDto = { + id: 'section-1', + title: sectionTitle, + formElementSubSections: [subSection] + } + + return { + id: 'form-1', + name: 'Test Form', + isTemplate: false, + formElementSections: [section] + } +} + +// Helper to create an ApplicationFormSnapshotDto with a single element +function createSnapshot(elements: FormElementSnapshotDto[], sectionTitle = 'Test Section'): ApplicationFormSnapshotDto { + const subSection: FormElementSubSectionSnapshotDto = { + title: 'Subsection', + elements + } + + const section: FormElementSectionSnapshotDto = { + title: sectionTitle, + subsections: [subSection] + } + + return { + name: 'Test Form', + status: 'DRAFT', + organizationId: 'org-1', + sections: [section] + } +} + +describe('formDiff', () => { + describe('compareApplicationFormValues', () => { + describe('TEXTFIELD element', () => { + it('should detect new answer (empty → filled)', () => { + const current = createForm([ + createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John Doe', '')]) + ]) + const version = createSnapshot([ + createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].elementTitle).toBe('Name') + expect(diff.newAnswers[0].currentLabel).toBe('John Doe') + expect(diff.newAnswers[0].previousLabel).toBeNull() + expect(diff.changedAnswers).toHaveLength(0) + expect(diff.clearedAnswers).toHaveLength(0) + }) + + it('should detect changed answer', () => { + const current = createForm([ + createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('Jane Doe', '')]) + ]) + const version = createSnapshot([ + createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John Doe', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].previousLabel).toBe('John Doe') + expect(diff.changedAnswers[0].currentLabel).toBe('Jane Doe') + expect(diff.newAnswers).toHaveLength(0) + expect(diff.clearedAnswers).toHaveLength(0) + }) + + it('should detect cleared answer (filled → empty)', () => { + const current = createForm([createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('', '')])]) + const version = createSnapshot([ + createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John Doe', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].previousLabel).toBe('John Doe') + expect(diff.clearedAnswers[0].currentLabel).toBeNull() + expect(diff.newAnswers).toHaveLength(0) + expect(diff.changedAnswers).toHaveLength(0) + }) + + it('should ignore unchanged values', () => { + const current = createForm([ + createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John Doe', '')]) + ]) + const version = createSnapshot([ + createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John Doe', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(0) + expect(diff.changedAnswers).toHaveLength(0) + expect(diff.clearedAnswers).toHaveLength(0) + }) + }) + + describe('TEXTAREA element', () => { + it('should detect new answer', () => { + const current = createForm([ + createFormElement('textarea_1', 'Description', 'TEXTAREA', [ + createOption('This is a long description text.', '') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('textarea_1', 'Description', 'TEXTAREA', [createOption('', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('This is a long description text.') + }) + + it('should detect changed answer', () => { + const current = createForm([ + createFormElement('textarea_1', 'Description', 'TEXTAREA', [createOption('Updated text', '')]) + ]) + const version = createSnapshot([ + createSnapshotElement('textarea_1', 'Description', 'TEXTAREA', [createOption('Original text', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].previousLabel).toBe('Original text') + expect(diff.changedAnswers[0].currentLabel).toBe('Updated text') + }) + + it('should detect cleared answer', () => { + const current = createForm([createFormElement('textarea_1', 'Description', 'TEXTAREA', [createOption('', '')])]) + const version = createSnapshot([ + createSnapshotElement('textarea_1', 'Description', 'TEXTAREA', [createOption('Some text', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].previousLabel).toBe('Some text') + }) + }) + + describe('RICH_TEXT element', () => { + it('should detect new answer', () => { + const current = createForm([ + createFormElement('richtext_1', 'Notes', 'RICH_TEXT', [ + createOption('

Rich text content

', '') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('richtext_1', 'Notes', 'RICH_TEXT', [createOption('', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('

Rich text content

') + }) + + it('should detect changed answer', () => { + const current = createForm([ + createFormElement('richtext_1', 'Notes', 'RICH_TEXT', [createOption('

Updated

', '')]) + ]) + const version = createSnapshot([ + createSnapshotElement('richtext_1', 'Notes', 'RICH_TEXT', [createOption('

Original

', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].previousLabel).toBe('

Original

') + expect(diff.changedAnswers[0].currentLabel).toBe('

Updated

') + }) + + it('should detect cleared answer', () => { + const current = createForm([createFormElement('richtext_1', 'Notes', 'RICH_TEXT', [createOption('', '')])]) + const version = createSnapshot([ + createSnapshotElement('richtext_1', 'Notes', 'RICH_TEXT', [createOption('

Content

', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + }) + }) + + describe('DATE element', () => { + it('should detect new answer', () => { + const current = createForm([ + createFormElement('date_1', 'Start Date', 'DATE', [createOption('2024-01-15', '')]) + ]) + const version = createSnapshot([createSnapshotElement('date_1', 'Start Date', 'DATE', [createOption('', '')])]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('2024-01-15') + }) + + it('should detect changed answer', () => { + const current = createForm([ + createFormElement('date_1', 'Start Date', 'DATE', [createOption('2024-02-20', '')]) + ]) + const version = createSnapshot([ + createSnapshotElement('date_1', 'Start Date', 'DATE', [createOption('2024-01-15', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].previousLabel).toBe('2024-01-15') + expect(diff.changedAnswers[0].currentLabel).toBe('2024-02-20') + }) + + it('should detect cleared answer', () => { + const current = createForm([createFormElement('date_1', 'Start Date', 'DATE', [createOption('', '')])]) + const version = createSnapshot([ + createSnapshotElement('date_1', 'Start Date', 'DATE', [createOption('2024-01-15', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + }) + }) + + describe('SELECT element', () => { + it('should detect new answer (nothing selected → option selected)', () => { + const current = createForm([ + createFormElement('select_1', 'Priority', 'SELECT', [ + createOption('false', 'Low'), + createOption('true', 'Medium'), + createOption('false', 'High') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('select_1', 'Priority', 'SELECT', [ + createOption('false', 'Low'), + createOption('false', 'Medium'), + createOption('false', 'High') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('Medium') + expect(diff.newAnswers[0].previousLabel).toBeNull() + }) + + it('should detect changed answer (different option selected)', () => { + const current = createForm([ + createFormElement('select_1', 'Priority', 'SELECT', [ + createOption('false', 'Low'), + createOption('false', 'Medium'), + createOption('true', 'High') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('select_1', 'Priority', 'SELECT', [ + createOption('true', 'Low'), + createOption('false', 'Medium'), + createOption('false', 'High') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].previousLabel).toBe('Low') + expect(diff.changedAnswers[0].currentLabel).toBe('High') + }) + + it('should detect cleared answer (option selected → nothing selected)', () => { + const current = createForm([ + createFormElement('select_1', 'Priority', 'SELECT', [ + createOption('false', 'Low'), + createOption('false', 'Medium'), + createOption('false', 'High') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('select_1', 'Priority', 'SELECT', [ + createOption('false', 'Low'), + createOption('true', 'Medium'), + createOption('false', 'High') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].previousLabel).toBe('Medium') + expect(diff.clearedAnswers[0].currentLabel).toBeNull() + }) + }) + + describe('RADIOBUTTON element', () => { + it('should detect new answer', () => { + const current = createForm([ + createFormElement('radio_1', 'Gender', 'RADIOBUTTON', [ + createOption('true', 'Male'), + createOption('false', 'Female'), + createOption('false', 'Other') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('radio_1', 'Gender', 'RADIOBUTTON', [ + createOption('false', 'Male'), + createOption('false', 'Female'), + createOption('false', 'Other') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('Male') + }) + + it('should detect changed answer', () => { + const current = createForm([ + createFormElement('radio_1', 'Gender', 'RADIOBUTTON', [ + createOption('false', 'Male'), + createOption('true', 'Female'), + createOption('false', 'Other') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('radio_1', 'Gender', 'RADIOBUTTON', [ + createOption('true', 'Male'), + createOption('false', 'Female'), + createOption('false', 'Other') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].previousLabel).toBe('Male') + expect(diff.changedAnswers[0].currentLabel).toBe('Female') + }) + + it('should detect cleared answer', () => { + const current = createForm([ + createFormElement('radio_1', 'Gender', 'RADIOBUTTON', [ + createOption('false', 'Male'), + createOption('false', 'Female'), + createOption('false', 'Other') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('radio_1', 'Gender', 'RADIOBUTTON', [ + createOption('false', 'Male'), + createOption('false', 'Female'), + createOption('true', 'Other') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].previousLabel).toBe('Other') + }) + }) + + describe('CHECKBOX element', () => { + it('should detect new answer (single checkbox selected)', () => { + const current = createForm([ + createFormElement('checkbox_1', 'Features', 'CHECKBOX', [ + createOption('true', 'Feature A'), + createOption('false', 'Feature B'), + createOption('false', 'Feature C') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('checkbox_1', 'Features', 'CHECKBOX', [ + createOption('false', 'Feature A'), + createOption('false', 'Feature B'), + createOption('false', 'Feature C') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('Feature A') + }) + + it('should detect new answer (multiple checkboxes selected)', () => { + const current = createForm([ + createFormElement('checkbox_1', 'Features', 'CHECKBOX', [ + createOption('true', 'Feature A'), + createOption('true', 'Feature B'), + createOption('false', 'Feature C') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('checkbox_1', 'Features', 'CHECKBOX', [ + createOption('false', 'Feature A'), + createOption('false', 'Feature B'), + createOption('false', 'Feature C') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('Feature A, Feature B') + }) + + it('should detect changed answer (different checkboxes selected)', () => { + const current = createForm([ + createFormElement('checkbox_1', 'Features', 'CHECKBOX', [ + createOption('false', 'Feature A'), + createOption('true', 'Feature B'), + createOption('true', 'Feature C') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('checkbox_1', 'Features', 'CHECKBOX', [ + createOption('true', 'Feature A'), + createOption('false', 'Feature B'), + createOption('false', 'Feature C') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].previousLabel).toBe('Feature A') + expect(diff.changedAnswers[0].currentLabel).toBe('Feature B, Feature C') + }) + + it('should detect cleared answer (all checkboxes deselected)', () => { + const current = createForm([ + createFormElement('checkbox_1', 'Features', 'CHECKBOX', [ + createOption('false', 'Feature A'), + createOption('false', 'Feature B'), + createOption('false', 'Feature C') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('checkbox_1', 'Features', 'CHECKBOX', [ + createOption('true', 'Feature A'), + createOption('true', 'Feature B'), + createOption('false', 'Feature C') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].previousLabel).toBe('Feature A, Feature B') + }) + }) + + describe('SWITCH element', () => { + it('should detect new answer (switch turned on)', () => { + const current = createForm([ + createFormElement('switch_1', 'Enable Notifications', 'SWITCH', [createOption('true', 'Enabled')]) + ]) + const version = createSnapshot([ + createSnapshotElement('switch_1', 'Enable Notifications', 'SWITCH', [createOption('false', 'Enabled')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('Enabled') + }) + + it('should detect cleared answer (switch turned off)', () => { + const current = createForm([ + createFormElement('switch_1', 'Enable Notifications', 'SWITCH', [createOption('false', 'Enabled')]) + ]) + const version = createSnapshot([ + createSnapshotElement('switch_1', 'Enable Notifications', 'SWITCH', [createOption('true', 'Enabled')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].previousLabel).toBe('Enabled') + }) + }) + + describe('TABLE element', () => { + it('should detect new rows added', () => { + const current = createForm([ + createFormElement('table_1', 'Employees', 'TABLE', [ + createOption('["John", "Jane"]', 'Name'), + createOption('["Developer", "Designer"]', 'Role') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('table_1', 'Employees', 'TABLE', [ + createOption('[]', 'Name'), + createOption('[]', 'Role') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('2 Zeilen') + expect(diff.newAnswers[0].tableDiff).toBeDefined() + expect(diff.newAnswers[0].tableDiff!.addedCount).toBe(2) + expect(diff.newAnswers[0].tableDiff!.removedCount).toBe(0) + expect(diff.newAnswers[0].tableDiff!.modifiedCount).toBe(0) + }) + + it('should detect rows removed', () => { + const current = createForm([ + createFormElement('table_1', 'Employees', 'TABLE', [createOption('[]', 'Name'), createOption('[]', 'Role')]) + ]) + const version = createSnapshot([ + createSnapshotElement('table_1', 'Employees', 'TABLE', [ + createOption('["John", "Jane"]', 'Name'), + createOption('["Developer", "Designer"]', 'Role') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].previousLabel).toBe('2 Zeilen') + expect(diff.clearedAnswers[0].tableDiff).toBeDefined() + expect(diff.clearedAnswers[0].tableDiff!.removedCount).toBe(2) + expect(diff.clearedAnswers[0].tableDiff!.addedCount).toBe(0) + }) + + it('should detect modified rows when row count changes', () => { + // Note: The diff algorithm compares by label first ("N Zeilen"). + // If row count is the same, changes are detected. If row count differs, it's a change. + const current = createForm([ + createFormElement('table_1', 'Employees', 'TABLE', [ + createOption('["John", "Janet", "Bob"]', 'Name'), + createOption('["Developer", "Designer", "Tester"]', 'Role') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('table_1', 'Employees', 'TABLE', [ + createOption('["John", "Jane"]', 'Name'), + createOption('["Developer", "Designer"]', 'Role') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].tableDiff).toBeDefined() + expect(diff.changedAnswers[0].tableDiff!.addedCount).toBe(1) + expect(diff.changedAnswers[0].tableDiff!.modifiedCount).toBe(1) + expect(diff.changedAnswers[0].tableDiff!.rows[1].changeType).toBe('modified') + expect(diff.changedAnswers[0].tableDiff!.rows[1].previousValues['Name']).toBe('Jane') + expect(diff.changedAnswers[0].tableDiff!.rows[1].currentValues['Name']).toBe('Janet') + }) + + it('should detect mixed changes (added, removed, modified)', () => { + const current = createForm([ + createFormElement('table_1', 'Employees', 'TABLE', [ + createOption('["John Updated", "New Person"]', 'Name'), + createOption('["Senior Dev", "Manager"]', 'Role') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('table_1', 'Employees', 'TABLE', [ + createOption('["John", "Jane", "Bob"]', 'Name'), + createOption('["Developer", "Designer", "Tester"]', 'Role') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].tableDiff).toBeDefined() + // Row 0: modified (John → John Updated, Developer → Senior Dev) + // Row 1: modified (Jane → New Person, Designer → Manager) + // Row 2: removed (Bob, Tester) + expect(diff.changedAnswers[0].tableDiff!.modifiedCount).toBe(2) + expect(diff.changedAnswers[0].tableDiff!.removedCount).toBe(1) + }) + + it('should handle single row correctly', () => { + const current = createForm([ + createFormElement('table_1', 'Employees', 'TABLE', [ + createOption('["John"]', 'Name'), + createOption('["Developer"]', 'Role') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('table_1', 'Employees', 'TABLE', [ + createOption('[]', 'Name'), + createOption('[]', 'Role') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].currentLabel).toBe('1 Zeile') + }) + + it('should handle boolean values in table cells', () => { + // Note: The diff algorithm compares by label first ("N Zeilen"). + // If row count is the same, no change is detected at the label level. + // To detect boolean cell changes, we need a row count change too. + const current = createForm([ + createFormElement('table_1', 'Settings', 'TABLE', [ + createOption('["Feature A", "Feature B"]', 'Name'), + createOption('[true, false]', 'Enabled') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('table_1', 'Settings', 'TABLE', [ + createOption('["Feature A"]', 'Name'), + createOption('[false]', 'Enabled') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].tableDiff!.rows[0].previousValues['Enabled']).toBe('Nein') + expect(diff.changedAnswers[0].tableDiff!.rows[0].currentValues['Enabled']).toBe('Ja') + }) + + it('should handle array values in table cells', () => { + // Note: The diff algorithm compares by label first ("N Zeilen"). + // To detect array cell changes, we need a row count change too. + const current = createForm([ + createFormElement('table_1', 'Projects', 'TABLE', [ + createOption('["Project A", "Project B"]', 'Name'), + createOption('[["Tag1", "Tag2"], ["Tag3"]]', 'Tags') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('table_1', 'Projects', 'TABLE', [ + createOption('["Project A"]', 'Name'), + createOption('[["Tag1"]]', 'Tags') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].tableDiff!.rows[0].previousValues['Tags']).toBe('Tag1') + expect(diff.changedAnswers[0].tableDiff!.rows[0].currentValues['Tags']).toBe('Tag1, Tag2') + }) + }) + + describe('element removed from form', () => { + it('should detect when element is removed and had a value', () => { + const current = createForm([]) + const version = createSnapshot([ + createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John Doe', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].elementTitle).toBe('Name') + expect(diff.clearedAnswers[0].previousLabel).toBe('John Doe') + }) + + it('should not report removed element if it had no value', () => { + const current = createForm([]) + const version = createSnapshot([ + createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.clearedAnswers).toHaveLength(0) + }) + }) + + describe('element added to form', () => { + it('should detect new element with value as new answer', () => { + const current = createForm([ + createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John Doe', '')]) + ]) + const version = createSnapshot([]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].elementTitle).toBe('Name') + expect(diff.newAnswers[0].currentLabel).toBe('John Doe') + }) + + it('should not report new element if it has no value', () => { + const current = createForm([createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('', '')])]) + const version = createSnapshot([]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(0) + }) + }) + + describe('multiple elements', () => { + it('should handle multiple elements with different change types', () => { + const current = createForm([ + createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John Doe', '')]), + createFormElement('textfield_2', 'Email', 'TEXTFIELD', [createOption('new@email.com', '')]), + createFormElement('select_1', 'Status', 'SELECT', [ + createOption('false', 'Active'), + createOption('false', 'Inactive') + ]) + ]) + const version = createSnapshot([ + createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('', '')]), + createSnapshotElement('textfield_2', 'Email', 'TEXTFIELD', [createOption('old@email.com', '')]), + createSnapshotElement('select_1', 'Status', 'SELECT', [ + createOption('true', 'Active'), + createOption('false', 'Inactive') + ]) + ]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(1) + expect(diff.newAnswers[0].elementTitle).toBe('Name') + + expect(diff.changedAnswers).toHaveLength(1) + expect(diff.changedAnswers[0].elementTitle).toBe('Email') + + expect(diff.clearedAnswers).toHaveLength(1) + expect(diff.clearedAnswers[0].elementTitle).toBe('Status') + }) + }) + + describe('multiple sections', () => { + it('should track section titles correctly', () => { + const section1: FormElementSectionDto = { + id: 'section-1', + title: 'Personal Info', + formElementSubSections: [ + { + id: 'sub-1', + title: 'Basic', + formElements: [createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('John', '')])] + } + ] + } + + const section2: FormElementSectionDto = { + id: 'section-2', + title: 'Contact Info', + formElementSubSections: [ + { + id: 'sub-2', + title: 'Email', + formElements: [ + createFormElement('textfield_2', 'Email', 'TEXTFIELD', [createOption('john@example.com', '')]) + ] + } + ] + } + + const current: ApplicationFormDto = { + id: 'form-1', + name: 'Test Form', + isTemplate: false, + formElementSections: [section1, section2] + } + + const versionSection1: FormElementSectionSnapshotDto = { + title: 'Personal Info', + subsections: [ + { + title: 'Basic', + elements: [createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('', '')])] + } + ] + } + + const versionSection2: FormElementSectionSnapshotDto = { + title: 'Contact Info', + subsections: [ + { + title: 'Email', + elements: [createSnapshotElement('textfield_2', 'Email', 'TEXTFIELD', [createOption('', '')])] + } + ] + } + + const version: ApplicationFormSnapshotDto = { + name: 'Test Form', + status: 'DRAFT', + organizationId: 'org-1', + sections: [versionSection1, versionSection2] + } + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(2) + expect(diff.newAnswers.find((c) => c.sectionTitle === 'Personal Info')).toBeDefined() + expect(diff.newAnswers.find((c) => c.sectionTitle === 'Contact Info')).toBeDefined() + }) + }) + + describe('edge cases', () => { + it('should handle empty forms', () => { + const current = createForm([]) + const version = createSnapshot([]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(0) + expect(diff.changedAnswers).toHaveLength(0) + expect(diff.clearedAnswers).toHaveLength(0) + }) + + it('should handle elements without reference', () => { + const elementWithoutRef: FormElementDto = { + id: 'id-1', + title: 'No Reference', + type: 'TEXTFIELD', + options: [createOption('value', '')] + } + + const current = createForm([elementWithoutRef]) + const version = createSnapshot([]) + + const diff = compareApplicationFormValues(current, version) + + // Element without reference should be ignored + expect(diff.newAnswers).toHaveLength(0) + }) + + it('should handle whitespace-only text values as empty', () => { + const current = createForm([createFormElement('textfield_1', 'Name', 'TEXTFIELD', [createOption(' ', '')])]) + const version = createSnapshot([ + createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [createOption('', '')]) + ]) + + const diff = compareApplicationFormValues(current, version) + + // Whitespace-only should be treated as empty + expect(diff.newAnswers).toHaveLength(0) + expect(diff.changedAnswers).toHaveLength(0) + expect(diff.clearedAnswers).toHaveLength(0) + }) + + it('should handle null/undefined options gracefully', () => { + const current = createForm([createFormElement('textfield_1', 'Name', 'TEXTFIELD', [])]) + const version = createSnapshot([createSnapshotElement('textfield_1', 'Name', 'TEXTFIELD', [])]) + + const diff = compareApplicationFormValues(current, version) + + expect(diff.newAnswers).toHaveLength(0) + expect(diff.changedAnswers).toHaveLength(0) + expect(diff.clearedAnswers).toHaveLength(0) + }) + }) + }) + + describe('groupChangesBySection', () => { + it('should group changes by section title', () => { + const current: ApplicationFormDto = { + id: 'form-1', + name: 'Test Form', + isTemplate: false, + formElementSections: [ + { + id: 'section-1', + title: 'Section A', + formElementSubSections: [ + { + id: 'sub-1', + title: 'Sub', + formElements: [ + createFormElement('text_1', 'Field 1', 'TEXTFIELD', [createOption('Value 1', '')]), + createFormElement('text_2', 'Field 2', 'TEXTFIELD', [createOption('Value 2', '')]) + ] + } + ] + }, + { + id: 'section-2', + title: 'Section B', + formElementSubSections: [ + { + id: 'sub-2', + title: 'Sub', + formElements: [createFormElement('text_3', 'Field 3', 'TEXTFIELD', [createOption('Value 3', '')])] + } + ] + } + ] + } + + const version: ApplicationFormSnapshotDto = { + name: 'Test Form', + status: 'DRAFT', + organizationId: 'org-1', + sections: [ + { + title: 'Section A', + subsections: [ + { + title: 'Sub', + elements: [ + createSnapshotElement('text_1', 'Field 1', 'TEXTFIELD', [createOption('', '')]), + createSnapshotElement('text_2', 'Field 2', 'TEXTFIELD', [createOption('', '')]) + ] + } + ] + }, + { + title: 'Section B', + subsections: [ + { + title: 'Sub', + elements: [createSnapshotElement('text_3', 'Field 3', 'TEXTFIELD', [createOption('', '')])] + } + ] + } + ] + } + + const diff = compareApplicationFormValues(current, version) + const grouped = groupChangesBySection(diff) + + expect(grouped).toHaveLength(2) + + const sectionA = grouped.find((g) => g.sectionTitle === 'Section A') + expect(sectionA).toBeDefined() + expect(sectionA!.changes).toHaveLength(2) + + const sectionB = grouped.find((g) => g.sectionTitle === 'Section B') + expect(sectionB).toBeDefined() + expect(sectionB!.changes).toHaveLength(1) + }) + + it('should return empty array when no changes', () => { + const diff = { + newAnswers: [], + changedAnswers: [], + clearedAnswers: [] + } + + const grouped = groupChangesBySection(diff) + + expect(grouped).toHaveLength(0) + }) + + it('should combine all change types in the same section', () => { + const current: ApplicationFormDto = { + id: 'form-1', + name: 'Test Form', + isTemplate: false, + formElementSections: [ + { + id: 'section-1', + title: 'Section A', + formElementSubSections: [ + { + id: 'sub-1', + title: 'Sub', + formElements: [ + createFormElement('text_1', 'New Field', 'TEXTFIELD', [createOption('New Value', '')]), + createFormElement('text_2', 'Changed Field', 'TEXTFIELD', [createOption('Updated', '')]), + createFormElement('text_3', 'Cleared Field', 'TEXTFIELD', [createOption('', '')]) + ] + } + ] + } + ] + } + + const version: ApplicationFormSnapshotDto = { + name: 'Test Form', + status: 'DRAFT', + organizationId: 'org-1', + sections: [ + { + title: 'Section A', + subsections: [ + { + title: 'Sub', + elements: [ + createSnapshotElement('text_1', 'New Field', 'TEXTFIELD', [createOption('', '')]), + createSnapshotElement('text_2', 'Changed Field', 'TEXTFIELD', [createOption('Original', '')]), + createSnapshotElement('text_3', 'Cleared Field', 'TEXTFIELD', [createOption('Was Here', '')]) + ] + } + ] + } + ] + } + + const diff = compareApplicationFormValues(current, version) + const grouped = groupChangesBySection(diff) + + expect(grouped).toHaveLength(1) + expect(grouped[0].sectionTitle).toBe('Section A') + expect(grouped[0].changes).toHaveLength(3) + }) + }) +}) diff --git a/legalconsenthub/vitest.config.ts b/legalconsenthub/vitest.config.ts new file mode 100644 index 0000000..d7b7e32 --- /dev/null +++ b/legalconsenthub/vitest.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from 'vitest/config' +import { defineVitestProject } from '@nuxt/test-utils/config' + +export default defineConfig({ + test: { + projects: [ + { + test: { + name: 'unit', + include: ['test/{e2e,unit}/*.{test,spec}.ts'], + environment: 'node' + } + }, + await defineVitestProject({ + test: { + name: 'nuxt', + include: ['test/nuxt/*.{test,spec}.ts'], + environment: 'nuxt' + } + }) + ] + } +})