test(07-00): add failing stubs for VALID-01 and UX-01

- Add VALID-01 describe block with 7 tests: Azure account name (reject uppercase, reject <3 chars, accept valid), S3 region (reject spaces, accept valid), GCS project_number (reject non-digits, accept valid)
- Add UX-01 describe block with 5 tests: ⓘ button presence on sas_url, SFTP auth section, OneDrive token; tooltip show/hide toggle
- 9 new tests fail (RED state — validate property and tooltipText not on FieldDef yet)
- 3 new acceptance tests pass (absence-of-error condition already met before implementation)
- All 147 pre-existing tests still pass (zero regressions)
This commit is contained in:
2026-03-31 09:45:21 +02:00
parent 9d02764fa3
commit 327421ed69
@@ -2,6 +2,8 @@
// Covers BACK-01: Azure Blob config form — account + SAS/Key toggle, both values preserved // Covers BACK-01: Azure Blob config form — account + SAS/Key toggle, both values preserved
// Covers BACK-02: Amazon S3 config form — access_key_id, secret_access_key, region // Covers BACK-02: Amazon S3 config form — access_key_id, secret_access_key, region
// Covers BACK-03: S3-Compatible config form — same as S3 plus endpoint field // Covers BACK-03: S3-Compatible config form — same as S3 plus endpoint field
// Covers VALID-01: Format validation stubs (RED — validate property not implemented yet)
// Covers UX-01: Tooltip toggle stubs (RED — tooltipText property not implemented yet)
import { describe, it, expect } from 'vitest'; import { describe, it, expect } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
@@ -227,4 +229,174 @@ describe('RemoteConfigStep', () => {
}); });
}); });
}); });
describe('VALID-01 — format validation', () => {
// RED stubs — validate property not on FieldDef yet; no regex chaining in buildZodSchema()
// These tests will fail until Plan 07-01 implements format validation
// Azure account name tests
it('rejects account name with uppercase letters', async () => {
const user = userEvent.setup();
renderWithBackend('azureblob');
await waitFor(() => {
expect(screen.getByLabelText(/storage account name/i)).toBeDefined();
});
await user.clear(screen.getByLabelText(/storage account name/i));
await user.type(screen.getByLabelText(/storage account name/i), 'MyStorage');
await user.click(screen.getByRole('button', { name: /next/i }));
await waitFor(() => {
expect(screen.getByText(/3.{1,5}24 lowercase alphanumeric/i)).toBeDefined();
});
});
it('rejects account name shorter than 3 characters', async () => {
const user = userEvent.setup();
renderWithBackend('azureblob');
await waitFor(() => {
expect(screen.getByLabelText(/storage account name/i)).toBeDefined();
});
await user.clear(screen.getByLabelText(/storage account name/i));
await user.type(screen.getByLabelText(/storage account name/i), 'ab');
await user.click(screen.getByRole('button', { name: /next/i }));
await waitFor(() => {
expect(screen.getByText(/3.{1,5}24 lowercase alphanumeric/i)).toBeDefined();
});
});
it('accepts valid account name (no format error)', async () => {
const user = userEvent.setup();
renderWithBackend('azureblob');
await waitFor(() => {
expect(screen.getByLabelText(/storage account name/i)).toBeDefined();
});
await user.type(screen.getByLabelText(/storage account name/i), 'mystorageaccount');
// Fill SAS URL field so form can submit without other required-field errors
await user.type(screen.getByLabelText(/sas url/i), 'https://mystorage.blob.core.windows.net/?sv=test');
await user.click(screen.getByRole('button', { name: /next/i }));
await waitFor(() => {
expect(screen.queryByText(/3.{1,5}24 lowercase alphanumeric/i)).toBeNull();
});
});
// S3 region tests
it('rejects S3 region with invalid format (spaces)', async () => {
const user = userEvent.setup();
renderWithBackend('s3');
await waitFor(() => {
expect(screen.getByLabelText(/region/i)).toBeDefined();
});
await user.type(screen.getByLabelText(/region/i), 'us east 1');
await user.click(screen.getByRole('button', { name: /next/i }));
await waitFor(() => {
expect(screen.getByText(/valid aws region format/i)).toBeDefined();
});
});
it('accepts valid S3 region (no format error)', async () => {
const user = userEvent.setup();
renderWithBackend('s3');
await waitFor(() => {
expect(screen.getByLabelText(/region/i)).toBeDefined();
});
// Fill all required S3 fields so form can submit
await user.type(screen.getByLabelText(/access key id/i), 'AKIAIOSFODNN7EXAMPLE');
await user.type(screen.getByLabelText(/secret access key/i), 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY');
await user.type(screen.getByLabelText(/region/i), 'us-east-1');
await user.click(screen.getByRole('button', { name: /next/i }));
await waitFor(() => {
expect(screen.queryByText(/valid aws region format/i)).toBeNull();
});
});
// GCS project_number tests
it('rejects GCS project_number with non-digits', async () => {
const user = userEvent.setup();
renderWithBackend('gcs' as BackendType);
await waitFor(() => {
expect(screen.getByLabelText(/project number/i)).toBeDefined();
});
await user.type(screen.getByLabelText(/project number/i), 'abc');
await user.click(screen.getByRole('button', { name: /next/i }));
await waitFor(() => {
expect(screen.getByText(/digits only/i)).toBeDefined();
});
});
it('accepts valid GCS project_number (no format error)', async () => {
const user = userEvent.setup();
renderWithBackend('gcs' as BackendType);
await waitFor(() => {
expect(screen.getByLabelText(/project number/i)).toBeDefined();
});
await user.type(screen.getByLabelText(/project number/i), '123456789');
await user.click(screen.getByRole('button', { name: /next/i }));
await waitFor(() => {
expect(screen.queryByText(/digits only/i)).toBeNull();
});
});
});
describe('UX-01 — contextual tooltips', () => {
// RED stubs — tooltipText not on FieldDef yet; no ⓘ buttons rendered
// These tests will fail until Plan 07-02 implements tooltip toggle
it('renders ⓘ button on azureblob sas_url field', async () => {
renderWithBackend('azureblob');
await waitFor(() => {
expect(screen.getByLabelText(/sas url/i)).toBeDefined();
});
// ⓘ button does not exist yet — will throw "Unable to find role 'button'"
expect(screen.getByRole('button', { name: /more info about sas url/i })).toBeDefined();
});
it('shows tooltip panel when ⓘ is clicked on sas_url', async () => {
const user = userEvent.setup();
renderWithBackend('azureblob');
await waitFor(() => {
expect(screen.getByLabelText(/sas url/i)).toBeDefined();
});
// ⓘ button does not exist yet — getByRole will throw
await user.click(screen.getByRole('button', { name: /more info about sas url/i }));
await waitFor(() => {
// Tooltip text should be visible after clicking ⓘ
expect(screen.getByText(/shared access signature/i)).toBeDefined();
});
});
it('hides tooltip panel when ⓘ is clicked again', async () => {
const user = userEvent.setup();
renderWithBackend('azureblob');
await waitFor(() => {
expect(screen.getByLabelText(/sas url/i)).toBeDefined();
});
// ⓘ button does not exist yet — getByRole will throw
await user.click(screen.getByRole('button', { name: /more info about sas url/i }));
await waitFor(() => {
expect(screen.getByText(/shared access signature/i)).toBeDefined();
});
await user.click(screen.getByRole('button', { name: /more info about sas url/i }));
await waitFor(() => {
expect(screen.queryByText(/shared access signature/i)).toBeNull();
});
});
it('renders ⓘ button on SFTP auth method section', async () => {
renderWithBackend('sftp' as BackendType);
await waitFor(() => {
expect(screen.getByLabelText(/host/i)).toBeDefined();
});
// ⓘ button does not exist yet — will throw "Unable to find role 'button'"
expect(screen.getByRole('button', { name: /more info about authentication/i })).toBeDefined();
});
it('renders ⓘ button on OneDrive token field', async () => {
renderWithBackend('onedrive' as BackendType);
await waitFor(() => {
expect(screen.getByLabelText(/oauth token/i)).toBeDefined();
});
// ⓘ button does not exist yet — will throw "Unable to find role 'button'"
// Note: this matches any "more info about X" button on the OneDrive form
expect(screen.getByRole('button', { name: /more info about oauth token/i })).toBeDefined();
});
});
}); });