feat: alive UI wave, galaxy presence, spread and fleet enhancements
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
Dashboard ambient layer, comrade presence, Mission Deck and War Room, Emberwake supply chain, spread/docs publishing, fleet policy and modules API, CI docker mining, and refreshed USB pack.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { cleanup, fireEvent, render, screen, waitFor, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import BuilderPage, { formatBytes } from './BuilderPage';
|
||||
@@ -21,6 +21,12 @@ function renderBuilder(initialEntries = ['/forge']) {
|
||||
);
|
||||
}
|
||||
|
||||
function missionWizardScope() {
|
||||
const root = screen.getByText('MISSION RITUAL — 3-STEP WIZARD').closest('.forge-mission-wizard');
|
||||
if (!root) throw new Error('Mission wizard not found');
|
||||
return within(root as HTMLElement);
|
||||
}
|
||||
|
||||
describe('formatBytes', () => {
|
||||
it('formats sub-kilobyte values as bytes', () => {
|
||||
expect(formatBytes(512)).toBe('512 B');
|
||||
@@ -184,4 +190,104 @@ describe('BuilderPage', () => {
|
||||
screen.getByText(/Drop any file — PDF, video, document/i)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('applies forge skin class from default operation mode', async () => {
|
||||
localStorage.setItem('aetherforge-operation-mode', 'ghost_walk');
|
||||
localStorage.setItem('aetherforge-forge-theme', 'auto');
|
||||
const { container } = renderBuilder();
|
||||
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
|
||||
expect(container.querySelector('.command-deck')).toHaveClass('forge-skin--ghost');
|
||||
});
|
||||
|
||||
it('honors forge theme override over operation mode', async () => {
|
||||
localStorage.setItem('aetherforge-operation-mode', 'ghost_walk');
|
||||
localStorage.setItem('aetherforge-forge-theme', 'halloween');
|
||||
const { container } = renderBuilder();
|
||||
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
|
||||
expect(container.querySelector('.command-deck')).toHaveClass('forge-skin--halloween');
|
||||
expect(container.querySelector('.command-deck')).not.toHaveClass('forge-skin--ghost');
|
||||
});
|
||||
|
||||
it('updates forge skin when operation mode chip is selected', async () => {
|
||||
localStorage.setItem('aetherforge-forge-theme', 'auto');
|
||||
const { container } = renderBuilder();
|
||||
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
|
||||
await userEvent.setup().click(screen.getByRole('button', { name: 'Wildfire' }));
|
||||
expect(container.querySelector('.command-deck')).toHaveClass('forge-skin--wildfire');
|
||||
});
|
||||
|
||||
it('applies crucible skin for Crucible Storm mode', async () => {
|
||||
localStorage.setItem('aetherforge-operation-mode', 'crucible_storm');
|
||||
localStorage.setItem('aetherforge-forge-theme', 'auto');
|
||||
const { container } = renderBuilder();
|
||||
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
|
||||
expect(container.querySelector('.command-deck')).toHaveClass('forge-skin--crucible');
|
||||
});
|
||||
|
||||
it('applies halloween skin for Sigil Mask mode', async () => {
|
||||
localStorage.setItem('aetherforge-operation-mode', 'sigil_mask');
|
||||
localStorage.setItem('aetherforge-forge-theme', 'auto');
|
||||
const { container } = renderBuilder();
|
||||
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
|
||||
expect(container.querySelector('.command-deck')).toHaveClass('forge-skin--halloween');
|
||||
});
|
||||
|
||||
it('renders mission ritual wizard with three step pills', async () => {
|
||||
renderBuilder();
|
||||
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
|
||||
const wizard = missionWizardScope();
|
||||
expect(screen.getByText('MISSION RITUAL — 3-STEP WIZARD')).toBeInTheDocument();
|
||||
expect(wizard.getByRole('tab', { name: /Mode/i })).toBeInTheDocument();
|
||||
expect(wizard.getByRole('tab', { name: /Profile/i })).toBeInTheDocument();
|
||||
expect(wizard.getByRole('tab', { name: /Launch/i })).toBeInTheDocument();
|
||||
expect(wizard.getByRole('button', { name: 'Ghost' })).toBeInTheDocument();
|
||||
expect(wizard.getByRole('button', { name: 'Loud' })).toBeInTheDocument();
|
||||
expect(wizard.getByRole('button', { name: 'Spread' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('advances mission wizard from mode to profile to launch', async () => {
|
||||
const user = userEvent.setup();
|
||||
renderBuilder();
|
||||
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
|
||||
const wizard = missionWizardScope();
|
||||
|
||||
await user.click(wizard.getByRole('button', { name: 'Spread' }));
|
||||
await user.click(wizard.getByRole('button', { name: 'Next →' }));
|
||||
expect(wizard.getByText('Spread profile (optional)')).toBeInTheDocument();
|
||||
|
||||
await user.click(wizard.getByRole('button', { name: 'LAN Kindling' }));
|
||||
await user.click(wizard.getByRole('button', { name: 'Next →' }));
|
||||
expect(wizard.getByRole('button', { name: '🚀 Launch Ritual' })).toBeInTheDocument();
|
||||
expect(wizard.getByLabelText('Campaign slug (?c=)')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('runs mission ritual and shows success modal with spread landing link', async () => {
|
||||
vi.spyOn(api, 'buildAgent').mockResolvedValue({
|
||||
success: true,
|
||||
build_id: 'ritual-build-1',
|
||||
file_name: 'worker-ritual.exe',
|
||||
file_size: 4096,
|
||||
download_url: '/api/v1/builds/ritual-build-1/download',
|
||||
fusion_enabled: false,
|
||||
obfuscated: false,
|
||||
signed: false,
|
||||
});
|
||||
vi.spyOn(api, 'exportSpreadKit').mockResolvedValue(undefined);
|
||||
|
||||
const user = userEvent.setup();
|
||||
renderBuilder();
|
||||
await screen.findByRole('heading', { level: 2, name: 'Quick Forge' });
|
||||
const wizard = missionWizardScope();
|
||||
|
||||
await user.click(wizard.getByRole('button', { name: 'Spread' }));
|
||||
await user.click(wizard.getByRole('button', { name: 'Next →' }));
|
||||
await user.click(wizard.getByRole('button', { name: 'LAN Kindling' }));
|
||||
await user.click(wizard.getByRole('button', { name: 'Next →' }));
|
||||
await user.click(wizard.getByRole('button', { name: '🚀 Launch Ritual' }));
|
||||
|
||||
expect(await screen.findByText('Mission complete — links copied')).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'Open spread landing' })).toHaveAttribute('href', '/spread/');
|
||||
expect(api.buildAgent).toHaveBeenCalled();
|
||||
expect(api.exportSpreadKit).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user