diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index a78fb6f..23d4cd8 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -1,7 +1,19 @@
-
+
+
+
+
+
+
+
diff --git a/frontend/src/__tests__/AboutPage.spec.ts b/frontend/src/__tests__/AboutPage.spec.ts
new file mode 100644
index 0000000..df7ab24
--- /dev/null
+++ b/frontend/src/__tests__/AboutPage.spec.ts
@@ -0,0 +1,10 @@
+import { describe, it, expect } from 'vitest'
+import { mount } from '@vue/test-utils'
+import AboutPage from '@/pages/AboutPage.vue'
+
+describe('AboutPage', () => {
+ it('renders heading', () => {
+ const wrapper = mount(AboutPage)
+ expect(wrapper.text()).toContain('Om BilHälsning')
+ })
+})
diff --git a/frontend/src/__tests__/App.spec.ts b/frontend/src/__tests__/App.spec.ts
index 579492b..4d39ba0 100644
--- a/frontend/src/__tests__/App.spec.ts
+++ b/frontend/src/__tests__/App.spec.ts
@@ -1,9 +1,23 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import App from '@/App.vue'
+import AppHeader from '@/components/AppHeader.vue'
+import AppFooter from '@/components/AppFooter.vue'
import router from '@/router'
describe('App', () => {
+ it('renders AppHeader and AppFooter', async () => {
+ router.push('/')
+ await router.isReady()
+ const wrapper = mount(App, {
+ global: {
+ plugins: [router],
+ },
+ })
+ expect(wrapper.findComponent(AppHeader).exists()).toBe(true)
+ expect(wrapper.findComponent(AppFooter).exists()).toBe(true)
+ })
+
it('renders RouterView with HomePage content', async () => {
router.push('/')
await router.isReady()
@@ -12,6 +26,6 @@ describe('App', () => {
plugins: [router],
},
})
- expect(wrapper.text()).toContain('BilHälsning')
+ expect(wrapper.text()).toContain('Skicka ett brev till en fordonsägare')
})
})
diff --git a/frontend/src/__tests__/AppFooter.spec.ts b/frontend/src/__tests__/AppFooter.spec.ts
new file mode 100644
index 0000000..42587d1
--- /dev/null
+++ b/frontend/src/__tests__/AppFooter.spec.ts
@@ -0,0 +1,54 @@
+import { describe, it, expect } from 'vitest'
+import { mount } from '@vue/test-utils'
+import { createRouter, createMemoryHistory } from 'vue-router'
+import AppFooter from '@/components/AppFooter.vue'
+
+function createTestRouter() {
+ return createRouter({
+ history: createMemoryHistory(),
+ routes: [
+ {
+ path: '/om',
+ name: 'about',
+ component: { template: '
About
' },
+ },
+ {
+ path: '/kontakt',
+ name: 'contact',
+ component: { template: 'Contact
' },
+ },
+ {
+ path: '/integritetspolicy',
+ name: 'privacy',
+ component: { template: 'Privacy
' },
+ },
+ {
+ path: '/villkor',
+ name: 'terms',
+ component: { template: 'Terms
' },
+ },
+ ],
+ })
+}
+
+describe('AppFooter', () => {
+ it('renders all four links', () => {
+ const router = createTestRouter()
+ const wrapper = mount(AppFooter, {
+ global: { plugins: [router] },
+ })
+ const links = wrapper.findAll('a')
+
+ expect(links[0].text()).toBe('Om oss')
+ expect(links[0].attributes('href')).toBe('/om')
+
+ expect(links[1].text()).toBe('Kontakt')
+ expect(links[1].attributes('href')).toBe('/kontakt')
+
+ expect(links[2].text()).toBe('Integritetspolicy')
+ expect(links[2].attributes('href')).toBe('/integritetspolicy')
+
+ expect(links[3].text()).toBe('Villkor')
+ expect(links[3].attributes('href')).toBe('/villkor')
+ })
+})
diff --git a/frontend/src/__tests__/AppHeader.spec.ts b/frontend/src/__tests__/AppHeader.spec.ts
new file mode 100644
index 0000000..a4bd878
--- /dev/null
+++ b/frontend/src/__tests__/AppHeader.spec.ts
@@ -0,0 +1,33 @@
+import { describe, it, expect } from 'vitest'
+import { mount } from '@vue/test-utils'
+import { createRouter, createMemoryHistory } from 'vue-router'
+import AppHeader from '@/components/AppHeader.vue'
+
+function createTestRouter() {
+ return createRouter({
+ history: createMemoryHistory(),
+ routes: [
+ { path: '/', name: 'home', component: { template: 'Home
' } },
+ ],
+ })
+}
+
+describe('AppHeader', () => {
+ it('renders the logo text', () => {
+ const router = createTestRouter()
+ const wrapper = mount(AppHeader, {
+ global: { plugins: [router] },
+ })
+ expect(wrapper.text()).toContain('BilHälsning')
+ })
+
+ it('has a link to home', () => {
+ const router = createTestRouter()
+ const wrapper = mount(AppHeader, {
+ global: { plugins: [router] },
+ })
+ const links = wrapper.findAll('a')
+ const homeLink = links.find((a) => a.attributes('href') === '/')
+ expect(homeLink).toBeTruthy()
+ })
+})
diff --git a/frontend/src/__tests__/ComposePage.spec.ts b/frontend/src/__tests__/ComposePage.spec.ts
new file mode 100644
index 0000000..6c00756
--- /dev/null
+++ b/frontend/src/__tests__/ComposePage.spec.ts
@@ -0,0 +1,43 @@
+import { describe, it, expect } from 'vitest'
+import { mount } from '@vue/test-utils'
+import { createRouter, createMemoryHistory } from 'vue-router'
+import ComposePage from '@/pages/ComposePage.vue'
+
+function createTestRouter() {
+ return createRouter({
+ history: createMemoryHistory(),
+ routes: [{ path: '/compose', name: 'compose', component: ComposePage }],
+ })
+}
+
+describe('ComposePage', () => {
+ it('renders heading', async () => {
+ const router = createTestRouter()
+ router.push('/compose')
+ await router.isReady()
+ const wrapper = mount(ComposePage, {
+ global: { plugins: [router] },
+ })
+ expect(wrapper.text()).toContain('Skriv ditt brev')
+ })
+
+ it('displays plate from query param', async () => {
+ const router = createTestRouter()
+ router.push({ path: '/compose', query: { plate: 'ABC123' } })
+ await router.isReady()
+ const wrapper = mount(ComposePage, {
+ global: { plugins: [router] },
+ })
+ expect(wrapper.text()).toContain('ABC123')
+ })
+
+ it('does not show plate when no query param', async () => {
+ const router = createTestRouter()
+ router.push('/compose')
+ await router.isReady()
+ const wrapper = mount(ComposePage, {
+ global: { plugins: [router] },
+ })
+ expect(wrapper.find('.compose__plate').exists()).toBe(false)
+ })
+})
diff --git a/frontend/src/__tests__/ContactPage.spec.ts b/frontend/src/__tests__/ContactPage.spec.ts
new file mode 100644
index 0000000..a317239
--- /dev/null
+++ b/frontend/src/__tests__/ContactPage.spec.ts
@@ -0,0 +1,10 @@
+import { describe, it, expect } from 'vitest'
+import { mount } from '@vue/test-utils'
+import ContactPage from '@/pages/ContactPage.vue'
+
+describe('ContactPage', () => {
+ it('renders heading', () => {
+ const wrapper = mount(ContactPage)
+ expect(wrapper.text()).toContain('Kontakta oss')
+ })
+})
diff --git a/frontend/src/__tests__/HomePage.spec.ts b/frontend/src/__tests__/HomePage.spec.ts
index 97d93c0..c899d7a 100644
--- a/frontend/src/__tests__/HomePage.spec.ts
+++ b/frontend/src/__tests__/HomePage.spec.ts
@@ -1,10 +1,85 @@
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
+import { createRouter, createMemoryHistory } from 'vue-router'
import HomePage from '@/pages/HomePage.vue'
+import ComposePage from '@/pages/ComposePage.vue'
+
+function createTestRouter() {
+ return createRouter({
+ history: createMemoryHistory(),
+ routes: [
+ { path: '/', name: 'home', component: HomePage },
+ { path: '/compose', name: 'compose', component: ComposePage },
+ ],
+ })
+}
+
+function mountHome(router: ReturnType) {
+ return mount(HomePage, {
+ global: { plugins: [router] },
+ })
+}
describe('HomePage', () => {
- it('mounts successfully', () => {
- const wrapper = mount(HomePage)
- expect(wrapper.text()).toContain('BilHälsning')
+ it('renders subtitle', () => {
+ const router = createTestRouter()
+ const wrapper = mountHome(router)
+ expect(wrapper.text()).toContain('Skicka ett brev till en fordonsägare')
+ })
+
+ it('does not show CTA button initially', () => {
+ const router = createTestRouter()
+ const wrapper = mountHome(router)
+ expect(wrapper.find('.home__cta').exists()).toBe(false)
+ })
+
+ it('does not show CTA while loading', async () => {
+ const router = createTestRouter()
+ const wrapper = mountHome(router)
+ const plateInput = wrapper.findComponent({ name: 'PlateInput' })
+
+ await plateInput.vm.$emit('lookup', 'ABC123')
+ await wrapper.vm.$nextTick()
+
+ expect(wrapper.find('.home__cta').exists()).toBe(false)
+ })
+
+ it('does not show CTA after not-found', async () => {
+ const router = createTestRouter()
+ const wrapper = mountHome(router)
+ const plateInput = wrapper.findComponent({ name: 'PlateInput' })
+
+ await plateInput.vm.$emit('lookup', 'UNKNOWN')
+ await new Promise((resolve) => setTimeout(resolve, 500))
+
+ expect(wrapper.find('.home__cta').exists()).toBe(false)
+ })
+
+ it('shows CTA button when vehicle data present', async () => {
+ const router = createTestRouter()
+ const wrapper = mountHome(router)
+ const plateInput = wrapper.findComponent({ name: 'PlateInput' })
+
+ await plateInput.vm.$emit('update:modelValue', 'ABC123')
+ await plateInput.vm.$emit('lookup', 'ABC123')
+ await new Promise((resolve) => setTimeout(resolve, 500))
+
+ const cta = wrapper.find('.home__cta')
+ expect(cta.exists()).toBe(true)
+ expect(cta.text()).toBe('Skicka ett brev till ägaren')
+ })
+
+ it('CTA links to compose page with plate query param', async () => {
+ const router = createTestRouter()
+ const wrapper = mountHome(router)
+ const plateInput = wrapper.findComponent({ name: 'PlateInput' })
+
+ await plateInput.vm.$emit('update:modelValue', 'ABC123')
+ await plateInput.vm.$emit('lookup', 'ABC123')
+ await new Promise((resolve) => setTimeout(resolve, 500))
+
+ const cta = wrapper.find('.home__cta')
+ const href = cta.attributes('href')
+ expect(href).toBe('/compose?plate=ABC123')
})
})
diff --git a/frontend/src/components/AppFooter.vue b/frontend/src/components/AppFooter.vue
new file mode 100644
index 0000000..3234322
--- /dev/null
+++ b/frontend/src/components/AppFooter.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/components/AppHeader.vue b/frontend/src/components/AppHeader.vue
new file mode 100644
index 0000000..e135b32
--- /dev/null
+++ b/frontend/src/components/AppHeader.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/pages/AboutPage.vue b/frontend/src/pages/AboutPage.vue
new file mode 100644
index 0000000..94b0caa
--- /dev/null
+++ b/frontend/src/pages/AboutPage.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
Om BilHälsning
+
+ BilHälsning är en tjänst som låter dig skicka fysiska brev till
+ fordonsägare via registreringsnummer.
+
+
+
+
+
diff --git a/frontend/src/pages/ComposePage.vue b/frontend/src/pages/ComposePage.vue
new file mode 100644
index 0000000..e8e0d65
--- /dev/null
+++ b/frontend/src/pages/ComposePage.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
Skriv ditt brev
+
Registreringsnummer: {{ plate }}
+
+
+
+
diff --git a/frontend/src/pages/ContactPage.vue b/frontend/src/pages/ContactPage.vue
new file mode 100644
index 0000000..7f8d9bd
--- /dev/null
+++ b/frontend/src/pages/ContactPage.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
diff --git a/frontend/src/pages/HomePage.vue b/frontend/src/pages/HomePage.vue
index 240eb89..804e984 100644
--- a/frontend/src/pages/HomePage.vue
+++ b/frontend/src/pages/HomePage.vue
@@ -1,5 +1,6 @@