Add pool presets, operator bake-ins, and USB pack improvements

- Pool preset checkboxes with failover in Calibrate and Forge

- Tier 1/2 UX: setup banner, forge next worker, LAN defaults, blueprint prompt

- USB: auto-install forge tools, seed config.json, sync LAUNCH.bat
This commit is contained in:
AetherForge
2026-06-01 21:06:19 -07:00
parent 1e0482ebe1
commit 1413a9394f
29 changed files with 1078 additions and 314 deletions

View File

@@ -59,6 +59,7 @@ describe('BuilderPage', () => {
});
afterEach(() => {
vi.unstubAllGlobals();
cleanup();
});
@@ -115,6 +116,34 @@ describe('BuilderPage', () => {
expect(screen.getByRole('button', { name: /Download/i })).toBeInTheDocument();
});
it('offers forge-next-worker after a successful build', async () => {
renderBuilder();
await screen.findByRole('button', { name: /FORGE INSTALLER/i });
await userEvent.setup().click(screen.getByRole('button', { name: /FORGE INSTALLER/i }));
expect(await screen.findByText('worker-1.exe')).toBeInTheDocument();
await userEvent.setup().click(screen.getByRole('button', { name: 'Forge next worker' }));
expect(screen.queryByText('worker-1.exe')).not.toBeInTheDocument();
expect(screen.getByPlaceholderText(/office-pc-1/i)).toHaveValue('worker-2');
});
it('shows fleet blueprint save prompt after first successful forge', async () => {
const promptMock = vi.fn().mockReturnValue('fleet-default');
vi.stubGlobal('prompt', promptMock);
const saveSpy = vi.spyOn(api, 'saveBlueprint');
renderBuilder();
await screen.findByRole('button', { name: /FORGE INSTALLER/i });
await userEvent.setup().click(screen.getByRole('button', { name: /FORGE INSTALLER/i }));
expect(await screen.findByText('worker-1.exe')).toBeInTheDocument();
await userEvent.setup().click(screen.getByRole('button', { name: 'Save as fleet blueprint' }));
await waitFor(() => {
expect(promptMock).toHaveBeenCalledWith('Save this setup as a fleet blueprint:', 'fleet-default');
expect(saveSpy).toHaveBeenCalledWith('fleet-default', expect.any(Object));
});
});
it('blocks forge when preflight has errors (empty wallet)', async () => {
renderBuilder();
const wallet = await screen.findByDisplayValue(/^4A+/);