Release validation: tests green, USB pack, fleet UX and API hardening.
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Some checks failed
CI Docker Mining Proof / Linux agent hashrate proof (push) Has been cancelled
Fix macOS agent cross-compile (SilentAVExclusion) and Calibrate E2E nav selector; expand tests and docs; refresh portable usb binary and spread/wiki assets.
This commit is contained in:
@@ -1,107 +1,111 @@
|
||||
/**
|
||||
|
||||
* @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();
|
||||
/**
|
||||
* @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(screen.getByText(/Pick a preset loadout/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders Ghost / Loud / Spread mode chips in loadout layout', async () => {
|
||||
renderMissionDeck();
|
||||
await screen.findByRole('heading', { level: 1, name: 'Mission Deck' });
|
||||
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('heading', { level: 1, name: 'Mission Deck' });
|
||||
expect(screen.getByRole('heading', { level: 3, name: /Spread profile/i })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: 'LAN Kindling' })).toBeInTheDocument();
|
||||
expect(screen.getByLabelText(/Campaign slug/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('links to Forge, Emberwake, Builds, and field guide', async () => {
|
||||
renderMissionDeck();
|
||||
await screen.findByRole('heading', { level: 1, name: /Mission Deck/i });
|
||||
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('heading', { level: 1, name: 'Mission Deck' });
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user