205 lines
8.7 KiB
JavaScript
205 lines
8.7 KiB
JavaScript
|
|
function flushPromises() {
|
|||
|
|
return new Promise(resolve => setTimeout(resolve, 0));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
describe("NotePage", () => {
|
|||
|
|
let testDiv, item1, item2, don1, doff1, don2, doff2;
|
|||
|
|
|
|||
|
|
function makeItem(slug, isEquipped) {
|
|||
|
|
const li = document.createElement("li");
|
|||
|
|
li.className = "note-item";
|
|||
|
|
li.dataset.slug = slug;
|
|||
|
|
li.dataset.donUrl = `/billboard/note/${slug}/don`;
|
|||
|
|
li.dataset.doffUrl = `/billboard/note/${slug}/doff`;
|
|||
|
|
li.innerHTML = `
|
|||
|
|
<div class="note-don-doff">
|
|||
|
|
<button class="btn btn-equip note-don-btn${isEquipped ? " btn-disabled" : ""}">${isEquipped ? "×" : "DON"}</button>
|
|||
|
|
<button class="btn btn-unequip note-doff-btn${isEquipped ? "" : " btn-disabled"}">${isEquipped ? "DOFF" : "×"}</button>
|
|||
|
|
</div>
|
|||
|
|
<div class="note-item__body"><p class="note-item__title">${slug}</p></div>
|
|||
|
|
`;
|
|||
|
|
return li;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
beforeEach(() => {
|
|||
|
|
NotePage._testReset();
|
|||
|
|
testDiv = document.createElement("div");
|
|||
|
|
testDiv.innerHTML = `<span id="id_greeting_prefix">Welcome,</span><span id="id_greeting_name">Earthman</span>`;
|
|||
|
|
item1 = makeItem("super-schizo", false);
|
|||
|
|
item2 = makeItem("super-nomad", false);
|
|||
|
|
testDiv.appendChild(item1);
|
|||
|
|
testDiv.appendChild(item2);
|
|||
|
|
document.body.appendChild(testDiv);
|
|||
|
|
|
|||
|
|
window.fetch = jasmine.createSpy("fetch").and.returnValue(
|
|||
|
|
Promise.resolve({ ok: true, json: () => Promise.resolve({ title: "Schizoid Man", greeting: "21st Century" }) })
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
don1 = item1.querySelector(".note-don-btn");
|
|||
|
|
doff1 = item1.querySelector(".note-doff-btn");
|
|||
|
|
don2 = item2.querySelector(".note-don-btn");
|
|||
|
|
doff2 = item2.querySelector(".note-doff-btn");
|
|||
|
|
|
|||
|
|
NotePage._init();
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
afterEach(() => {
|
|||
|
|
testDiv.remove();
|
|||
|
|
document.body.classList.remove("notes-locked");
|
|||
|
|
delete window.fetch;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// ── Lock / unlock ─────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
describe("click-lock behaviour", () => {
|
|||
|
|
it("clicking a note adds note-item--locked", () => {
|
|||
|
|
item1.click();
|
|||
|
|
expect(item1.classList.contains("note-item--locked")).toBe(true);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("clicking a note adds notes-locked to body", () => {
|
|||
|
|
item1.click();
|
|||
|
|
expect(document.body.classList.contains("notes-locked")).toBe(true);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("clicking the same note again removes lock", () => {
|
|||
|
|
item1.click();
|
|||
|
|
item1.click();
|
|||
|
|
expect(item1.classList.contains("note-item--locked")).toBe(false);
|
|||
|
|
expect(document.body.classList.contains("notes-locked")).toBe(false);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("clicking a different note moves lock to that note", () => {
|
|||
|
|
item1.click();
|
|||
|
|
item2.click();
|
|||
|
|
expect(item1.classList.contains("note-item--locked")).toBe(false);
|
|||
|
|
expect(item2.classList.contains("note-item--locked")).toBe(true);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("body click clears all locks", () => {
|
|||
|
|
item1.click();
|
|||
|
|
document.body.click();
|
|||
|
|
expect(item1.classList.contains("note-item--locked")).toBe(false);
|
|||
|
|
expect(document.body.classList.contains("notes-locked")).toBe(false);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// ── DON/DOFF state ────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
describe("DON button", () => {
|
|||
|
|
it("clicking DON sends POST to don URL", async () => {
|
|||
|
|
don1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
expect(window.fetch).toHaveBeenCalledWith(
|
|||
|
|
"/billboard/note/super-schizo/don",
|
|||
|
|
jasmine.objectContaining({ method: "POST" })
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("after DON, note gains note-item--donned", async () => {
|
|||
|
|
don1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
expect(item1.classList.contains("note-item--donned")).toBe(true);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("after DON, lock is cleared", async () => {
|
|||
|
|
item1.click(); // lock first
|
|||
|
|
don1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
expect(item1.classList.contains("note-item--locked")).toBe(false);
|
|||
|
|
expect(document.body.classList.contains("notes-locked")).toBe(false);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("after DON, greeting prefix and name are updated", async () => {
|
|||
|
|
don1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
expect(document.getElementById("id_greeting_prefix").innerHTML).toContain("21st Century");
|
|||
|
|
expect(document.getElementById("id_greeting_name").textContent).toBe("Schizoid Man");
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("DONning item1 auto-removes donned state from previously DONned item2", async () => {
|
|||
|
|
item2.classList.add("note-item--donned");
|
|||
|
|
don2.classList.add("btn-disabled"); don2.textContent = "×";
|
|||
|
|
doff2.classList.remove("btn-disabled"); doff2.textContent = "DOFF";
|
|||
|
|
NotePage._donnedItem = item2;
|
|||
|
|
|
|||
|
|
don1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
|
|||
|
|
expect(item2.classList.contains("note-item--donned")).toBe(false);
|
|||
|
|
expect(don2.classList.contains("btn-disabled")).toBe(false);
|
|||
|
|
expect(don2.textContent).toBe("DON");
|
|||
|
|
expect(doff2.classList.contains("btn-disabled")).toBe(true);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("DONning one does not POST a doff for the previous", async () => {
|
|||
|
|
item2.classList.add("note-item--donned");
|
|||
|
|
NotePage._donnedItem = item2;
|
|||
|
|
don1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
// Only one fetch call (the DON), no DOFF call for item2
|
|||
|
|
expect(window.fetch.calls.count()).toBe(1);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("DOFF button", () => {
|
|||
|
|
it("clicking DOFF sends POST to doff URL", async () => {
|
|||
|
|
window.fetch = jasmine.createSpy("fetch").and.returnValue(
|
|||
|
|
Promise.resolve({ ok: true, json: () => Promise.resolve({ greeting: "Welcome,", title: "Earthman" }) })
|
|||
|
|
);
|
|||
|
|
item1.classList.add("note-item--donned");
|
|||
|
|
doff1.classList.remove("btn-disabled"); doff1.textContent = "DOFF";
|
|||
|
|
don1.classList.add("btn-disabled"); don1.textContent = "×";
|
|||
|
|
NotePage._donnedItem = item1;
|
|||
|
|
|
|||
|
|
doff1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
expect(window.fetch).toHaveBeenCalledWith(
|
|||
|
|
"/billboard/note/super-schizo/doff",
|
|||
|
|
jasmine.objectContaining({ method: "POST" })
|
|||
|
|
);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("after DOFF, note loses note-item--donned", async () => {
|
|||
|
|
window.fetch = jasmine.createSpy("fetch").and.returnValue(
|
|||
|
|
Promise.resolve({ ok: true, json: () => Promise.resolve({ greeting: "Welcome,", title: "Earthman" }) })
|
|||
|
|
);
|
|||
|
|
item1.classList.add("note-item--donned");
|
|||
|
|
doff1.classList.remove("btn-disabled"); doff1.textContent = "DOFF";
|
|||
|
|
NotePage._donnedItem = item1;
|
|||
|
|
|
|||
|
|
doff1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
expect(item1.classList.contains("note-item--donned")).toBe(false);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it("after DOFF, greeting reverts to Welcome, / Earthman", async () => {
|
|||
|
|
window.fetch = jasmine.createSpy("fetch").and.returnValue(
|
|||
|
|
Promise.resolve({ ok: true, json: () => Promise.resolve({ greeting: "Welcome,", title: "Earthman" }) })
|
|||
|
|
);
|
|||
|
|
document.getElementById("id_greeting_prefix").textContent = "21st Century";
|
|||
|
|
document.getElementById("id_greeting_name").textContent = "Schizoid Man";
|
|||
|
|
item1.classList.add("note-item--donned");
|
|||
|
|
doff1.classList.remove("btn-disabled"); doff1.textContent = "DOFF";
|
|||
|
|
NotePage._donnedItem = item1;
|
|||
|
|
|
|||
|
|
doff1.click();
|
|||
|
|
await flushPromises();
|
|||
|
|
expect(document.getElementById("id_greeting_prefix").textContent).toBe("Welcome,");
|
|||
|
|
expect(document.getElementById("id_greeting_name").textContent).toBe("Earthman");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// ── Initial donned state ──────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
describe("initial load with an already-donned note", () => {
|
|||
|
|
it("note whose DON is btn-disabled gets note-item--donned on init", () => {
|
|||
|
|
const equippedItem = makeItem("stargazer", true);
|
|||
|
|
testDiv.appendChild(equippedItem);
|
|||
|
|
NotePage._testReset();
|
|||
|
|
NotePage._init();
|
|||
|
|
expect(equippedItem.classList.contains("note-item--donned")).toBe(true);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
});
|