diff --git a/frontend/vue-app/package-lock.json b/frontend/vue-app/package-lock.json index 6c3a2f5..ce15722 100644 --- a/frontend/vue-app/package-lock.json +++ b/frontend/vue-app/package-lock.json @@ -111,6 +111,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -663,6 +664,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -706,6 +708,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -1955,6 +1958,7 @@ "integrity": "sha512-tK3GPFWbirvNgsNKto+UmB/cRtn6TZfyw0D6IKrW55n6Vbs7KJoZtI//kpTKzE/DUmmnAFD8/Ca46s7Obs92/w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.46.4", "@typescript-eslint/types": "8.46.4", @@ -2710,6 +2714,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2904,6 +2909,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.19", "caniuse-lite": "^1.0.30001751", @@ -3409,6 +3415,7 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3469,6 +3476,7 @@ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -3516,6 +3524,7 @@ "integrity": "sha512-SbR9ZBUFKgvWAbq3RrdCtWaW0IKm6wwUiApxf3BVTNfqUIo4IQQmreMg2iHFJJ6C/0wss3LXURBJ1OwS/MhFcQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "natural-compare": "^1.4.0", @@ -4195,6 +4204,7 @@ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } @@ -4972,6 +4982,7 @@ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -5542,6 +5553,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -5680,6 +5692,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -5803,6 +5816,7 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -6023,6 +6037,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -6036,6 +6051,7 @@ "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", @@ -6128,6 +6144,7 @@ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.24.tgz", "integrity": "sha512-uTHDOpVQTMjcGgrqFPSb8iO2m1DUvo+WbGqoXQz8Y1CeBYQ0FXf2z1gLRaBtHjlRz7zZUBHxjVB5VTLzYkvftg==", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.24", "@vue/compiler-sfc": "3.5.24", diff --git a/frontend/vue-app/src/__tests__/ItemList.spec.ts b/frontend/vue-app/src/__tests__/ItemList.spec.ts index 7b07c7a..c8e389d 100644 --- a/frontend/vue-app/src/__tests__/ItemList.spec.ts +++ b/frontend/vue-app/src/__tests__/ItemList.spec.ts @@ -10,14 +10,12 @@ describe('ItemList.vue', () => { it('does not show delete button for system items', async () => { const wrapper = mount(ItemList, { props: { + fetchUrl: '', itemKey: 'items', itemFields: ['name'], deletable: true, testItems: [systemItem], }, - global: { - stubs: ['svg'], - }, }) await flushPromises() expect(wrapper.find('.delete-btn').exists()).toBe(false) @@ -26,14 +24,12 @@ describe('ItemList.vue', () => { it('shows delete button for user items', async () => { const wrapper = mount(ItemList, { props: { + fetchUrl: '', itemKey: 'items', itemFields: ['name'], deletable: true, testItems: [userItem], }, - global: { - stubs: ['svg'], - }, }) await flushPromises() expect(wrapper.find('.delete-btn').exists()).toBe(true) diff --git a/frontend/vue-app/src/__tests__/UserProfile.spec.ts b/frontend/vue-app/src/__tests__/UserProfile.spec.ts index de78322..293cf0f 100644 --- a/frontend/vue-app/src/__tests__/UserProfile.spec.ts +++ b/frontend/vue-app/src/__tests__/UserProfile.spec.ts @@ -11,8 +11,8 @@ global.fetch = vi.fn() const mockRouter = createRouter({ history: createMemoryHistory(), routes: [ - { path: '/auth/login', name: 'Login' }, - { path: '/profile', name: 'UserProfile' }, + { path: '/auth/login', name: 'Login', component: { template: '
' } }, + { path: '/profile', name: 'UserProfile', component: { template: '' } }, ], }) diff --git a/frontend/vue-app/src/components/shared/__tests__/LoginButton.spec.ts b/frontend/vue-app/src/components/shared/__tests__/LoginButton.spec.ts index 5106720..d7c137b 100644 --- a/frontend/vue-app/src/components/shared/__tests__/LoginButton.spec.ts +++ b/frontend/vue-app/src/components/shared/__tests__/LoginButton.spec.ts @@ -4,6 +4,10 @@ import { nextTick } from 'vue' import LoginButton from '../LoginButton.vue' import { authenticateParent, logoutParent } from '../../../stores/auth' +vi.mock('vue-router', () => ({ + useRouter: vi.fn(() => ({ push: vi.fn() })), +})) + // Mock imageCache module vi.mock('@/common/imageCache', () => ({ getCachedImageUrl: vi.fn(async (imageId: string) => `blob:mock-url-${imageId}`), diff --git a/frontend/vue-app/src/test/setup.ts b/frontend/vue-app/src/test/setup.ts new file mode 100644 index 0000000..1f84105 --- /dev/null +++ b/frontend/vue-app/src/test/setup.ts @@ -0,0 +1,20 @@ +import { vi } from 'vitest' + +// jsdom does not implement scrollTo — stub it to suppress "Not implemented" warnings +window.scrollTo = vi.fn() + +// jsdom does not support navigation — stub window.location to suppress +// "Not implemented: navigation to another Document" warnings +Object.defineProperty(window, 'location', { + value: { href: '', pathname: '/', assign: vi.fn(), replace: vi.fn(), reload: vi.fn() }, + writable: true, +}) + +// Globally mock imageCache so component tests don't make real fetch calls +// and don't spam "response.blob is not a function" errors in jsdom. +vi.mock('@/common/imageCache', () => ({ + getCachedImageUrl: vi.fn().mockResolvedValue(''), + getCachedImageBlob: vi.fn().mockResolvedValue(new Blob()), + revokeImageUrl: vi.fn(), + revokeAllImageUrls: vi.fn(), +})) diff --git a/frontend/vue-app/vitest.config.ts b/frontend/vue-app/vitest.config.ts index c328717..4387643 100644 --- a/frontend/vue-app/vitest.config.ts +++ b/frontend/vue-app/vitest.config.ts @@ -9,6 +9,7 @@ export default mergeConfig( environment: 'jsdom', exclude: [...configDefaults.exclude, 'e2e/**'], root: fileURLToPath(new URL('./', import.meta.url)), + setupFiles: ['src/test/setup.ts'], }, }), )