mirror of
https://github.com/vernu/textbee.git
synced 2026-07-31 09:28:14 -04:00
- prefetch route-tab links so tab clicks reuse a cached payload instead of paying an uncached server round trip each time - add loading.tsx boundaries (dashboard root and each section) so clicks paint within a frame; section-level files keep the header and tabs mounted while only the content area swaps - link the sidebar Account item and the checkout page straight to /dashboard/account/billing. The /dashboard/account redirect stub stays for old links, but no internal link pays the extra hop anymore. The stub also dropped query params, which silently ate the plan-change success toast. - set QueryClient defaults (staleTime 60s, no focus refetch, retry 1); mutations already invalidate their keys, so the user's own changes stay instant. Device messages and webhook deliveries get a 15s staleTime since they change from outside the tab. - replace the axios getCachedSession TTL cache with a token seeded from the server session in Providers and kept in sync by a session bridge. Requests attach the token synchronously; /api/auth/session is only a deduped fallback, instead of a refetch every 2 minutes with a thundering herd on expiry. - swap the billing card's 16px loading spinner for a card-shaped skeleton so the tab no longer looks blank while loading Tests: interceptor seeding/dedupe/signed-out behavior, provider defaults and token seeding, nav active-state matching, tab prefetch, billing loading state, plus e2e coverage for direct-to-billing navigation and an at-most-one-session-call budget guard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
import { expect, test } from '@playwright/test'
|
|
import { authenticate } from './session'
|
|
import { mockApi } from './mock-api'
|
|
|
|
// Serverless-cost regression guard. The API client's token is seeded from the
|
|
// server-fetched session, so browsing the dashboard must not keep calling
|
|
// /api/auth/session. Before this guard, the axios interceptor refetched the
|
|
// session every 2 minutes with no dedupe, so a burst of queries fanned out
|
|
// one session call each.
|
|
test('dashboard navigation makes at most one /api/auth/session call', async ({
|
|
page,
|
|
context,
|
|
}) => {
|
|
await authenticate(context)
|
|
await mockApi(page)
|
|
|
|
let sessionCalls = 0
|
|
page.on('request', (request) => {
|
|
if (new URL(request.url()).pathname === '/api/auth/session') {
|
|
sessionCalls += 1
|
|
}
|
|
})
|
|
|
|
await page.goto('/dashboard')
|
|
await expect(
|
|
page.getByRole('heading', { name: 'Welcome back, Test', level: 2 })
|
|
).toBeVisible()
|
|
|
|
const mainNav = page.getByRole('navigation', { name: 'Main' })
|
|
const tabs = page.getByRole('navigation', { name: 'Section navigation' })
|
|
|
|
await mainNav.getByRole('link', { name: 'Messaging' }).click()
|
|
await expect(page).toHaveURL(/\/dashboard\/messaging$/)
|
|
await tabs.getByRole('link', { name: 'History' }).click()
|
|
await expect(page).toHaveURL(/\/dashboard\/messaging\/history$/)
|
|
|
|
await mainNav.getByRole('link', { name: 'Webhooks' }).click()
|
|
await expect(page).toHaveURL(/\/dashboard\/webhooks$/)
|
|
await tabs.getByRole('link', { name: 'Deliveries' }).click()
|
|
await expect(page).toHaveURL(/\/dashboard\/webhooks\/deliveries$/)
|
|
|
|
await mainNav.getByRole('link', { name: 'Account' }).click()
|
|
await expect(page).toHaveURL(/\/dashboard\/account\/billing$/)
|
|
|
|
expect(sessionCalls).toBeLessThanOrEqual(1)
|
|
})
|