From 8c554a38fd6c30d8860a6ec3c7870006dd199681 Mon Sep 17 00:00:00 2001 From: Valentyne Stigloher Date: Sun, 11 Aug 2024 12:22:05 +0200 Subject: [PATCH] (test) use nuxt environment for integrated pages testing --- app/router.d.ts | 5 ++ app/router.options.ts | 5 ++ nuxt.config.ts | 2 +- package.json | 1 + plugins/rollup/suml.ts | 2 +- plugins/rollup/tsv.ts | 2 +- pnpm-lock.yaml | 94 +++++++++++++++++++++++++++++-- server/loader.ts | 2 +- src/tsv.ts | 2 +- test/locales/translations.test.ts | 24 ++++++-- vitest.config.ts | 5 ++ 11 files changed, 129 insertions(+), 15 deletions(-) create mode 100644 app/router.d.ts diff --git a/app/router.d.ts b/app/router.d.ts new file mode 100644 index 000000000..abaf88164 --- /dev/null +++ b/app/router.d.ts @@ -0,0 +1,5 @@ +import type { RouteRecordRaw } from 'vue-router'; + +declare global { + var originalRoutes: readonly RouteRecordRaw[]; +} diff --git a/app/router.options.ts b/app/router.options.ts index 38018859a..c8f299614 100644 --- a/app/router.options.ts +++ b/app/router.options.ts @@ -2,6 +2,11 @@ import type { RouterOptions } from '@nuxt/schema'; const routerOptions: RouterOptions = { routes: (routes) => { + if (process.env.TEST) { + // workaround as there is no way to retrieve the original page routes + // which get overridden in the nuxt test environment by this router options + global.originalRoutes = routes; + } const config = useConfig(); return routes.flatMap((route) => { if (route.meta?.translatedPaths) { diff --git a/nuxt.config.ts b/nuxt.config.ts index 6b54b574b..4ab8bff4f 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -1,7 +1,7 @@ import './src/dotenv.ts'; import { loadSuml } from './server/loader.ts'; -import fs from 'fs'; +import fs from 'node:fs'; import path from 'path'; import { defineNuxtConfig } from 'nuxt/config'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; diff --git a/package.json b/package.json index ef1cc2fab..c4ca86b01 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "@typescript-eslint/eslint-plugin": "^7.14.1", "@typescript-eslint/parser": "^7.14.1", "@vitest/coverage-v8": "^2.0.3", + "@vue/test-utils": "^2.4.6", "avris-daemonise": "^0.0.2", "bootstrap": "^5.3.1", "clipboard": "^2.0.6", diff --git a/plugins/rollup/suml.ts b/plugins/rollup/suml.ts index c9d60fbb7..9b77c6a14 100644 --- a/plugins/rollup/suml.ts +++ b/plugins/rollup/suml.ts @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'node:fs'; import Suml from 'suml'; import type { TransformResult, Plugin } from 'rollup'; diff --git a/plugins/rollup/tsv.ts b/plugins/rollup/tsv.ts index 2e3965c2d..f3d89197f 100644 --- a/plugins/rollup/tsv.ts +++ b/plugins/rollup/tsv.ts @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'node:fs'; import Papa from 'papaparse'; import type { TransformResult, Plugin } from 'rollup'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cf19ffaf5..60cefe097 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -222,7 +222,7 @@ devDependencies: version: git/gitlab.com+Avris/FontAwesomePro/f00db606f659dca78b143b7bcab5671b2cb459a8 '@nuxt/test-utils': specifier: ^3.14.1 - version: 3.14.1(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3) + version: 3.14.1(@vue/test-utils@2.4.6)(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3) '@rollup/plugin-replace': specifier: ^5.0.7 version: 5.0.7(rollup@4.21.0) @@ -313,6 +313,9 @@ devDependencies: '@vitest/coverage-v8': specifier: ^2.0.3 version: 2.0.5(vitest@2.0.5) + '@vue/test-utils': + specifier: ^2.4.6 + version: 2.4.6 avris-daemonise: specifier: ^0.0.2 version: 0.0.2 @@ -3877,7 +3880,7 @@ packages: - webpack-sources dev: false - /@nuxt/test-utils@3.14.1(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3): + /@nuxt/test-utils@3.14.1(@vue/test-utils@2.4.6)(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3): resolution: {integrity: sha512-D8F18hnOHQSarKnzsLORRXzFPlI9Y5fcQFRKwJgGhnejlIRX6sFvVnyl2SDgCvoV+F2x2czQsdGkwg51iWAshA==} engines: {node: '>=18.20.4'} peerDependencies: @@ -3920,6 +3923,7 @@ packages: dependencies: '@nuxt/kit': 3.13.1(magicast@0.3.4)(rollup@4.21.0) '@nuxt/schema': 3.13.1(rollup@4.21.0) + '@vue/test-utils': 2.4.6 c12: 1.11.2(magicast@0.3.4) consola: 3.2.3 defu: 6.1.4 @@ -3945,7 +3949,7 @@ packages: unplugin: 1.13.1 vite: 5.4.3(@types/node@20.11.5)(sass@1.32.12) vitest: 2.0.5(@types/node@20.11.5)(jsdom@24.1.3)(sass@1.32.12) - vitest-environment-nuxt: 1.0.1(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3) + vitest-environment-nuxt: 1.0.1(@vue/test-utils@2.4.6)(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3) vue: 3.5.3(typescript@5.5.2) vue-router: 4.4.3(vue@3.5.3) transitivePeerDependencies: @@ -4033,6 +4037,10 @@ packages: - webpack-sources dev: false + /@one-ini/wasm@0.1.1: + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + dev: true + /@parcel/watcher-android-arm64@2.4.1: resolution: {integrity: sha512-LOi/WTbbh3aTn2RYddrO8pnapixAziFl6SMxHM69r3tvdSm94JtCenaKgk1GRg5FJ5wpMCpHeW+7yqPlvZv7kg==} engines: {node: '>= 10.0.0'} @@ -6272,6 +6280,13 @@ packages: /@vue/shared@3.5.3: resolution: {integrity: sha512-Jp2v8nylKBT+PlOUjun2Wp/f++TfJVFjshLzNtJDdmFJabJa7noGMncqXRM1vXGX+Yo2V7WykQFNxusSim8SCA==} + /@vue/test-utils@2.4.6: + resolution: {integrity: sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==} + dependencies: + js-beautify: 1.15.1 + vue-component-type-helpers: 2.1.6 + dev: true + /@vuepic/vue-datepicker@8.8.1(vue@3.5.3): resolution: {integrity: sha512-8ehfUz1m69Vuc16Pm4ukgb3Mg1VT14x4EsG1ag4O/qbSNRWztTo+pUV4JnFt0FGLl5gGb6NXlxIvR7EjLgD7Gg==} peerDependencies: @@ -6419,6 +6434,11 @@ packages: /abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + /abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + dev: true + /abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -7747,6 +7767,11 @@ packages: dependencies: delayed-stream: 1.0.0 + /commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + dev: true + /commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -7808,6 +7833,13 @@ packages: /confbox@0.1.7: resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + /config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + /consola@3.2.3: resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} engines: {node: ^14.18.0 || >=16.10.0} @@ -8619,6 +8651,17 @@ packages: safe-buffer: 5.2.1 dev: false + /editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.6.3 + dev: true + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -10724,7 +10767,6 @@ packages: /ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} - dev: false /ini@4.1.1: resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} @@ -11314,6 +11356,23 @@ packages: resolution: {integrity: sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA==} dev: false + /js-beautify@1.15.1: + resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==} + engines: {node: '>=14'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.4.5 + js-cookie: 3.0.5 + nopt: 7.2.1 + dev: true + + /js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + dev: true + /js-md5@0.7.3: resolution: {integrity: sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ==} dev: false @@ -12181,6 +12240,13 @@ packages: brace-expansion: 2.0.1 dev: true + /minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + dependencies: + brace-expansion: 2.0.1 + dev: true + /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} engines: {node: '>=16 || 14 >=14.17'} @@ -12664,6 +12730,14 @@ packages: dependencies: abbrev: 1.1.1 + /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 + dependencies: + abbrev: 2.0.0 + dev: true + /normalize-package-data@2.5.0: resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} dependencies: @@ -13908,6 +13982,10 @@ packages: resolution: {integrity: sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==} dev: false + /proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + dev: true + /protocols@2.0.1: resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==} dev: false @@ -16695,10 +16773,10 @@ packages: optionalDependencies: fsevents: 2.3.3 - /vitest-environment-nuxt@1.0.1(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3): + /vitest-environment-nuxt@1.0.1(@vue/test-utils@2.4.6)(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3): resolution: {integrity: sha512-eBCwtIQriXW5/M49FjqNKfnlJYlG2LWMSNFsRVKomc8CaMqmhQPBS5LZ9DlgYL9T8xIVsiA6RZn2lk7vxov3Ow==} dependencies: - '@nuxt/test-utils': 3.14.1(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3) + '@nuxt/test-utils': 3.14.1(@vue/test-utils@2.4.6)(h3@1.12.0)(jsdom@24.1.3)(nitropack@2.9.7)(rollup@4.21.0)(vite@5.4.3)(vitest@2.0.5)(vue-router@4.4.3)(vue@3.5.3) transitivePeerDependencies: - '@cucumber/cucumber' - '@jest/globals' @@ -16827,6 +16905,10 @@ packages: ufo: 1.5.4 dev: false + /vue-component-type-helpers@2.1.6: + resolution: {integrity: sha512-ng11B8B/ZADUMMOsRbqv0arc442q7lifSubD0v8oDXIFoMg/mXwAPUunrroIDkY+mcD0dHKccdaznSVp8EoX3w==} + dev: true + /vue-demi@0.14.10(vue@3.5.3): resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} engines: {node: '>=12'} diff --git a/server/loader.ts b/server/loader.ts index 008dd0999..1ffb00c92 100644 --- a/server/loader.ts +++ b/server/loader.ts @@ -1,4 +1,4 @@ -import fs from 'fs'; +import fs from 'node:fs'; import Suml from 'suml'; import { loadTsv as baseLoadTsv } from '../src/tsv.ts'; diff --git a/src/tsv.ts b/src/tsv.ts index cfe508ee0..b0da0ae1e 100644 --- a/src/tsv.ts +++ b/src/tsv.ts @@ -1,5 +1,5 @@ import Papa from 'papaparse'; -import fs from 'fs'; +import fs from 'node:fs'; export const tsvParseConfig = { dynamicTyping: true, diff --git a/test/locales/translations.test.ts b/test/locales/translations.test.ts index cdd6dfa7d..c899ac8e0 100644 --- a/test/locales/translations.test.ts +++ b/test/locales/translations.test.ts @@ -1,8 +1,13 @@ -import { beforeEach, describe, expect, test, vi } from 'vitest'; +// @vitest-environment nuxt + +import { mockNuxtImport } from '@nuxt/test-utils/runtime'; +import { beforeAll, describe, expect, test, vi } from 'vitest'; import pathToRegexp from 'path-to-regexp'; import type { NuxtPage } from '@nuxt/schema'; import type { SyncExpectationResult, MatcherState } from '@vitest/expect'; +import type { RouteRecordRaw } from 'vue-router'; +import routerOptions from '../../app/router.options.ts'; import { loadSumlFromBase } from '../../server/loader.ts'; import allLocales from '../../locale/locales.ts'; import { deepGet, deepListKeys } from '../../src/helpers.ts'; @@ -84,13 +89,22 @@ function toBeValidPath(this: MatcherState, actual: string, paths: RegExp[], key: } } +const { useConfigMock } = vi.hoisted(() => ({ + useConfigMock: vi.fn().mockImplementation(() => null), +})); + +mockNuxtImport('useConfig', () => useConfigMock); + const pathRegex = /\{(\/[^}]+?)(?:#[^}]+)?=[^}]+\}/g; describe.each(allLocales)('translations for $code', ({ code, published }) => { const config = loadSumlFromBase(`locale/${code}/config`); const translations = loadSumlFromBase(`locale/${code}/translations`) as Translations; - beforeEach(() => { + beforeAll(() => { + // for router.options.ts + useConfigMock.mockImplementation(() => config); + // for pages:extend hook in nuxt.config.ts vi.doMock('../../server/loader.ts', () => { return { loadSuml(name: string): unknown { @@ -117,8 +131,10 @@ describe.each(allLocales)('translations for $code', ({ code, published }) => { vi.resetModules(); const { default: nuxtConfig } = await import('../../nuxt.config.ts'); - const routes: NuxtPage[] = []; - nuxtConfig.hooks!['pages:extend']!(routes); + const baseRoutes = routerOptions.routes!(global.originalRoutes) as RouteRecordRaw[]; + const extendedRoutes: NuxtPage[] = []; + nuxtConfig.hooks!['pages:extend']!(extendedRoutes); + const routes = [...baseRoutes, ...extendedRoutes]; const paths = routes.filter((route) => route.name !== 'all').map((route) => pathToRegexp(route.path)); for (const key of deepListKeys(translations)) { diff --git a/vitest.config.ts b/vitest.config.ts index 0c25765ff..3f88bff0f 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,5 +7,10 @@ export default defineVitestConfig({ include: ['plugins/**', 'server/**', 'src/**', 'store/**'], reporter: ['text', 'cobertura'], }, + environmentOptions: { + nuxt: { + domEnvironment: 'jsdom', + }, + }, }, });