diff --git a/src/apps/epic/static/apps/epic/sig-select.js b/src/apps/epic/static/apps/epic/sig-select.js index 94d222e..df81ca1 100644 --- a/src/apps/epic/static/apps/epic/sig-select.js +++ b/src/apps/epic/static/apps/epic/sig-select.js @@ -29,6 +29,10 @@ var SigSelect = (function () { var _reservedFloats = {}; // key: role → portal element (thumbs-up, frozen) var _cursorPortal = null; + // CAST SKY click → page reload. Reassignable via setReload for tests so + // they can spy without actually reloading the Jasmine runner. + var _reload = function () { window.location.reload(); }; + function getCsrf() { var m = document.cookie.match(/csrftoken=([^;]+)/); return m ? m[1] : ''; @@ -625,12 +629,16 @@ var SigSelect = (function () { userRole = overlay.dataset.userRole; userPolarity= overlay.dataset.polarity; - // CAST SKY btn is rendered hidden during SIG_SELECT; reveal on pick_sky_available + // CAST SKY btn is rendered hidden during SIG_SELECT; revealed by + // room:pick_sky_available once both polarity rooms are done. Clicking + // it must reload the page — the _sky_overlay.html partial is only + // included server-side once room.table_status == "SKY_SELECT", so + // reloading is the only path that brings the modal + its openSky + // handler into the DOM. (Tray.placeSig already played during the + // polarity_room_done sequence; do NOT re-open the tray here.) var pickSkyBtn = document.getElementById('id_pick_sky_btn'); if (pickSkyBtn) { - pickSkyBtn.addEventListener('click', function () { - if (typeof Tray !== 'undefined') Tray.open(); - }); + pickSkyBtn.addEventListener('click', function () { _reload(); }); } // Restore reservations from server-rendered JSON (page-load state). @@ -738,5 +746,6 @@ var SigSelect = (function () { }, _setFrozen: function (v) { _stageFrozen = v; }, _setReservedCardId: function (id) { _reservedCardId = id; }, + setReload: function (fn) { _reload = fn; }, }; }()); diff --git a/src/static/tests/SigSelectSpec.js b/src/static/tests/SigSelectSpec.js index ccabfbb..330de47 100644 --- a/src/static/tests/SigSelectSpec.js +++ b/src/static/tests/SigSelectSpec.js @@ -894,4 +894,44 @@ describe("SigSelect", () => { expect(document.getElementById("id_hex_waiting_msg")).toBe(null); }); }); + + // ── CAST SKY click (post pick_sky_available reveal) ──────────────────── // + // + // After room:pick_sky_available reveals the hidden #id_pick_sky_btn, a + // click on CAST SKY must reload the page — the _sky_overlay.html partial + // is only rendered server-side once room.table_status == "SKY_SELECT", so + // reloading is the only way to bring its modal + openSky handler into the + // DOM. The handler must NOT call Tray.open: the tray was already played + // during the polarity_room_done sequence (Tray.placeSig) and re-opening it + // here would swap Sky Select for the tray. + + describe("CAST SKY click (post pick_sky_available)", () => { + let pickSkyBtn, reloadSpy; + + beforeEach(() => { + pickSkyBtn = document.createElement("button"); + pickSkyBtn.id = "id_pick_sky_btn"; + pickSkyBtn.style.display = "none"; + document.body.appendChild(pickSkyBtn); + makeFixture({ polarity: "levity", userRole: "PC" }); + reloadSpy = jasmine.createSpy("reload"); + SigSelect.setReload(reloadSpy); + spyOn(Tray, "open"); + }); + + afterEach(() => { + if (pickSkyBtn) pickSkyBtn.remove(); + SigSelect.setReload(function () { window.location.reload(); }); + }); + + it("reloads the page so the sky overlay partial renders", () => { + pickSkyBtn.dispatchEvent(new MouseEvent("click", { bubbles: true })); + expect(reloadSpy).toHaveBeenCalled(); + }); + + it("does NOT call Tray.open (tray was already played by Tray.placeSig)", () => { + pickSkyBtn.dispatchEvent(new MouseEvent("click", { bubbles: true })); + expect(Tray.open).not.toHaveBeenCalled(); + }); + }); }); diff --git a/src/static_src/tests/SigSelectSpec.js b/src/static_src/tests/SigSelectSpec.js index ccabfbb..330de47 100644 --- a/src/static_src/tests/SigSelectSpec.js +++ b/src/static_src/tests/SigSelectSpec.js @@ -894,4 +894,44 @@ describe("SigSelect", () => { expect(document.getElementById("id_hex_waiting_msg")).toBe(null); }); }); + + // ── CAST SKY click (post pick_sky_available reveal) ──────────────────── // + // + // After room:pick_sky_available reveals the hidden #id_pick_sky_btn, a + // click on CAST SKY must reload the page — the _sky_overlay.html partial + // is only rendered server-side once room.table_status == "SKY_SELECT", so + // reloading is the only way to bring its modal + openSky handler into the + // DOM. The handler must NOT call Tray.open: the tray was already played + // during the polarity_room_done sequence (Tray.placeSig) and re-opening it + // here would swap Sky Select for the tray. + + describe("CAST SKY click (post pick_sky_available)", () => { + let pickSkyBtn, reloadSpy; + + beforeEach(() => { + pickSkyBtn = document.createElement("button"); + pickSkyBtn.id = "id_pick_sky_btn"; + pickSkyBtn.style.display = "none"; + document.body.appendChild(pickSkyBtn); + makeFixture({ polarity: "levity", userRole: "PC" }); + reloadSpy = jasmine.createSpy("reload"); + SigSelect.setReload(reloadSpy); + spyOn(Tray, "open"); + }); + + afterEach(() => { + if (pickSkyBtn) pickSkyBtn.remove(); + SigSelect.setReload(function () { window.location.reload(); }); + }); + + it("reloads the page so the sky overlay partial renders", () => { + pickSkyBtn.dispatchEvent(new MouseEvent("click", { bubbles: true })); + expect(reloadSpy).toHaveBeenCalled(); + }); + + it("does NOT call Tray.open (tray was already played by Tray.placeSig)", () => { + pickSkyBtn.dispatchEvent(new MouseEvent("click", { bubbles: true })); + expect(Tray.open).not.toHaveBeenCalled(); + }); + }); });