|
| 1 | +import { describe, it } from "node:test"; |
| 2 | +import { Window } from "happy-dom"; |
| 3 | +import assert from "node:assert/strict"; |
| 4 | + |
| 5 | +const window = new Window({ url: "https://example.com/index.html" }); |
| 6 | +globalThis.window = window; |
| 7 | +globalThis.document = window.document; |
| 8 | +window.marked = { parseInline: text => text }; |
| 9 | + |
| 10 | +document.body.innerHTML = ` |
| 11 | + <nav id="nav-menu"><a id="nav-toggler"></a><a id="close-menu"></a></nav> |
| 12 | + <header></header> |
| 13 | + <input id="dark-mode" type="checkbox" /> |
| 14 | + <input id="show-outdated" type="checkbox" checked /> |
| 15 | + <input id="search-input" /> |
| 16 | + <select id="sort-dropdown"> |
| 17 | + <option value="default">Default</option> |
| 18 | + <option value="stars">Stars</option> |
| 19 | + <option value="name">Name</option> |
| 20 | + </select> |
| 21 | + <select id="category-filter"><option value="all">All</option></select> |
| 22 | + <div id="tag-buttons"></div> |
| 23 | + <button id="reset-button"></button> |
| 24 | + <div id="module-count-value"></div> |
| 25 | + <div id="last-update"></div> |
| 26 | + <main id="module-container"></main> |
| 27 | + <template id="card-template"> |
| 28 | + <article class="card"> |
| 29 | + <a class="name"></a> |
| 30 | + <div class="description"></div> |
| 31 | + <div class="maintainer"></div> |
| 32 | + <div class="stars"></div> |
| 33 | + <div class="tags"></div> |
| 34 | + <div class="img-container"><img /><div class="overlay"><img /></div></div> |
| 35 | + <div class="info"> |
| 36 | + <div class="container issues"><a class="text"></a></div> |
| 37 | + <div class="container commit"><a class="text"></a></div> |
| 38 | + <div class="container license"><a class="text"></a></div> |
| 39 | + </div> |
| 40 | + <div class="outdated-note"></div> |
| 41 | + </article> |
| 42 | + </template> |
| 43 | +`; |
| 44 | + |
| 45 | +const modules = [ |
| 46 | + { |
| 47 | + name: "MMM-Alpha", |
| 48 | + maintainer: "Alice", |
| 49 | + url: "https://example.com/alpha", |
| 50 | + description: "Weather module", |
| 51 | + category: "weather", |
| 52 | + tags: ["weather"], |
| 53 | + stars: 5, |
| 54 | + lastCommit: "2026-01-01T00:00:00.000Z", |
| 55 | + defaultSortWeight: 1 |
| 56 | + }, |
| 57 | + { |
| 58 | + name: "MMM-Beta", |
| 59 | + maintainer: "Bob", |
| 60 | + url: "https://example.com/beta", |
| 61 | + description: "Calendar module", |
| 62 | + category: "calendar", |
| 63 | + tags: ["calendar"], |
| 64 | + stars: 10, |
| 65 | + lastCommit: "2026-02-01T00:00:00.000Z", |
| 66 | + defaultSortWeight: 2 |
| 67 | + } |
| 68 | +]; |
| 69 | + |
| 70 | +const skippedModules = [{ name: "MMM-Skipped", error: "Load failed" }]; |
| 71 | +globalThis.fetch = (url) => { |
| 72 | + const responses = { |
| 73 | + "data/modules.min.json": { modules }, |
| 74 | + "data/skipped_modules.json": skippedModules, |
| 75 | + "data/stats.json": { lastUpdate: "2026-03-01T00:00:00.000Z" } |
| 76 | + }; |
| 77 | + return { json: () => Promise.resolve(responses[url]) }; |
| 78 | +}; |
| 79 | + |
| 80 | +await import("../../../website/script.js"); |
| 81 | +await new Promise((resolve) => { |
| 82 | + setTimeout(resolve, 0); |
| 83 | +}); |
| 84 | + |
| 85 | +function cardNames() { |
| 86 | + return [...document.querySelectorAll("#module-container .name")] |
| 87 | + .map(card => card.textContent); |
| 88 | +} |
| 89 | + |
| 90 | +describe("website module catalogue", () => { |
| 91 | + it("loads modules and renders categories", () => { |
| 92 | + assert.deepEqual(cardNames(), ["MMM-Alpha", "MMM-Beta", "MMM-Skipped"]); |
| 93 | + assert.ok(document.querySelector("#category-filter option[value=weather]")); |
| 94 | + assert.equal(document.getElementById("module-count-value").textContent, "3 of 3"); |
| 95 | + }); |
| 96 | + |
| 97 | + it("sorts modules by stars", () => { |
| 98 | + const sortDropdown = document.getElementById("sort-dropdown"); |
| 99 | + sortDropdown.value = "stars"; |
| 100 | + sortDropdown.dispatchEvent(new window.Event("change")); |
| 101 | + |
| 102 | + assert.deepEqual(cardNames(), ["MMM-Beta", "MMM-Alpha", "MMM-Skipped"]); |
| 103 | + }); |
| 104 | + |
| 105 | + it("filters by category and resets the catalogue", () => { |
| 106 | + const categoryFilter = document.getElementById("category-filter"); |
| 107 | + categoryFilter.value = "weather"; |
| 108 | + categoryFilter.dispatchEvent(new window.Event("change")); |
| 109 | + assert.deepEqual(cardNames(), ["MMM-Alpha"]); |
| 110 | + |
| 111 | + document.getElementById("reset-button").click(); |
| 112 | + assert.deepEqual(cardNames(), ["MMM-Alpha", "MMM-Beta", "MMM-Skipped"]); |
| 113 | + }); |
| 114 | +}); |
0 commit comments