demo'd old inventory area in room.html to make way for new content (hex table now centered in view); old test suite now targets Role card in #id_tray cells where appropriate, or skips Sig card select until aforementioned new feature deployed; new scripts & jasmine tests too; removed one irrelevant test case from apps.epic.tests.ITs.test_views.SelectRoleViewTest
This commit is contained in:
@@ -7,7 +7,6 @@ describe("RoleSelect", () => {
|
||||
<div class="room-page"
|
||||
data-select-role-url="/epic/room/test-uuid/select-role">
|
||||
</div>
|
||||
<div id="id_inv_role_card"></div>
|
||||
`;
|
||||
document.body.appendChild(testDiv);
|
||||
window.fetch = jasmine.createSpy("fetch").and.returnValue(
|
||||
@@ -102,12 +101,6 @@ describe("RoleSelect", () => {
|
||||
expect(document.getElementById("id_role_select")).toBeNull();
|
||||
});
|
||||
|
||||
it("clicking a card appends a .card to #id_inv_role_card", () => {
|
||||
const card = document.querySelector("#id_role_select .card");
|
||||
card.click();
|
||||
expect(document.querySelector("#id_inv_role_card .card")).not.toBeNull();
|
||||
});
|
||||
|
||||
it("clicking a card POSTs to the select_role URL", () => {
|
||||
const card = document.querySelector("#id_role_select .card");
|
||||
card.click();
|
||||
@@ -117,11 +110,6 @@ describe("RoleSelect", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("clicking a card results in exactly one card in inventory", () => {
|
||||
const card = document.querySelector("#id_role_select .card");
|
||||
card.click();
|
||||
expect(document.querySelectorAll("#id_inv_role_card .card").length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
@@ -135,11 +123,6 @@ describe("RoleSelect", () => {
|
||||
expect(document.getElementById("id_role_select")).toBeNull();
|
||||
});
|
||||
|
||||
it("does not add a card to inventory", () => {
|
||||
RoleSelect.openFan();
|
||||
document.querySelector(".role-select-backdrop").click();
|
||||
expect(document.querySelector("#id_inv_role_card .card")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// ------------------------------------------------------------------ //
|
||||
@@ -259,20 +242,13 @@ describe("RoleSelect", () => {
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
describe("tray card after successful role selection", () => {
|
||||
let grid, guardConfirm;
|
||||
let guardConfirm;
|
||||
|
||||
beforeEach(() => {
|
||||
// Minimal tray grid matching room.html structure
|
||||
grid = document.createElement("div");
|
||||
grid.id = "id_tray_grid";
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const cell = document.createElement("div");
|
||||
cell.className = "tray-cell";
|
||||
grid.appendChild(cell);
|
||||
}
|
||||
testDiv.appendChild(grid);
|
||||
|
||||
spyOn(Tray, "open");
|
||||
// Spy on Tray.placeCard: call the onComplete callback immediately.
|
||||
spyOn(Tray, "placeCard").and.callFake((roleCode, cb) => {
|
||||
if (cb) cb();
|
||||
});
|
||||
|
||||
// Capturing guard spy — holds onConfirm so we can fire it per-test
|
||||
window.showGuard = jasmine.createSpy("showGuard").and.callFake(
|
||||
@@ -283,54 +259,128 @@ describe("RoleSelect", () => {
|
||||
document.querySelector("#id_role_select .card").click();
|
||||
});
|
||||
|
||||
it("prepends a .tray-role-card to #id_tray_grid on success", async () => {
|
||||
it("calls Tray.placeCard() on success", async () => {
|
||||
guardConfirm();
|
||||
await Promise.resolve();
|
||||
expect(grid.querySelector(".tray-role-card")).not.toBeNull();
|
||||
expect(Tray.placeCard).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("tray-role-card is the first child of #id_tray_grid", async () => {
|
||||
it("passes the role code string to Tray.placeCard", async () => {
|
||||
guardConfirm();
|
||||
await Promise.resolve();
|
||||
expect(grid.firstElementChild.classList.contains("tray-role-card")).toBe(true);
|
||||
const roleCode = Tray.placeCard.calls.mostRecent().args[0];
|
||||
expect(typeof roleCode).toBe("string");
|
||||
expect(roleCode.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("tray-role-card carries the selected role as data-role", async () => {
|
||||
guardConfirm();
|
||||
await Promise.resolve();
|
||||
const trayCard = grid.querySelector(".tray-role-card");
|
||||
expect(trayCard.dataset.role).toBeTruthy();
|
||||
});
|
||||
|
||||
it("calls Tray.open() on success", async () => {
|
||||
guardConfirm();
|
||||
await Promise.resolve();
|
||||
expect(Tray.open).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not prepend a tray-role-card on server rejection", async () => {
|
||||
it("does not call Tray.placeCard() on server rejection", async () => {
|
||||
window.fetch = jasmine.createSpy("fetch").and.returnValue(
|
||||
Promise.resolve({ ok: false })
|
||||
);
|
||||
guardConfirm();
|
||||
await Promise.resolve();
|
||||
expect(grid.querySelector(".tray-role-card")).toBeNull();
|
||||
expect(Tray.placeCard).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it("does not call Tray.open() on server rejection", async () => {
|
||||
window.fetch = jasmine.createSpy("fetch").and.returnValue(
|
||||
Promise.resolve({ ok: false })
|
||||
// ------------------------------------------------------------------ //
|
||||
// WS turn_changed pause during animation //
|
||||
// ------------------------------------------------------------------ //
|
||||
|
||||
describe("WS turn_changed pause during placeCard animation", () => {
|
||||
let stack, guardConfirm;
|
||||
|
||||
beforeEach(() => {
|
||||
// Six table seats, slot 1 starts active
|
||||
for (let i = 1; i <= 6; i++) {
|
||||
const seat = document.createElement("div");
|
||||
seat.className = "table-seat" + (i === 1 ? " active" : "");
|
||||
seat.dataset.slot = String(i);
|
||||
testDiv.appendChild(seat);
|
||||
}
|
||||
stack = document.createElement("div");
|
||||
stack.className = "card-stack";
|
||||
stack.dataset.state = "ineligible";
|
||||
stack.dataset.userSlots = "1";
|
||||
stack.dataset.starterRoles = "";
|
||||
testDiv.appendChild(stack);
|
||||
|
||||
const grid = document.createElement("div");
|
||||
grid.id = "id_tray_grid";
|
||||
testDiv.appendChild(grid);
|
||||
|
||||
// placeCard spy that holds the onComplete callback
|
||||
let heldCallback = null;
|
||||
spyOn(Tray, "placeCard").and.callFake((roleCode, cb) => {
|
||||
heldCallback = cb; // don't call immediately — simulate animation in-flight
|
||||
});
|
||||
spyOn(Tray, "forceClose");
|
||||
|
||||
// Expose heldCallback so tests can fire it
|
||||
Tray._testFirePlaceCardComplete = () => {
|
||||
if (heldCallback) heldCallback();
|
||||
};
|
||||
|
||||
window.showGuard = jasmine.createSpy("showGuard").and.callFake(
|
||||
(anchor, message, onConfirm) => { guardConfirm = onConfirm; }
|
||||
);
|
||||
guardConfirm();
|
||||
await Promise.resolve();
|
||||
expect(Tray.open).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("grid grows by exactly 1 on success", async () => {
|
||||
const before = grid.children.length;
|
||||
afterEach(() => {
|
||||
delete Tray._testFirePlaceCardComplete;
|
||||
RoleSelect._testReset();
|
||||
});
|
||||
|
||||
it("turn_changed during animation does not call Tray.forceClose immediately", async () => {
|
||||
RoleSelect.openFan();
|
||||
document.querySelector("#id_role_select .card").click();
|
||||
guardConfirm();
|
||||
await Promise.resolve(); // fetch resolves; placeCard called; animation pending
|
||||
|
||||
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
||||
detail: { active_slot: 2, starter_roles: [] }
|
||||
}));
|
||||
expect(Tray.forceClose).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("turn_changed during animation does not immediately move the active seat", async () => {
|
||||
RoleSelect.openFan();
|
||||
document.querySelector("#id_role_select .card").click();
|
||||
guardConfirm();
|
||||
await Promise.resolve();
|
||||
expect(grid.children.length).toBe(before + 1);
|
||||
|
||||
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
||||
detail: { active_slot: 2, starter_roles: [] }
|
||||
}));
|
||||
const activeSeat = testDiv.querySelector(".table-seat.active");
|
||||
expect(activeSeat && activeSeat.dataset.slot).toBe("1"); // still slot 1
|
||||
});
|
||||
|
||||
it("deferred turn_changed is processed when animation completes", async () => {
|
||||
RoleSelect.openFan();
|
||||
document.querySelector("#id_role_select .card").click();
|
||||
guardConfirm();
|
||||
await Promise.resolve();
|
||||
|
||||
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
||||
detail: { active_slot: 2, starter_roles: [] }
|
||||
}));
|
||||
|
||||
// Fire onComplete — deferred turn_changed should now run
|
||||
Tray._testFirePlaceCardComplete();
|
||||
|
||||
const activeSeat = testDiv.querySelector(".table-seat.active");
|
||||
expect(activeSeat && activeSeat.dataset.slot).toBe("2");
|
||||
});
|
||||
|
||||
it("turn_changed after animation completes is processed immediately", () => {
|
||||
// No animation in flight — turn_changed should run right away
|
||||
window.dispatchEvent(new CustomEvent("room:turn_changed", {
|
||||
detail: { active_slot: 2, starter_roles: [] }
|
||||
}));
|
||||
expect(Tray.forceClose).toHaveBeenCalled();
|
||||
const activeSeat = testDiv.querySelector(".table-seat.active");
|
||||
expect(activeSeat && activeSeat.dataset.slot).toBe("2");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -433,9 +483,6 @@ describe("RoleSelect", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("appends a .card to #id_inv_role_card", () => {
|
||||
expect(document.querySelector("#id_inv_role_card .card")).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("dismissing the guard (NVM or outside click)", () => {
|
||||
@@ -463,10 +510,6 @@ describe("RoleSelect", () => {
|
||||
expect(window.fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("does not add a card to inventory", () => {
|
||||
expect(document.querySelector("#id_inv_role_card .card")).toBeNull();
|
||||
});
|
||||
|
||||
it("restores normal mouseleave behaviour on the card", () => {
|
||||
card.dispatchEvent(new MouseEvent("mouseenter"));
|
||||
card.dispatchEvent(new MouseEvent("mouseleave"));
|
||||
|
||||
@@ -422,4 +422,97 @@ describe("Tray", () => {
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
// ---------------------------------------------------------------------- //
|
||||
// placeCard() //
|
||||
// ---------------------------------------------------------------------- //
|
||||
//
|
||||
// placeCard(roleCode, onComplete):
|
||||
// 1. Marks the first .tray-cell with .tray-role-card + data-role.
|
||||
// 2. Opens the tray.
|
||||
// 3. Arc-in animates the cell (.arc-in class, animationend fires).
|
||||
// 4. forceClose() — tray closes instantly.
|
||||
// 5. Calls onComplete.
|
||||
//
|
||||
// The grid always has exactly 8 .tray-cell elements (from the template);
|
||||
// no new elements are inserted.
|
||||
//
|
||||
// ---------------------------------------------------------------------- //
|
||||
|
||||
describe("placeCard()", () => {
|
||||
let grid, firstCell;
|
||||
|
||||
beforeEach(() => {
|
||||
grid = document.createElement("div");
|
||||
grid.id = "id_tray_grid";
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const cell = document.createElement("div");
|
||||
cell.className = "tray-cell";
|
||||
grid.appendChild(cell);
|
||||
}
|
||||
document.body.appendChild(grid);
|
||||
// Re-init so _grid is set (reset() in outer afterEach clears it)
|
||||
Tray.init();
|
||||
firstCell = grid.querySelector(".tray-cell");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
grid.remove();
|
||||
});
|
||||
|
||||
it("adds .tray-role-card to the first .tray-cell", () => {
|
||||
Tray.placeCard("PC", null);
|
||||
expect(firstCell.classList.contains("tray-role-card")).toBe(true);
|
||||
});
|
||||
|
||||
it("sets data-role on the first cell", () => {
|
||||
Tray.placeCard("NC", null);
|
||||
expect(firstCell.dataset.role).toBe("NC");
|
||||
});
|
||||
|
||||
it("grid cell count stays at 8", () => {
|
||||
Tray.placeCard("PC", null);
|
||||
expect(grid.children.length).toBe(8);
|
||||
});
|
||||
|
||||
it("opens the tray", () => {
|
||||
Tray.placeCard("PC", null);
|
||||
expect(Tray.isOpen()).toBe(true);
|
||||
});
|
||||
|
||||
it("adds .arc-in to the first cell", () => {
|
||||
Tray.placeCard("PC", null);
|
||||
expect(firstCell.classList.contains("arc-in")).toBe(true);
|
||||
});
|
||||
|
||||
it("removes .arc-in and force-closes after animationend", () => {
|
||||
Tray.placeCard("PC", null);
|
||||
expect(Tray.isOpen()).toBe(true);
|
||||
firstCell.dispatchEvent(new Event("animationend"));
|
||||
expect(firstCell.classList.contains("arc-in")).toBe(false);
|
||||
expect(Tray.isOpen()).toBe(false);
|
||||
});
|
||||
|
||||
it("calls onComplete after the tray closes", () => {
|
||||
let called = false;
|
||||
Tray.placeCard("PC", () => { called = true; });
|
||||
firstCell.dispatchEvent(new Event("animationend"));
|
||||
expect(called).toBe(true);
|
||||
});
|
||||
|
||||
it("landscape: same behaviour — first cell gets role card", () => {
|
||||
Tray._testSetLandscape(true);
|
||||
Tray.init();
|
||||
Tray.placeCard("EC", null);
|
||||
expect(firstCell.classList.contains("tray-role-card")).toBe(true);
|
||||
expect(firstCell.dataset.role).toBe("EC");
|
||||
});
|
||||
|
||||
it("reset() removes .tray-role-card and data-role from cells", () => {
|
||||
Tray.placeCard("PC", null);
|
||||
Tray.reset();
|
||||
expect(firstCell.classList.contains("tray-role-card")).toBe(false);
|
||||
expect(firstCell.dataset.role).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user