feat(ui): give the app one visual identity as Movie Loop
Gold is the primary in both themes, Bricolage Grotesque carries the wordmark, titles and the score, and Movie A / Movie B wear the blue and magenta of three-strip Technicolor on their posters instead of colouring whole rows. The loop closes on screen - connectors light, a gold line draws back to Movie A, the row glows, then the result arrives - in place of confetti, on every play mode, scaled to the chain's length. Every page shares one header and hairline lists; copy is sentence case with one verb per flow, and the Home hero shows a real finished loop. Also clamps link counts at zero: an opponent who had not moved yet showed "-1 links".
This commit is contained in:
1 parent
da32e49b0b
commit
8d0b7f39ee
122 files changed
+3129
-2089
No files matched your search
@@ -2,6 +2,13 @@ import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { Link, MemoryRouter, Route, Routes } from 'react-router';
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
// The completion modal waits for the loop-closing animation; there is none
|
||||
// to wait for in jsdom, and these tests are about the content, not the delay.
|
||||
vi.mock('@/lib/motion', async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import('@/lib/motion')>()),
|
||||
prefersReducedMotion: () => true,
|
||||
}));
|
||||
import type {
|
||||
ActorChainLink,
|
||||
MovieChainLink,
|
||||
@@ -231,7 +238,7 @@ function renderPage(initialEntry = '/versus/async-game/match-1') {
|
||||
* target card + target badge), so wait on the chain-length badge instead -
|
||||
* it renders exactly once, as soon as the game is ready to play. */
|
||||
async function waitForReady() {
|
||||
await screen.findByText('Chain: 0 links');
|
||||
await screen.findByText('0 links');
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -364,7 +371,7 @@ describe('abort on unmount (WP cleanup)', () => {
|
||||
// Switch to a different match before match-1's slow session finishes.
|
||||
await userEvent.click(screen.getByText('go to match 2'));
|
||||
await flush();
|
||||
expect(await screen.findByText('Chain: 0 links')).toBeInTheDocument();
|
||||
expect(await screen.findByText('0 links')).toBeInTheDocument();
|
||||
expect(useGameStore.getState().movieA?.title).toBe('Third');
|
||||
|
||||
// match-1's delayed movie details now arrive - after being superseded.
|
||||
@@ -391,8 +398,8 @@ describe('creator mode', () => {
|
||||
|
||||
expect(submitCreatorScore).toHaveBeenCalledWith('match-1', { sessionId: 'session-1' });
|
||||
expect(submitCreatorScore).toHaveBeenCalledTimes(1);
|
||||
expect(await screen.findByText('Score Submitted!')).toBeInTheDocument();
|
||||
expect(screen.getByText('2400')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Score submitted')).toBeInTheDocument();
|
||||
expect(screen.getByText('2,400')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('navigates from the completion modal to the leaderboard', async () => {
|
||||
@@ -400,9 +407,9 @@ describe('creator mode', () => {
|
||||
renderPage();
|
||||
await waitForReady();
|
||||
closeTheLoop();
|
||||
await screen.findByText('Score Submitted!');
|
||||
await screen.findByText('Score submitted');
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'View Leaderboard' }));
|
||||
await userEvent.click(screen.getByRole('button', { name: 'View leaderboard' }));
|
||||
expect(await screen.findByText('leaderboard route')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -440,8 +447,8 @@ describe('challenger mode', () => {
|
||||
|
||||
expect(submitAsyncAttempt).toHaveBeenCalledWith('match-1', { sessionId: 'session-1' });
|
||||
expect(submitAsyncAttempt).toHaveBeenCalledTimes(1);
|
||||
expect(await screen.findByText('Challenge Complete!')).toBeInTheDocument();
|
||||
expect(screen.getByText('You beat the creator!')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Challenge complete')).toBeInTheDocument();
|
||||
expect(screen.getByText('You beat the creator')).toBeInTheDocument();
|
||||
expect(screen.getByText('Your rank:')).toBeInTheDocument();
|
||||
expect(screen.getByText('#1')).toBeInTheDocument();
|
||||
});
|
||||
@@ -464,7 +471,7 @@ describe('challenger mode', () => {
|
||||
closeTheLoop();
|
||||
await flush();
|
||||
|
||||
expect(await screen.findByText('Challenge Complete!')).toBeInTheDocument();
|
||||
expect(await screen.findByText('Challenge complete')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Your rank:')).toBeNull();
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user