Files
python-tdd/src/static/tests/MySeaSeatsSpec.js

55 lines
2.0 KiB
JavaScript
Raw Normal View History

my-sea spectator Phase B: seat-2C occupancy + visitor token gate + one-shot seated glow + gear BYE — TDD Phase B of the my-sea invite → spectator → voice blueprint. An ACCEPTED invitee can watch the owner's my-sea read-only, deposit a token to occupy seat 2C (opening a 24h voice window for Phase C), and BYE out. Owner's my_sea.html is left structurally intact — the spectator gets a dedicated, simpler my_sea_visit.html; the read-only draw reuses the existing `latest_draw_slots` payload (no picker surgery). - B1: my_sea_visit(owner_id) spectator view — 403 unless an ACCEPTED SeaInvite(owner, request.user); owner bounced to their own my_sea. Context forces owner-only controls off (sea_btn_active=False, read_only=True); renders the table hex (1C owner / 2C visitor) + owner draw read-only. - B2: visitor gate — my_sea_visit_gate reuses my_sea_gate.html w. a spectator branch (titles the OWNER's Sea, INSERT posts to the visitor endpoint, bud-panel suppressed, gear NVM→visit + BYE). Single-step my_sea_visit_insert_token selects+debits the visitor's token (same priority chain) and records token_deposited_at + a 24h voice_until on the SeaInvite → seat 2C present. Center btn flips GATE VIEW → VIEW DRAW. - B3: spectator gear BYE — my_sea_visit_leave sets status=LEFT, left_at, clears voice_until (frees 2C, ends voice), redirects /gameboard/. _my_sea_gear.html gains a `leave_url`-gated BYE below NVM (owner pages pass no leave_url, so unchanged). - B-seat: one-shot "seated" glow per user-spec 2026-05-27 — new shared apps/gameboard/my-sea-seats.js: on first view (localStorage-gated by a per-occupancy data-seat-token) an occupied seat flares --terUser + --ninUser glow ~1.5s then settles to full-opacity --secUser (.fa-ban already swapped to .fa-circle-check). _room.scss adds .seated / .seat-just-seated + the my-sea-seat-flare keyframes (mirrors the room's .active→.role-confirmed handoff). Wired on BOTH the spectator page (load) and the owner page (load + on the FREE DRAW seat-1 transition). MySeaSeatsSpec.js Jasmine spec covers the gating + timed class removal. - B5: MySeaSpectatorFlowTest FT — accept → visit → GATE VIEW → deposit → VIEW DRAW + seat 2C seated. URLs: my-sea/visit/<uuid:owner_id>/ (+ /gate/, /insert, /leave). 470 IT/UT green; spectator FT + full Jasmine suite green. Phase C (WebRTC mesh voice + coturn droplet) next — the 24h voice_until window set here drives it. Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:35:00 -04:00
// Jasmine spec for my-sea-seats.js — the one-shot "seated" glow module
// (Phase B of the my-sea invite/voice sprint). Verifies the localStorage-
// gated first-view behaviour + the timed flare-class removal.
describe('my-sea-seats one-shot seated glow', function () {
var seat;
beforeEach(function () {
window.localStorage.clear();
seat = document.createElement('div');
seat.className = 'table-seat seated';
document.body.appendChild(seat);
});
afterEach(function () {
if (seat && seat.parentNode) seat.parentNode.removeChild(seat);
window.localStorage.clear();
});
it('exposes playSeatGlow globally', function () {
expect(typeof window.playSeatGlow).toBe('function');
});
it('adds the seat-just-seated flare class', function () {
window.playSeatGlow(seat);
expect(seat.classList.contains('seat-just-seated')).toBe(true);
});
it('marks a tokened seat seen in localStorage', function () {
seat.setAttribute('data-seat-token', 'visit-42');
window.playSeatGlow(seat);
expect(window.localStorage.getItem('mysea-seat-seen:visit-42')).toBe('1');
});
it('does not replay the flare for an already-seen token', function () {
seat.setAttribute('data-seat-token', 'visit-42');
window.localStorage.setItem('mysea-seat-seen:visit-42', '1');
window.playSeatGlow(seat);
expect(seat.classList.contains('seat-just-seated')).toBe(false);
});
it('always animates a tokenless seat (no persistence)', function () {
window.playSeatGlow(seat);
expect(seat.classList.contains('seat-just-seated')).toBe(true);
});
it('removes the flare class after the ~1.5s glow window', function () {
jasmine.clock().install();
window.playSeatGlow(seat);
expect(seat.classList.contains('seat-just-seated')).toBe(true);
jasmine.clock().tick(1600);
expect(seat.classList.contains('seat-just-seated')).toBe(false);
jasmine.clock().uninstall();
});
});