From 5c3d7ce535830d2177fe8e479fc98bc8854f60f1 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:50:35 +0200 Subject: [PATCH 01/22] add LanguageSwitchButton.test.js file - empty --- src/utils/__tests__/LanguageSwitchButton.test.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/utils/__tests__/LanguageSwitchButton.test.js diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js new file mode 100644 index 0000000..e69de29 -- 2.39.5 From d5b76b0a1794d64ec1b1aa59d5c1b69a89bba204 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:51:24 +0200 Subject: [PATCH 02/22] add license --- src/utils/__tests__/LanguageSwitchButton.test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index e69de29..c5a170b 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -0,0 +1,15 @@ +/* + * Copyright 2026 Seekra + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ \ No newline at end of file -- 2.39.5 From baa1f6ed4475987064031240791e8ed2bb643be1 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:56:46 +0200 Subject: [PATCH 03/22] add imports --- src/utils/__tests__/LanguageSwitchButton.test.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index c5a170b..199970f 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -12,4 +12,9 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ \ No newline at end of file + */ + +import { expect, test, describe, vi, beforeEach } from 'vitest'; +import { mount } from '@vue/test-utils'; +import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue'; +import { loadLanguage } from '@/i18n'; -- 2.39.5 From a97ee3c66018409eeaa66c20ce5ac952bd0e492a Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:58:20 +0200 Subject: [PATCH 04/22] mock i18n and icon dependencies for language switch --- .../__tests__/LanguageSwitchButton.test.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index 199970f..ba949f2 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -18,3 +18,23 @@ import { expect, test, describe, vi, beforeEach } from 'vitest'; import { mount } from '@vue/test-utils'; import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue'; import { loadLanguage } from '@/i18n'; + +vi.mock('@/i18n', () => ({ + loadLanguage: vi.fn(() => Promise.resolve()), + LANGUAGES_RTL: ['ar', 'he'], + SUPPORTED_LANGUAGES: ['en', 'de', 'ar'] +})); + +vi.mock('vue-i18n', () => ({ + useI18n: () => ({ + t: (key) => key, + locale: { value: 'de' } + }) +})); + +vi.mock('@/features/icons/components/Icon.vue', () => ({ + default: { + name: 'Icon', + template: 'Icon' + } +})); \ No newline at end of file -- 2.39.5 From 1c3175a3e4f74cd2d5a28f331c3081ba5f8c73d9 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:58:47 +0200 Subject: [PATCH 05/22] initialize language switch test suite with beforeEach setup --- src/utils/__tests__/LanguageSwitchButton.test.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index ba949f2..edf62b1 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -37,4 +37,12 @@ vi.mock('@/features/icons/components/Icon.vue', () => ({ name: 'Icon', template: 'Icon' } -})); \ No newline at end of file +})); + +describe('LanguageSwitchButton.vue', () => { + beforeEach(() => { + vi.clearAllMocks(); + localStorage.clear(); + document.documentElement.lang = ''; + document.documentElement.dir = ''; + }); \ No newline at end of file -- 2.39.5 From e8cb978a61c9c0100038d147cde760dd2740923d Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:59:08 +0200 Subject: [PATCH 06/22] verify initial closed state and toggle opening of dropdown --- .../__tests__/LanguageSwitchButton.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index edf62b1..1867f24 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -45,4 +45,22 @@ describe('LanguageSwitchButton.vue', () => { localStorage.clear(); document.documentElement.lang = ''; document.documentElement.dir = ''; + }); + +test('renders correctly with initial state closed', () => { + const wrapper = mount(LanguageSwitchButton); + + expect(wrapper.find('.language-button').exists()).toBe(true); + expect(wrapper.find('.language-dropdown').exists()).toBe(false); + expect(wrapper.find('.language-button').attributes('aria-expanded')).toBe('false'); + }); + + test('opens the dropdown when language button is clicked', async () => { + const wrapper = mount(LanguageSwitchButton); + const button = wrapper.find('.language-button'); + + await button.trigger('click'); + + expect(wrapper.find('.language-dropdown').exists()).toBe(true); + expect(button.attributes('aria-expanded')).toBe('true'); }); \ No newline at end of file -- 2.39.5 From dc1caac2ed5cba3ebfa09f1146fa5be38eb5b77a Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:59:41 +0200 Subject: [PATCH 07/22] assert language selection updates localStorage and HTML attributes --- .../__tests__/LanguageSwitchButton.test.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index 1867f24..443c741 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -63,4 +63,27 @@ test('renders correctly with initial state closed', () => { expect(wrapper.find('.language-dropdown').exists()).toBe(true); expect(button.attributes('aria-expanded')).toBe('true'); + }); + + const languageTestCases = [ + { code: 'en', expectedDir: 'ltr' }, + { code: 'de', expectedDir: 'ltr' }, + { code: 'ar', expectedDir: 'rtl' } + ]; + + test.for(languageTestCases)('selectLanguage($code) sets localStorage, html attributes and changes layout direction to $expectedDir', async ({ code, expectedDir }) => { + const wrapper = mount(LanguageSwitchButton); + + await wrapper.find('.language-button').trigger('click'); + + const options = wrapper.findAll('.language-dropdown li'); + const optionToClick = options.find(opt => opt.text().includes(code)); + + await optionToClick.trigger('click'); + + expect(loadLanguage).toHaveBeenCalledWith(code); + expect(localStorage.getItem('locale')).toBe(code); + expect(document.documentElement.lang).toBe(code); + expect(document.documentElement.dir).toBe(expectedDir); + expect(wrapper.find('.language-dropdown').exists()).toBe(false); }); \ No newline at end of file -- 2.39.5 From 521f1becb44e0d77cbbda155628ea6126b88b445 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:59:59 +0200 Subject: [PATCH 08/22] validate dropdown closes on click outside the component --- .../__tests__/LanguageSwitchButton.test.js | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index 443c741..bbd2993 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -86,4 +86,29 @@ test('renders correctly with initial state closed', () => { expect(document.documentElement.lang).toBe(code); expect(document.documentElement.dir).toBe(expectedDir); expect(wrapper.find('.language-dropdown').exists()).toBe(false); - }); \ No newline at end of file + }); + + test('closes the dropdown when clicking outside the component', async () => { + const wrapper = mount(LanguageSwitchButton, { + attachTo: document.body + }); + + await wrapper.find('.language-button').trigger('click'); + expect(wrapper.find('.language-dropdown').exists()).toBe(true); + + await new Promise(resolve => setTimeout(resolve, 0)); + + const externalDiv = document.createElement('div'); + document.body.appendChild(externalDiv); + + const clickEvent = new MouseEvent('click', { bubbles: true }); + externalDiv.dispatchEvent(clickEvent); + + await wrapper.vm.$nextTick(); + + expect(wrapper.find('.language-dropdown').exists()).toBe(false); + + wrapper.unmount(); + externalDiv.remove(); + }); +}); \ No newline at end of file -- 2.39.5 From 924698df24a90f37f4ddc8a2ae3122bfefc0669b Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 13:33:46 +0200 Subject: [PATCH 09/22] chore: add @vue/test-utils to devDependencies --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9fe3429..3187015 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,11 @@ "vue-router": "^5.0.6" }, "devDependencies": { + "@vue/test-utils": "^2.4.6", "@vitejs/plugin-vue": "^6.0.6", "jsdom": "^29.1.1", "vite": "^8.0.10", "vite-plugin-html": "^3.2.2", "vitest": "^4.1.7" } -} +} \ No newline at end of file -- 2.39.5 From 918d6768e85c84e5dd5f9f4e416265d525e1c81a Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 13:34:48 +0200 Subject: [PATCH 10/22] test: move language switch test, fix header comments and code indentation --- .../__tests__/LanguageSwitchButton.test.js | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index bbd2993..add624c 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -1,22 +1,22 @@ /* - * Copyright 2026 Seekra - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +Copyright 2026 Seekra + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ import { expect, test, describe, vi, beforeEach } from 'vitest'; import { mount } from '@vue/test-utils'; -import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue'; +import LanguageSwitchButton from '../LanguageSwitchButton.vue'; import { loadLanguage } from '@/i18n'; vi.mock('@/i18n', () => ({ @@ -47,7 +47,7 @@ describe('LanguageSwitchButton.vue', () => { document.documentElement.dir = ''; }); -test('renders correctly with initial state closed', () => { + test('renders correctly with initial state closed', () => { const wrapper = mount(LanguageSwitchButton); expect(wrapper.find('.language-button').exists()).toBe(true); -- 2.39.5 From ebe21f7f4d9f842c7d8f11868d61dddbc726578f Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:50:35 +0200 Subject: [PATCH 11/22] add LanguageSwitchButton.test.js file - empty --- src/utils/__tests__/LanguageSwitchButton.test.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/utils/__tests__/LanguageSwitchButton.test.js diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js new file mode 100644 index 0000000..e69de29 -- 2.39.5 From 54ffaf73e3e8d5ed2b171c4848b3527cc2c09272 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:51:24 +0200 Subject: [PATCH 12/22] add license --- src/utils/__tests__/LanguageSwitchButton.test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index e69de29..c5a170b 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -0,0 +1,15 @@ +/* + * Copyright 2026 Seekra + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ \ No newline at end of file -- 2.39.5 From d6700d69657b9863bf7854c14c7f82e621173e36 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:56:46 +0200 Subject: [PATCH 13/22] add imports --- src/utils/__tests__/LanguageSwitchButton.test.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index c5a170b..199970f 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -12,4 +12,9 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ \ No newline at end of file + */ + +import { expect, test, describe, vi, beforeEach } from 'vitest'; +import { mount } from '@vue/test-utils'; +import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue'; +import { loadLanguage } from '@/i18n'; -- 2.39.5 From a6194bb8747ed226bfe36390359a1578015058bd Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:58:20 +0200 Subject: [PATCH 14/22] mock i18n and icon dependencies for language switch --- .../__tests__/LanguageSwitchButton.test.js | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index 199970f..ba949f2 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -18,3 +18,23 @@ import { expect, test, describe, vi, beforeEach } from 'vitest'; import { mount } from '@vue/test-utils'; import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue'; import { loadLanguage } from '@/i18n'; + +vi.mock('@/i18n', () => ({ + loadLanguage: vi.fn(() => Promise.resolve()), + LANGUAGES_RTL: ['ar', 'he'], + SUPPORTED_LANGUAGES: ['en', 'de', 'ar'] +})); + +vi.mock('vue-i18n', () => ({ + useI18n: () => ({ + t: (key) => key, + locale: { value: 'de' } + }) +})); + +vi.mock('@/features/icons/components/Icon.vue', () => ({ + default: { + name: 'Icon', + template: 'Icon' + } +})); \ No newline at end of file -- 2.39.5 From 02af96a07d0805cd75a2669364e9bed6311c3696 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:58:47 +0200 Subject: [PATCH 15/22] initialize language switch test suite with beforeEach setup --- src/utils/__tests__/LanguageSwitchButton.test.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index ba949f2..edf62b1 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -37,4 +37,12 @@ vi.mock('@/features/icons/components/Icon.vue', () => ({ name: 'Icon', template: 'Icon' } -})); \ No newline at end of file +})); + +describe('LanguageSwitchButton.vue', () => { + beforeEach(() => { + vi.clearAllMocks(); + localStorage.clear(); + document.documentElement.lang = ''; + document.documentElement.dir = ''; + }); \ No newline at end of file -- 2.39.5 From 0bb0f233474d77ec2852dbd99427f9ba5f1daf26 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:59:08 +0200 Subject: [PATCH 16/22] verify initial closed state and toggle opening of dropdown --- .../__tests__/LanguageSwitchButton.test.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index edf62b1..1867f24 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -45,4 +45,22 @@ describe('LanguageSwitchButton.vue', () => { localStorage.clear(); document.documentElement.lang = ''; document.documentElement.dir = ''; + }); + +test('renders correctly with initial state closed', () => { + const wrapper = mount(LanguageSwitchButton); + + expect(wrapper.find('.language-button').exists()).toBe(true); + expect(wrapper.find('.language-dropdown').exists()).toBe(false); + expect(wrapper.find('.language-button').attributes('aria-expanded')).toBe('false'); + }); + + test('opens the dropdown when language button is clicked', async () => { + const wrapper = mount(LanguageSwitchButton); + const button = wrapper.find('.language-button'); + + await button.trigger('click'); + + expect(wrapper.find('.language-dropdown').exists()).toBe(true); + expect(button.attributes('aria-expanded')).toBe('true'); }); \ No newline at end of file -- 2.39.5 From 98cc6b75568d8e1139917b0642f9a61d0504f2a4 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:59:41 +0200 Subject: [PATCH 17/22] assert language selection updates localStorage and HTML attributes --- .../__tests__/LanguageSwitchButton.test.js | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index 1867f24..443c741 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -63,4 +63,27 @@ test('renders correctly with initial state closed', () => { expect(wrapper.find('.language-dropdown').exists()).toBe(true); expect(button.attributes('aria-expanded')).toBe('true'); + }); + + const languageTestCases = [ + { code: 'en', expectedDir: 'ltr' }, + { code: 'de', expectedDir: 'ltr' }, + { code: 'ar', expectedDir: 'rtl' } + ]; + + test.for(languageTestCases)('selectLanguage($code) sets localStorage, html attributes and changes layout direction to $expectedDir', async ({ code, expectedDir }) => { + const wrapper = mount(LanguageSwitchButton); + + await wrapper.find('.language-button').trigger('click'); + + const options = wrapper.findAll('.language-dropdown li'); + const optionToClick = options.find(opt => opt.text().includes(code)); + + await optionToClick.trigger('click'); + + expect(loadLanguage).toHaveBeenCalledWith(code); + expect(localStorage.getItem('locale')).toBe(code); + expect(document.documentElement.lang).toBe(code); + expect(document.documentElement.dir).toBe(expectedDir); + expect(wrapper.find('.language-dropdown').exists()).toBe(false); }); \ No newline at end of file -- 2.39.5 From 88634e10cdf5aeea6d2792ba90b5bfc82ad25f27 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 12:59:59 +0200 Subject: [PATCH 18/22] validate dropdown closes on click outside the component --- .../__tests__/LanguageSwitchButton.test.js | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index 443c741..bbd2993 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -86,4 +86,29 @@ test('renders correctly with initial state closed', () => { expect(document.documentElement.lang).toBe(code); expect(document.documentElement.dir).toBe(expectedDir); expect(wrapper.find('.language-dropdown').exists()).toBe(false); - }); \ No newline at end of file + }); + + test('closes the dropdown when clicking outside the component', async () => { + const wrapper = mount(LanguageSwitchButton, { + attachTo: document.body + }); + + await wrapper.find('.language-button').trigger('click'); + expect(wrapper.find('.language-dropdown').exists()).toBe(true); + + await new Promise(resolve => setTimeout(resolve, 0)); + + const externalDiv = document.createElement('div'); + document.body.appendChild(externalDiv); + + const clickEvent = new MouseEvent('click', { bubbles: true }); + externalDiv.dispatchEvent(clickEvent); + + await wrapper.vm.$nextTick(); + + expect(wrapper.find('.language-dropdown').exists()).toBe(false); + + wrapper.unmount(); + externalDiv.remove(); + }); +}); \ No newline at end of file -- 2.39.5 From 3a07b0482fcf01077117e07b81859c5f81744d1d Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 13:33:46 +0200 Subject: [PATCH 19/22] chore: add @vue/test-utils to devDependencies --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 9fe3429..3187015 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,11 @@ "vue-router": "^5.0.6" }, "devDependencies": { + "@vue/test-utils": "^2.4.6", "@vitejs/plugin-vue": "^6.0.6", "jsdom": "^29.1.1", "vite": "^8.0.10", "vite-plugin-html": "^3.2.2", "vitest": "^4.1.7" } -} +} \ No newline at end of file -- 2.39.5 From 27f696247d6987ee754fbbf7c7fb998297e90729 Mon Sep 17 00:00:00 2001 From: "johannes.vos" Date: Thu, 4 Jun 2026 13:34:48 +0200 Subject: [PATCH 20/22] test: move language switch test, fix header comments and code indentation --- .../__tests__/LanguageSwitchButton.test.js | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js index bbd2993..add624c 100644 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ b/src/utils/__tests__/LanguageSwitchButton.test.js @@ -1,22 +1,22 @@ /* - * Copyright 2026 Seekra - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +Copyright 2026 Seekra + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ import { expect, test, describe, vi, beforeEach } from 'vitest'; import { mount } from '@vue/test-utils'; -import LanguageSwitchButton from '@/features/i18n/components/LanguageSwitchButton.vue'; +import LanguageSwitchButton from '../LanguageSwitchButton.vue'; import { loadLanguage } from '@/i18n'; vi.mock('@/i18n', () => ({ @@ -47,7 +47,7 @@ describe('LanguageSwitchButton.vue', () => { document.documentElement.dir = ''; }); -test('renders correctly with initial state closed', () => { + test('renders correctly with initial state closed', () => { const wrapper = mount(LanguageSwitchButton); expect(wrapper.find('.language-button').exists()).toBe(true); -- 2.39.5 From eb892d9725c06d53b19fc8c6b8039aa0007ba9fb Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Thu, 4 Jun 2026 13:43:55 +0200 Subject: [PATCH 21/22] test(language-switch-button): move test file to the correct directory --- .../i18n/components}/__tests__/LanguageSwitchButton.test.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{utils => features/i18n/components}/__tests__/LanguageSwitchButton.test.js (100%) diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/features/i18n/components/__tests__/LanguageSwitchButton.test.js similarity index 100% rename from src/utils/__tests__/LanguageSwitchButton.test.js rename to src/features/i18n/components/__tests__/LanguageSwitchButton.test.js -- 2.39.5 From dc4c47e2eff13779ae9cf2fe2c0b7bf368ab6506 Mon Sep 17 00:00:00 2001 From: Jakob Scheid Date: Thu, 4 Jun 2026 13:45:22 +0200 Subject: [PATCH 22/22] test(language-switch-button): remove redundant test file --- .../__tests__/LanguageSwitchButton.test.js | 114 ------------------ 1 file changed, 114 deletions(-) delete mode 100644 src/utils/__tests__/LanguageSwitchButton.test.js diff --git a/src/utils/__tests__/LanguageSwitchButton.test.js b/src/utils/__tests__/LanguageSwitchButton.test.js deleted file mode 100644 index add624c..0000000 --- a/src/utils/__tests__/LanguageSwitchButton.test.js +++ /dev/null @@ -1,114 +0,0 @@ -/* -Copyright 2026 Seekra - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { expect, test, describe, vi, beforeEach } from 'vitest'; -import { mount } from '@vue/test-utils'; -import LanguageSwitchButton from '../LanguageSwitchButton.vue'; -import { loadLanguage } from '@/i18n'; - -vi.mock('@/i18n', () => ({ - loadLanguage: vi.fn(() => Promise.resolve()), - LANGUAGES_RTL: ['ar', 'he'], - SUPPORTED_LANGUAGES: ['en', 'de', 'ar'] -})); - -vi.mock('vue-i18n', () => ({ - useI18n: () => ({ - t: (key) => key, - locale: { value: 'de' } - }) -})); - -vi.mock('@/features/icons/components/Icon.vue', () => ({ - default: { - name: 'Icon', - template: 'Icon' - } -})); - -describe('LanguageSwitchButton.vue', () => { - beforeEach(() => { - vi.clearAllMocks(); - localStorage.clear(); - document.documentElement.lang = ''; - document.documentElement.dir = ''; - }); - - test('renders correctly with initial state closed', () => { - const wrapper = mount(LanguageSwitchButton); - - expect(wrapper.find('.language-button').exists()).toBe(true); - expect(wrapper.find('.language-dropdown').exists()).toBe(false); - expect(wrapper.find('.language-button').attributes('aria-expanded')).toBe('false'); - }); - - test('opens the dropdown when language button is clicked', async () => { - const wrapper = mount(LanguageSwitchButton); - const button = wrapper.find('.language-button'); - - await button.trigger('click'); - - expect(wrapper.find('.language-dropdown').exists()).toBe(true); - expect(button.attributes('aria-expanded')).toBe('true'); - }); - - const languageTestCases = [ - { code: 'en', expectedDir: 'ltr' }, - { code: 'de', expectedDir: 'ltr' }, - { code: 'ar', expectedDir: 'rtl' } - ]; - - test.for(languageTestCases)('selectLanguage($code) sets localStorage, html attributes and changes layout direction to $expectedDir', async ({ code, expectedDir }) => { - const wrapper = mount(LanguageSwitchButton); - - await wrapper.find('.language-button').trigger('click'); - - const options = wrapper.findAll('.language-dropdown li'); - const optionToClick = options.find(opt => opt.text().includes(code)); - - await optionToClick.trigger('click'); - - expect(loadLanguage).toHaveBeenCalledWith(code); - expect(localStorage.getItem('locale')).toBe(code); - expect(document.documentElement.lang).toBe(code); - expect(document.documentElement.dir).toBe(expectedDir); - expect(wrapper.find('.language-dropdown').exists()).toBe(false); - }); - - test('closes the dropdown when clicking outside the component', async () => { - const wrapper = mount(LanguageSwitchButton, { - attachTo: document.body - }); - - await wrapper.find('.language-button').trigger('click'); - expect(wrapper.find('.language-dropdown').exists()).toBe(true); - - await new Promise(resolve => setTimeout(resolve, 0)); - - const externalDiv = document.createElement('div'); - document.body.appendChild(externalDiv); - - const clickEvent = new MouseEvent('click', { bubbles: true }); - externalDiv.dispatchEvent(clickEvent); - - await wrapper.vm.$nextTick(); - - expect(wrapper.find('.language-dropdown').exists()).toBe(false); - - wrapper.unmount(); - externalDiv.remove(); - }); -}); \ No newline at end of file -- 2.39.5