feat(frontend): breakable grass block on the 404 page
Implements the last unbuilt easter egg (PLAN Task 11 #5, TODO «Пасхалки» #5): the 404 used to show static ASCII art; now a grass block drops into a dusk world, cracks over three clicks, then bursts into square debris and respawns. Keyboard accessible (a real button), and under prefers-reduced-motion the fall and burst are skipped while the crack stages still work. Replaces the NOT_FOUND_ART pane; i18n adds notFound.blockAria/blockHint (ru+en).
This commit is contained in:
parent
07b3f55bee
commit
904c724c9f
6 changed files with 201 additions and 4 deletions
36
frontend/src/shared/easter/tests/breakable.test.tsx
Normal file
36
frontend/src/shared/easter/tests/breakable.test.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import { screen } from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import { expect, test, vi } from 'vitest'
|
||||
import { BreakableBlock } from '../BreakableBlock'
|
||||
import { renderApp } from '../../../test/render'
|
||||
|
||||
// motion's useReducedMotion reads matchMedia, which jsdom lacks.
|
||||
vi.stubGlobal('matchMedia', (query: string) => ({
|
||||
matches: !query.includes('reduce'),
|
||||
media: query,
|
||||
onchange: null,
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
addListener() {},
|
||||
removeListener() {},
|
||||
dispatchEvent: () => false,
|
||||
}))
|
||||
|
||||
test('a block drops in and cracks over three clicks, then bursts and respawns', async () => {
|
||||
const user = userEvent.setup()
|
||||
renderApp([{ path: '/', element: <BreakableBlock /> }])
|
||||
|
||||
// The block remounts (new key) on respawn, so re-query it before each click.
|
||||
const block = () => screen.getByRole('button', { name: 'Сломать блок' })
|
||||
expect(block().querySelector('.lv-block-crack')).toBeNull()
|
||||
|
||||
await user.click(block())
|
||||
expect(block().querySelector('.lv-block-crack')).toHaveAttribute('data-stage', '1')
|
||||
await user.click(block())
|
||||
expect(block().querySelector('.lv-block-crack')).toHaveAttribute('data-stage', '2')
|
||||
|
||||
// Third click shatters: cracks clear (respawn) and debris spawns.
|
||||
await user.click(block())
|
||||
expect(block().querySelector('.lv-block-crack')).toBeNull()
|
||||
expect(document.querySelectorAll('.lv-block-particle').length).toBeGreaterThan(0)
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue