From cf06d72945a65746303c7f71a58c8de5a1fda926 Mon Sep 17 00:00:00 2001 From: Kawa Date: Fri, 27 Mar 2026 09:32:35 +0100 Subject: [PATCH] test(03-03): add failing tests for BackendSelectionStep (WIZD-01, WIZD-04) - Replace expect.fail stubs with real test assertions - Tests cover card render order, card click dispatch, name validation - RED: component file does not exist yet --- .../wizard/BackendSelectionStep.test.tsx | 80 ++++++++++++++++--- 1 file changed, 70 insertions(+), 10 deletions(-) diff --git a/src/components/wizard/BackendSelectionStep.test.tsx b/src/components/wizard/BackendSelectionStep.test.tsx index de17130..e689f14 100644 --- a/src/components/wizard/BackendSelectionStep.test.tsx +++ b/src/components/wizard/BackendSelectionStep.test.tsx @@ -2,49 +2,109 @@ // Covers WIZD-01: card grid renders Azure Blob, Amazon S3, S3-Compatible (Azure first) // Covers WIZD-04: remote name field validates alphanumeric/dash/underscore import { describe, it, expect } from 'vitest'; +import { render, screen, fireEvent } from '@testing-library/react'; +import { WizardProvider } from '../../store/context'; +import { BackendSelectionStep } from './BackendSelectionStep'; + +function renderStep() { + return render( + + + + ); +} describe('BackendSelectionStep', () => { describe('WIZD-01: backend card grid', () => { it('renders Azure Blob Storage card', () => { - expect.fail('not yet implemented'); + renderStep(); + expect(screen.getByText('Azure Blob Storage')).toBeDefined(); }); it('renders Amazon S3 card', () => { - expect.fail('not yet implemented'); + renderStep(); + expect(screen.getByText('Amazon S3')).toBeDefined(); }); it('renders S3-Compatible card', () => { - expect.fail('not yet implemented'); + renderStep(); + expect(screen.getByText('S3-Compatible')).toBeDefined(); }); it('Azure Blob is listed before S3 in DOM order', () => { - expect.fail('not yet implemented'); + renderStep(); + const buttons = screen.getAllByRole('button'); + // Find Azure and S3 buttons by their accessible names + const azureIdx = buttons.findIndex(b => b.textContent?.includes('Azure Blob Storage')); + const s3Idx = buttons.findIndex(b => b.textContent?.includes('Amazon S3')); + expect(azureIdx).toBeGreaterThanOrEqual(0); + expect(s3Idx).toBeGreaterThanOrEqual(0); + expect(azureIdx).toBeLessThan(s3Idx); }); it('clicking a backend card dispatches SET_BACKEND_TYPE and SET_STEP', () => { - expect.fail('not yet implemented'); + renderStep(); + // Fill in a valid remote name first + const nameInput = screen.getByRole('textbox'); + fireEvent.change(nameInput, { target: { value: 'my-remote' } }); + // Click the Azure card — if navigation dispatches SET_STEP(1), component may unmount + // We verify no validation error appears (meaning dispatch proceeded) + const azureButton = screen.getAllByRole('button').find(b => b.textContent?.includes('Azure Blob Storage'))!; + expect(azureButton).toBeDefined(); + fireEvent.click(azureButton); + // After successful navigation step fires, no validation alert should be present + expect(screen.queryByRole('alert')).toBeNull(); }); }); describe('WIZD-04: remote name field', () => { it('renders remote name input at the top of the step', () => { - expect.fail('not yet implemented'); + renderStep(); + const nameInput = screen.getByRole('textbox'); + expect(nameInput).toBeDefined(); + // Verify input appears before backend cards in DOM + const container = nameInput.closest('form') ?? document.body; + const allButtons = container.querySelectorAll('button'); + const inputPosition = Array.from(container.querySelectorAll('input, button')).indexOf(nameInput as HTMLInputElement); + const firstButtonPosition = Array.from(container.querySelectorAll('input, button')).indexOf(allButtons[0]); + expect(inputPosition).toBeLessThan(firstButtonPosition); }); it('shows no error before first Next attempt', () => { - expect.fail('not yet implemented'); + renderStep(); + // No alert role should be present on initial render + expect(screen.queryByRole('alert')).toBeNull(); }); it('shows inline error after first Next attempt with invalid name', () => { - expect.fail('not yet implemented'); + renderStep(); + // Click a card without filling in name — triggers validation + const azureButton = screen.getAllByRole('button').find(b => b.textContent?.includes('Azure Blob Storage'))!; + fireEvent.click(azureButton); + // Error should appear + expect(screen.getByRole('alert')).toBeDefined(); + expect(screen.getByText(/required/i)).toBeDefined(); }); it('accepts alphanumeric, dashes, and underscores', () => { - expect.fail('not yet implemented'); + renderStep(); + const nameInput = screen.getByRole('textbox'); + fireEvent.change(nameInput, { target: { value: 'my-remote_01' } }); + const azureButton = screen.getAllByRole('button').find(b => b.textContent?.includes('Azure Blob Storage'))!; + fireEvent.click(azureButton); + // Valid name — no alert should appear + expect(screen.queryByRole('alert')).toBeNull(); }); it('rejects names with spaces or special characters', () => { - expect.fail('not yet implemented'); + renderStep(); + const nameInput = screen.getByRole('textbox'); + fireEvent.change(nameInput, { target: { value: 'my remote!' } }); + const azureButton = screen.getAllByRole('button').find(b => b.textContent?.includes('Azure Blob Storage'))!; + fireEvent.click(azureButton); + // Invalid name — alert should appear + expect(screen.getByRole('alert')).toBeDefined(); + expect(screen.getByText(/letters, numbers, dashes/i)).toBeDefined(); }); }); });