diff --git a/apps/web/src/pages/LoginPage.stories.js b/apps/web/src/pages/LoginPage.stories.js index c875193..c5630ec 100644 --- a/apps/web/src/pages/LoginPage.stories.js +++ b/apps/web/src/pages/LoginPage.stories.js @@ -1,5 +1,22 @@ import LoginPage from './LoginPage.vue'; +import { expect, userEvent, within } from '@storybook/test'; export default { title: 'Pages/LoginPage', component: LoginPage }; export const Empty = {}; export const Loading = { parameters: { docs: { description: { story: 'Submit state is exercised through the live form.' } } } }; + +export const ErrorFocus = { + play: async ({ canvasElement }) => { + const originalFetch = window.fetch; + window.fetch = async () => new Response(JSON.stringify({ error: { message: 'Invalid credentials.' } }), { status: 401, headers: { 'content-type': 'application/json' } }); + try { + const canvas = within(canvasElement); + await userEvent.type(canvas.getByLabelText('Email'), 'seller@example.com'); + await userEvent.type(canvas.getByLabelText('Password'), 'wrong-password'); + await userEvent.click(canvas.getByRole('button', { name: 'Sign in' })); + await expect(canvas.getByRole('alert')).toHaveFocus(); + } finally { + window.fetch = originalFetch; + } + }, +}; diff --git a/docs/IMPLEMENTATION-STATUS.md b/docs/IMPLEMENTATION-STATUS.md index 997f9f6..da5667a 100644 --- a/docs/IMPLEMENTATION-STATUS.md +++ b/docs/IMPLEMENTATION-STATUS.md @@ -275,6 +275,7 @@ Remaining roadmap areas are approved external provider credentials/adapters, bro - Admin listing status cards now expose a programmatic product heading and status announcement for each listing. - Saved-view rows now expose an accessible view name, and the image-limit setting includes range guidance tied to its input. - Login errors now move focus to the announced error message so failed authentication is immediately available to keyboard and assistive-technology users. +- Login error focus behavior now has Storybook interaction coverage using a deterministic failed-authentication response. ## Local Commands