fix(guest): route homepage CTA to guest checkout when unauthenticated
Some checks failed
CI / Lint, type check, unit tests, coverage (push) Successful in 2m43s
CI / E2E browser tests (push) Failing after 1m17s

Guest checkout was unreachable: the homepage CTA "Fortsatt till brevet"
always linked to /compose (requiresAuth), so anonymous users hit the
router guard and got redirected to /logga-in instead of reaching the
guest checkout flow.

Changes:
- HomePage.vue: compute CTA target based on auth state. Authenticated
  users still go to /compose; unauthenticated users go to
  /gast-bestallning with the plate as a query param.
- GuestCheckoutPage.vue: accept plate from route.query.plate so the user
  does not have to re-enter it after looking it up on the homepage.
- HomePage.spec.ts: add Pinia setup + guest-checkout route; update CTA
  assertion to expect /gast-bestallning?plate=... when unauthenticated.

All 318 frontend tests pass, vue-tsc clean (exit 0).
This commit is contained in:
Hermes Agent 2026-06-22 12:08:43 +00:00
parent 8b7e1d5ce2
commit 48c2a50c5d
3 changed files with 34 additions and 7 deletions

View file

@ -1,8 +1,10 @@
import { describe, it, expect, vi } from 'vitest' import { describe, it, expect, vi, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import { createPinia, setActivePinia } from 'pinia'
import { createRouter, createMemoryHistory } from 'vue-router' import { createRouter, createMemoryHistory } from 'vue-router'
import HomePage from '@/pages/HomePage.vue' import HomePage from '@/pages/HomePage.vue'
import ComposePage from '@/pages/ComposePage.vue' import ComposePage from '@/pages/ComposePage.vue'
import GuestCheckoutPage from '@/pages/GuestCheckoutPage.vue'
vi.mock('@/api/vehicles', () => ({ vi.mock('@/api/vehicles', () => ({
lookupVehicle: vi.fn(), lookupVehicle: vi.fn(),
@ -17,6 +19,11 @@ function createTestRouter() {
routes: [ routes: [
{ path: '/', name: 'home', component: HomePage }, { path: '/', name: 'home', component: HomePage },
{ path: '/compose', name: 'compose', component: ComposePage }, { path: '/compose', name: 'compose', component: ComposePage },
{
path: '/gast-bestallning',
name: 'guest-checkout',
component: GuestCheckoutPage,
},
], ],
}) })
} }
@ -28,6 +35,12 @@ function mountHome(router: ReturnType<typeof createTestRouter>) {
} }
describe('HomePage', () => { describe('HomePage', () => {
beforeEach(() => {
setActivePinia(createPinia())
localStorage.removeItem('auth_token')
vi.clearAllMocks()
})
it('renders headline', () => { it('renders headline', () => {
const router = createTestRouter() const router = createTestRouter()
const wrapper = mountHome(router) const wrapper = mountHome(router)
@ -88,7 +101,7 @@ describe('HomePage', () => {
expect(cta.text()).toBe('Fortsätt till brevet') expect(cta.text()).toBe('Fortsätt till brevet')
}) })
it('CTA links to compose page with plate query param', async () => { it('CTA links to guest checkout with plate query param when unauthenticated', async () => {
mockLookupVehicle.mockResolvedValue({ mockLookupVehicle.mockResolvedValue({
make: 'Volvo', make: 'Volvo',
model: 'V70', model: 'V70',
@ -107,6 +120,6 @@ describe('HomePage', () => {
const cta = wrapper.find('.btn--primary') const cta = wrapper.find('.btn--primary')
const href = cta.attributes('href') const href = cta.attributes('href')
expect(href).toBe('/compose?plate=ABC123') expect(href).toBe('/gast-bestallning?plate=ABC123')
}) })
}) })

View file

@ -1,11 +1,12 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useRouter } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { createGuestOrder } from '@/api/guestOrders' import { createGuestOrder } from '@/api/guestOrders'
const router = useRouter() const router = useRouter()
const route = useRoute()
const plate = ref('') const plate = ref((route.query.plate as string) || '')
const letterText = ref('') const letterText = ref('')
const email = ref('') const email = ref('')
const submitting = ref(false) const submitting = ref(false)

View file

@ -1,10 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref, computed } from 'vue'
import { RouterLink } from 'vue-router' import { RouterLink } from 'vue-router'
import PlateInput from '@/components/PlateInput.vue' import PlateInput from '@/components/PlateInput.vue'
import VehicleInfo from '@/components/VehicleInfo.vue' import VehicleInfo from '@/components/VehicleInfo.vue'
import type { VehicleInfo as VehicleData } from '@/components/VehicleInfo.vue' import type { VehicleInfo as VehicleData } from '@/components/VehicleInfo.vue'
import { lookupVehicle } from '@/api/vehicles' import { lookupVehicle } from '@/api/vehicles'
import { useAuthStore } from '@/stores/authStore'
const auth = useAuthStore()
// When unauthenticated, the homepage CTA sends the user to the guest
// checkout page instead of the auth-protected /compose route.
// Without this, anonymous users hit the router guard and are bounced
// to /logga-in the guest flow we built was unreachable from the UI.
const ctaTarget = computed(() =>
auth.isAuthenticated
? { name: 'compose' as const, query: { plate: plate.value } }
: { name: 'guest-checkout' as const, query: { plate: plate.value } },
)
const useCases = [ const useCases = [
{ {
@ -204,7 +217,7 @@ async function handleLookup(lookedUpPlate: string) {
<RouterLink <RouterLink
v-if="vehicle" v-if="vehicle"
:to="{ name: 'compose', query: { plate } }" :to="ctaTarget"
class="btn btn--primary btn--lg home__cta" class="btn btn--primary btn--lg home__cta"
> >
Fortsätt till brevet Fortsätt till brevet