Files
AetherForge/server/web/src/pages/MissionDeckPage.test.tsx
AetherForge 821a0e2cd7
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Close test-gap noise rows and sync coverage documentation.
Restore P1/P2/fleet/erasure COVERED rows in PROBLEMS.md with refreshed counts; drop fixed poll-duplication and Vitest-noise items. Silence ECONNREFUSED stderr in Vitest setup, alias download mock exports, and expand erasure/Path Tracer test coverage.
2026-06-07 06:44:54 -07:00

112 lines
4.5 KiB
TypeScript

/**
* @vitest-environment happy-dom
*/
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { cleanup, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MemoryRouter } from 'react-router-dom';
import MissionDeckPage from './MissionDeckPage';
import { ForgeProvider } from '../context/ForgeContext';
import { routerFuture } from '../routerFuture';
import { mockServerConfig, mockServerInfo } from '../test/fixtures';
import { api } from '../api/client';
vi.mock('../context/PresenceContext', () => ({
usePresence: () => ({
othersOnPage: [],
comrades: [],
comradesHere: () => [],
othersOnline: false,
}),
}));
function renderMissionDeck() {
return render(
<MemoryRouter initialEntries={['/mission-deck']} future={routerFuture}>
<ForgeProvider>
<MissionDeckPage />
</ForgeProvider>
</MemoryRouter>,
);
}
describe('MissionDeckPage', () => {
beforeEach(() => {
vi.clearAllMocks();
localStorage.clear();
vi.spyOn(api, 'getConfig').mockResolvedValue(mockServerConfig());
vi.spyOn(api, 'getServerInfo').mockResolvedValue(mockServerInfo);
vi.spyOn(api, 'listBuilds').mockResolvedValue([]);
vi.spyOn(api, 'buildAgent').mockResolvedValue({
success: true,
build_id: 'deck-build-1',
file_name: 'worker-deck.exe',
file_size: 4096,
download_url: '/api/v1/builds/deck-build-1/download',
});
vi.spyOn(api, 'exportSpreadKit').mockResolvedValue(undefined);
vi.stubGlobal('navigator', {
clipboard: { writeText: vi.fn().mockResolvedValue(undefined) },
});
});
afterEach(() => {
vi.unstubAllGlobals();
cleanup();
});
it('shows loading state then mission deck hero', async () => {
renderMissionDeck();
expect(screen.getByText(/Loading loadout defaults/)).toBeInTheDocument();
expect(await screen.findByRole('heading', { level: 1, name: /Mission Deck/i })).toBeInTheDocument();
expect(screen.getByText('FAST PATH')).toBeInTheDocument();
expect(await screen.findByText(/Pick a preset loadout/i)).toBeInTheDocument();
});
it('renders Ghost / Loud / Spread mode chips in loadout layout', async () => {
renderMissionDeck();
await screen.findByRole('region', { name: 'Mission loadout' });
expect(screen.getByRole('region', { name: 'Mission loadout' })).toBeInTheDocument();
const loadout = screen.getByRole('region', { name: 'Mission loadout' });
expect(loadout).toHaveTextContent('Ghost');
expect(loadout).toHaveTextContent('Loud');
expect(loadout).toHaveTextContent('Spread');
expect(screen.getByRole('heading', { level: 2, name: /Ghost|Loud|Spread/ })).toBeInTheDocument();
});
it('shows spread profile chips and campaign slug on the right panel', async () => {
renderMissionDeck();
await screen.findByRole('region', { name: 'Mission loadout' });
expect(screen.getByRole('heading', { level: 3, name: /Spread profile/i })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'LAN Kindling' })).toBeInTheDocument();
expect(document.getElementById('md-campaign')).toBeInTheDocument();
});
it('links to Forge, Emberwake, Builds, and field guide', async () => {
renderMissionDeck();
await screen.findByRole('region', { name: 'Mission loadout' });
expect(screen.getAllByRole('link', { name: /^Forge$/i }).length).toBeGreaterThan(0);
expect(screen.getAllByRole('link', { name: /^Emberwake$/i }).length).toBeGreaterThan(0);
expect(screen.getAllByRole('link', { name: /^Builds$/i }).length).toBeGreaterThan(0);
expect(screen.getByRole('link', { name: /Field guide/i })).toHaveAttribute('href', '/docs/#mission-deck');
expect(screen.getByRole('link', { name: /Full Forge/i })).toHaveAttribute('href', '/forge');
});
it('equips and strikes using runForgeMission pipeline', async () => {
const user = userEvent.setup();
const buildSpy = vi.spyOn(api, 'buildAgent');
const exportSpy = vi.spyOn(api, 'exportSpreadKit');
renderMissionDeck();
await screen.findByRole('region', { name: 'Mission loadout' });
await user.click(screen.getByRole('button', { name: 'LAN Kindling' }));
await user.click(screen.getByRole('button', { name: /Equip & Strike/i }));
await waitFor(() => {
expect(buildSpy).toHaveBeenCalled();
});
expect(exportSpy).toHaveBeenCalled();
expect(await screen.findByText(/Forge complete/i)).toBeInTheDocument();
expect(screen.getByRole('link', { name: /View install commands in Builds/i })).toHaveAttribute('href', '/builds');
});
});