Extend message features
This commit is contained in:
1 parent
2665c61d33
commit
439575acd0
20 files changed
+1326
-155
No files matched your search
@@ -0,0 +1,186 @@
|
||||
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/,
|
||||
);
|
||||
});
|
||||
Reference in new issue
Block a user