fix(guest): route homepage CTA to guest checkout when unauthenticated
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:
parent
8b7e1d5ce2
commit
48c2a50c5d
3 changed files with 34 additions and 7 deletions
|
|
@ -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')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue