import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; import ts from "typescript"; // Run the pure editor helpers without a browser or additional test dependencies. const source = await readFile( new URL("../src/components/Reminder/message.ts", import.meta.url), "utf8", ); const { outputText } = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2020 }, }); const { addEmbed, embedFields, prepareReminder, reminderEmbeds, removeEmbed, updateEmbed, addLinkButton, canAddLinkButton, linkButtons, updateLinkButton, } = await import(`data:text/javascript;base64,${Buffer.from(outputText).toString("base64")}`); function reminder(embeds) { return { content: "hello", tts: false, username: "", avatar: null, attachment_name: null, uid: "unchanged", utc_time: "2026-10-12T12:00:00", ...embedFields(embeds?.[0] || {}), message_json: embeds ? { embeds, allowed_mentions: { parse: [] } } : null, }; } test("editing one embed preserves the others and unedited payload properties", () => { const original = reminder([ { title: "first" }, { description: "second", author: { name: "author", url: "https://example.com" } }, ]); const edited = updateEmbed(original, 1, (r) => ({ ...r, embed_description: "updated" })); const payload = prepareReminder(edited).message_json; assert.equal(payload.embeds[0].title, "first"); assert.equal(payload.embeds[1].description, "updated"); assert.equal(payload.embeds[1].author.url, "https://example.com"); assert.deepEqual(payload.allowed_mentions, { parse: [] }); assert.equal(edited.uid, original.uid); assert.equal(original.message_json.embeds[1].description, "second"); }); test("adding stops at ten embeds and removal permits another", () => { let value = reminder([{ title: "first" }]); for (let i = 0; i < 12; i++) value = addEmbed(value); assert.equal(reminderEmbeds(value).length, 10); assert.equal(addEmbed(value), value); value = removeEmbed(value, 0); assert.equal(reminderEmbeds(value).length, 9); value = updateEmbed(value, 0, (r) => ({ ...r, embed_title: "new first" })); assert.equal(prepareReminder(value).message_json.embeds[0].title, "new first"); assert.equal(reminderEmbeds(addEmbed(value)).length, 10); }); test("saving and reloading multiple embeds preserves order and drops blank editors", () => { let value = addEmbed(reminder([{ description: "first" }])); value = updateEmbed(value, 1, (r) => ({ ...r, embed_description: "second" })); value = addEmbed(value); const saved = prepareReminder(value); assert.deepEqual( saved.message_json.embeds.map((embed) => embed.description), ["first", "second"], ); assert.deepEqual(reminderEmbeds(JSON.parse(JSON.stringify(saved))), saved.message_json.embeds); assert.deepEqual(prepareReminder(reminder()).message_json.embeds, []); }); test("legacy embeds are retained when a second embed is added", () => { const legacy = { ...reminder(), embed_title: "legacy", embed_fields: [{ title: "name", value: "value", inline: true }], }; const saved = prepareReminder(addEmbed(legacy)); assert.equal(saved.message_json.embeds[0].title, "legacy"); assert.deepEqual(saved.message_json.embeds[0].fields, [ { name: "name", value: "value", inline: true }, ]); }); test("combined embed text accepts 6000 characters and rejects 6001", () => { const embeds = [{ description: "x".repeat(3000) }, { description: "y".repeat(3000) }]; assert.equal(prepareReminder(reminder(embeds)).message_json.embeds.length, 2); assert.throws( () => prepareReminder(reminder([embeds[0], { description: "y".repeat(3001) }])), /6000/, ); assert.throws( () => prepareReminder(reminder(Array.from({ length: 11 }, () => ({ title: "embed" })))), /10 embeds/, ); }); test("link buttons can be added, edited, saved, reloaded and removed without losing embeds", () => { const original = reminder([{ title: "first" }, { title: "second" }]); let value = addLinkButton(original); value = updateLinkButton(value, 0, 0, { label: "Visit", url: "https://example.com" }); const saved = prepareReminder(value); assert.deepEqual(saved.message_json.components, [ { type: 1, components: [{ type: 2, style: 5, label: "Visit", url: "https://example.com" }], }, ]); assert.deepEqual(saved.message_json.embeds, original.message_json.embeds); assert.deepEqual(saved.message_json.allowed_mentions, { parse: [] }); assert.equal( linkButtons(JSON.parse(JSON.stringify(saved)))[0].button.url, "https://example.com", ); assert.deepEqual( prepareReminder(updateLinkButton(saved, 0, 0, null)).message_json.components, [], ); assert.equal(original.message_json.components, undefined); const legacy = updateLinkButton(addLinkButton({ ...reminder(), embed_title: "legacy" }), 0, 0, { url: "https://example.com", }); assert.equal(prepareReminder(legacy).message_json.embeds[0].title, "legacy"); }); test("link buttons fill five rows of five and preserve other components", () => { let value = reminder(); for (let i = 0; i < 26; i++) value = addLinkButton(value); assert.equal(linkButtons(value).length, 25); assert.deepEqual( value.message_json.components.map((row) => row.components.length), [5, 5, 5, 5, 5], ); assert.equal(canAddLinkButton(value), false); assert.equal(addLinkButton(value), value); value = updateLinkButton(value, 2, 2, null); assert.equal(canAddLinkButton(value), true); assert.equal(linkButtons(addLinkButton(value)).length, 25); const other = { type: 1, id: 7, components: [{ type: 3, custom_id: "select", options: [] }] }; value = { ...reminder(), message_json: { components: [other] } }; value = addLinkButton(value); assert.deepEqual(value.message_json.components[0], other); assert.equal(linkButtons(value)[0].rowIndex, 1); }); test("invalid link button URLs, labels and row limits are rejected on save", () => { let value = addLinkButton(reminder()); assert.throws(() => prepareReminder(value), /URL/); value = updateLinkButton(value, 0, 0, { url: "https://example.com/" + "x".repeat(492), label: "x".repeat(80), }); assert.equal(prepareReminder(value).message_json.components.length, 1); for (const change of [ { url: "invalid" }, { url: "javascript:alert(1)" }, { url: "https://example.com/" + "x".repeat(493) }, { label: "" }, { label: "x".repeat(81) }, ]) assert.throws(() => prepareReminder(updateLinkButton(value, 0, 0, change))); const row = value.message_json.components[0]; assert.throws( () => prepareReminder({ ...value, message_json: { components: Array(6).fill(row) } }), /5 button rows/, ); assert.throws( () => prepareReminder({ ...value, message_json: { components: [{ ...row, components: Array(6).fill(row.components[0]) }], }, }), /5 components/, ); });