Lift the multi-step deploy wizard (~520 LOC) plus its private INI-builder helpers (PLACEHOLDER_LINES, b64encodeUtf8, buildIni, PickMode type) into their own file. Verbatim move; the underscore-prefixed helpers drop the leading underscore now that they're file-local rather than competing with hoisted parent constants. - New DeckyFleet/DeployWizard.tsx - DeployWizard.test.tsx covers the closed render guard, the open-at-step-0 archetype list, NEXT-disabled-until-archetype, and CANCEL -> onClose. ServiceConfigFields is vi.mock'd to a stub since it pulls schemas via api.get() that are out of scope for these tests. - DeckyFleet.tsx loses the wizard plus the now-unused imports (DEFAULT_SERVICES, Modal, PickIcon, ServiceConfigFields and its type aliases).
85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
import { render, screen } from '@testing-library/react';
|
|
import userEvent from '@testing-library/user-event';
|
|
import { DeployWizard } from './DeployWizard';
|
|
import type { Archetype } from './types';
|
|
|
|
// ServiceConfigFields fetches the per-service schema; replace with a stub
|
|
// so the wizard tests don't need MSW handlers for that side-channel.
|
|
vi.mock('../ServiceConfigFields', async () => {
|
|
const actual = await vi.importActual<object>('../ServiceConfigFields');
|
|
return {
|
|
...actual,
|
|
default: () => null,
|
|
};
|
|
});
|
|
|
|
const archetypes: Archetype[] = [
|
|
{ slug: 'web-server', name: 'Web Server', services: ['http', 'https'], icon: 'globe' },
|
|
{ slug: 'database', name: 'Database', services: ['postgres'], icon: 'database' },
|
|
];
|
|
|
|
describe('DeployWizard', () => {
|
|
it('renders nothing meaningful when closed', () => {
|
|
render(
|
|
<DeployWizard
|
|
open={false}
|
|
onClose={() => {}}
|
|
onComplete={() => {}}
|
|
archetypes={archetypes}
|
|
fleetSize={0}
|
|
/>,
|
|
);
|
|
expect(screen.queryByText('DEPLOY NEW DECKIES')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('opens at step 0 with archetype list rendered', () => {
|
|
render(
|
|
<DeployWizard
|
|
open
|
|
onClose={() => {}}
|
|
onComplete={() => {}}
|
|
archetypes={archetypes}
|
|
fleetSize={0}
|
|
/>,
|
|
);
|
|
expect(screen.getByText('DEPLOY NEW DECKIES')).toBeInTheDocument();
|
|
expect(screen.getByText('Web Server')).toBeInTheDocument();
|
|
expect(screen.getByText('Database')).toBeInTheDocument();
|
|
});
|
|
|
|
it('disables NEXT until an archetype is selected', async () => {
|
|
const user = userEvent.setup();
|
|
render(
|
|
<DeployWizard
|
|
open
|
|
onClose={() => {}}
|
|
onComplete={() => {}}
|
|
archetypes={archetypes}
|
|
fleetSize={0}
|
|
/>,
|
|
);
|
|
const nextBtn = screen.getByText('NEXT →') as HTMLButtonElement;
|
|
expect(nextBtn.disabled).toBe(true);
|
|
|
|
await user.click(screen.getByText('Web Server'));
|
|
expect(nextBtn.disabled).toBe(false);
|
|
});
|
|
|
|
it('CANCEL button invokes onClose', async () => {
|
|
const onClose = vi.fn();
|
|
const user = userEvent.setup();
|
|
render(
|
|
<DeployWizard
|
|
open
|
|
onClose={onClose}
|
|
onComplete={() => {}}
|
|
archetypes={archetypes}
|
|
fleetSize={0}
|
|
/>,
|
|
);
|
|
await user.click(screen.getByText('CANCEL'));
|
|
expect(onClose).toHaveBeenCalled();
|
|
});
|
|
});
|