diff --git a/frontend/src/__tests__/App.spec.ts b/frontend/src/__tests__/App.spec.ts new file mode 100644 index 0000000..579492b --- /dev/null +++ b/frontend/src/__tests__/App.spec.ts @@ -0,0 +1,17 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import App from '@/App.vue' +import router from '@/router' + +describe('App', () => { + it('renders RouterView with HomePage content', async () => { + router.push('/') + await router.isReady() + const wrapper = mount(App, { + global: { + plugins: [router], + }, + }) + expect(wrapper.text()).toContain('BilHälsning') + }) +}) diff --git a/frontend/src/__tests__/PlateInput.spec.ts b/frontend/src/__tests__/PlateInput.spec.ts new file mode 100644 index 0000000..a009543 --- /dev/null +++ b/frontend/src/__tests__/PlateInput.spec.ts @@ -0,0 +1,102 @@ +import { describe, it, expect } from 'vitest' +import { mount } from '@vue/test-utils' +import PlateInput from '@/components/PlateInput.vue' + +function createWrapper(modelValue = '') { + return mount(PlateInput, { + props: { modelValue }, + }) +} + +describe('PlateInput', () => { + it('renders an input element', () => { + const wrapper = createWrapper() + expect(wrapper.find('input').exists()).toBe(true) + }) + + it('auto-uppercases lowercase input', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('abc123') + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['ABC123']) + }) + + it('strips non-alphanumeric characters', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC 123') + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['ABC123']) + }) + + it('strips dashes and symbols', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC-12D') + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['ABC12D']) + }) + + it('shows error message for invalid format after input', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC12') + expect(wrapper.text()).toContain('giltigt registreringsnummer') + }) + + it('does not show error when input is empty', () => { + const wrapper = createWrapper() + expect(wrapper.text()).not.toContain('giltigt registreringsnummer') + }) + + it('emits lookup on valid ABC123 format', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC123') + expect(wrapper.emitted('lookup')).toBeTruthy() + expect(wrapper.emitted('lookup')?.[0]).toEqual(['ABC123']) + }) + + it('emits lookup on valid ABC12D format', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC12D') + expect(wrapper.emitted('lookup')).toBeTruthy() + expect(wrapper.emitted('lookup')?.[0]).toEqual(['ABC12D']) + }) + + it('does not emit lookup on invalid input', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC1') + expect(wrapper.emitted('lookup')).toBeFalsy() + }) + + it('updates modelValue via v-model', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC123') + expect(wrapper.emitted('update:modelValue')).toBeTruthy() + expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['ABC123']) + }) + + it('prevents re-emission for the same plate', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC123') + expect(wrapper.emitted('lookup')).toHaveLength(1) + await input.setValue('ABC12') + expect(wrapper.emitted('lookup')).toHaveLength(1) + await input.setValue('ABC123') + expect(wrapper.emitted('lookup')).toHaveLength(1) + }) + + it('emits again for a different valid plate', async () => { + const wrapper = createWrapper() + const input = wrapper.find('input') + await input.setValue('ABC123') + expect(wrapper.emitted('lookup')).toHaveLength(1) + await input.setValue('') + await input.setValue('DEF456') + expect(wrapper.emitted('lookup')).toHaveLength(2) + expect(wrapper.emitted('lookup')?.[1]).toEqual(['DEF456']) + }) +}) diff --git a/frontend/src/__tests__/Router.spec.ts b/frontend/src/__tests__/Router.spec.ts new file mode 100644 index 0000000..e27c96f --- /dev/null +++ b/frontend/src/__tests__/Router.spec.ts @@ -0,0 +1,16 @@ +import { describe, it, expect } from 'vitest' +import router from '@/router' + +describe('Router', () => { + it('resolves / to HomePage', async () => { + await router.push('/') + await router.isReady() + expect(router.currentRoute.value.name).toBe('home') + }) + + it('does not crash on unknown route', async () => { + await router.push('/nonexistent') + await router.isReady() + expect(router.currentRoute.value.matched.length).toBe(0) + }) +}) diff --git a/frontend/src/__tests__/Store.spec.ts b/frontend/src/__tests__/Store.spec.ts new file mode 100644 index 0000000..ab732aa --- /dev/null +++ b/frontend/src/__tests__/Store.spec.ts @@ -0,0 +1,10 @@ +import { describe, it, expect } from 'vitest' +import { createPinia } from 'pinia' + +describe('Pinia', () => { + it('creates a Pinia instance', () => { + const pinia = createPinia() + expect(pinia).toBeDefined() + expect(pinia.state).toBeDefined() + }) +}) diff --git a/frontend/src/components/PlateInput.vue b/frontend/src/components/PlateInput.vue new file mode 100644 index 0000000..3365035 --- /dev/null +++ b/frontend/src/components/PlateInput.vue @@ -0,0 +1,106 @@ + + + + + Registreringsnummer + + + Ange ett giltigt registreringsnummer + + + + + diff --git a/frontend/src/pages/HomePage.vue b/frontend/src/pages/HomePage.vue index 54500d2..b08745d 100644 --- a/frontend/src/pages/HomePage.vue +++ b/frontend/src/pages/HomePage.vue @@ -1,9 +1,107 @@ - + - + BilHälsning + Skicka ett brev till en fordonsägare + + + + Söker... + + + + {{ vehicle.make }} {{ vehicle.model }} ({{ vehicle.year }}) — + {{ vehicle.color }} + + + + + Inget fordon hittades för {{ plate }} + - + diff --git a/frontend/src/stores/.gitkeep b/frontend/src/stores/.gitkeep deleted file mode 100644 index e69de29..0000000
+ Ange ett giltigt registreringsnummer +
Skicka ett brev till en fordonsägare
+ {{ vehicle.make }} {{ vehicle.model }} ({{ vehicle.year }}) — + {{ vehicle.color }} +
Inget fordon hittades för {{ plate }}