From ff609624537d2f957802704a5dd9faaa8caa9a9e Mon Sep 17 00:00:00 2001 From: Starman <30315137+Starman3787@users.noreply.github.com> Date: Wed, 17 Jul 2024 16:19:19 +0100 Subject: [PATCH] add unit tests for embed builder --- src/constants.js | 8 +- src/util/builder/embedBuilder.js | 47 ++++- test/embedBuilder.js | 343 +++++++++++++++++++++++++++++++ 3 files changed, 390 insertions(+), 8 deletions(-) create mode 100644 test/embedBuilder.js diff --git a/src/constants.js b/src/constants.js index b751d00b7..625e0241c 100644 --- a/src/constants.js +++ b/src/constants.js @@ -308,6 +308,12 @@ module.exports.AUTO_MODERATION_ACTION_TYPES = { module.exports.GATEWAY_RECONNECT_CLOSE_CODES = [4000, 4001, 4002, 4003, 4005, 4007, 4008, 4009]; module.exports.LIMITS = { MAX_EMBED_SIZE: 6000, - MAX_EMBED_DESCRIPTION: 4096 + MAX_EMBED_DESCRIPTION: 4096, + MAX_EMBED_TITLE: 256, + MAX_EMBED_FIELDS: 25, + MAX_EMBED_FIELD_NAME: 256, + MAX_EMBED_FIELD_VALUE: 1024, + MAX_EMBED_FOOTER_TEXT: 2048, + MAX_EMBED_AUTHOR_NAME: 256, }; module.exports.VERSION = require("../package.json").version; \ No newline at end of file diff --git a/src/util/builder/embedBuilder.js b/src/util/builder/embedBuilder.js index 0f4e6c581..a29589956 100644 --- a/src/util/builder/embedBuilder.js +++ b/src/util/builder/embedBuilder.js @@ -1,3 +1,4 @@ +const { LIMITS } = require("../../constants"); const hexToInt = require("../general/hexToInt"); /** @@ -19,8 +20,12 @@ class Embed { * @returns {Embed} */ setTitle(title) { + + if (!title) + throw new TypeError("GLUON: Embed title must be provided."); + this.title = - title && title.length > 256 ? `${title.substring(0, 253)}...` : title; + title && title.length > LIMITS.MAX_EMBED_TITLE ? `${title.substring(0, LIMITS.MAX_EMBED_TITLE - 3)}...` : title; return this; } @@ -31,8 +36,12 @@ class Embed { * @returns {Embed} */ setDescription(text) { + + if (!text) + throw new TypeError("GLUON: Embed description must be provided."); + this.description = - text && text.length > 4096 ? `${text.substring(0, 4093)}...` : text; + text && text.length > LIMITS.MAX_EMBED_DESCRIPTION ? `${text.substring(0, LIMITS.MAX_EMBED_DESCRIPTION - 3)}...` : text; return this; } @@ -43,6 +52,10 @@ class Embed { * @returns {Embed} */ setURL(url) { + + if (!url) + throw new TypeError("GLUON: Embed url must be provided."); + this.url = url; return this; @@ -66,6 +79,10 @@ class Embed { * @returns {Embed} */ setColor(colour) { + + if (!colour) + throw new TypeError("GLUON: Embed colour must be provided."); + if (typeof colour == "string") { if (colour[0] == "#") colour = colour.substring(1); @@ -81,6 +98,10 @@ class Embed { * @returns {Embed} */ setThumbnail(url) { + + if (!url) + throw new TypeError("GLUON: Embed thumbnail url must be provided."); + this.thumbnail = { url, }; @@ -95,8 +116,12 @@ class Embed { * @returns {Embed} */ setFooter(text, icon) { + + if (!text) + throw new TypeError("GLUON: Embed footer text must be provided."); + this.footer = { - text: text && text.length > 2048 ? `${text.substring(0, 2045)}...` : text, + text: text && text.length > LIMITS.MAX_EMBED_FOOTER_TEXT ? `${text.substring(0, LIMITS.MAX_EMBED_FOOTER_TEXT - 3)}...` : text, }; if (icon) this.footer.icon_url = icon; @@ -111,9 +136,13 @@ class Embed { * @returns {Embed} */ setAuthor(name, url, icon_url) { + + if (!name) + throw new TypeError("GLUON: Embed author name must be provided."); + this.author = {}; - if (name) this.author.name = name; + if (name) this.author.name = (name && name.length > LIMITS.MAX_EMBED_AUTHOR_NAME ? `${name.substring(0, LIMITS.MAX_EMBED_AUTHOR_NAME - 3)}...` : name); if (url) this.author.url = url; if (icon_url) this.author.icon_url = icon_url; @@ -128,12 +157,16 @@ class Embed { * @returns {Embed} */ addField(name, value, inline = false) { - if (this.fields.length == 25) return this; + if (this.fields.length == LIMITS.MAX_EMBED_FIELDS) + throw new RangeError(`GLUON: Embed fields cannot exceed ${LIMITS.MAX_EMBED_FIELDS} fields.`); + + if (!name || !value) + throw new TypeError("GLUON: Embed field name and value must be provided."); this.fields.push({ - name: name && name.length > 256 ? `${name.substring(0, 253)}...` : name, + name: name && name.length > LIMITS.MAX_EMBED_FIELD_NAME ? `${name.substring(0, LIMITS.MAX_EMBED_FIELD_NAME - 3)}...` : name, value: - value && value.length > 1024 ? `${value.substring(0, 1021)}...` : value, + value && value.length > LIMITS.MAX_EMBED_FIELD_VALUE ? `${value.substring(0, LIMITS.MAX_EMBED_FIELD_VALUE - 3)}...` : value, inline, }); diff --git a/test/embedBuilder.js b/test/embedBuilder.js new file mode 100644 index 000000000..ed5d5d2e5 --- /dev/null +++ b/test/embedBuilder.js @@ -0,0 +1,343 @@ +let expect; +before(async () => { + expect = (await import("chai")).expect; +}); +const hexToInt = require("../src/util/general/hexToInt"); +const Embed = require("../src/util/builder/embedBuilder"); +const { LIMITS } = require("../src/constants"); + +describe("Embed", function () { + context("check import", function () { + it("should be an object", function () { + const embed = new Embed(); + expect(embed).to.be.an("object"); + }); + }); + + context("check setTitle", function () { + it("should have method setTitle", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setTitle"); + }); + it("should set the title of the embed", function () { + const embed = new Embed(); + embed.setTitle("title"); + expect(embed.title).to.equal("title"); + }); + it("should throw an error if the title is empty", function () { + const embed = new Embed(); + expect(() => { + embed.setTitle(undefined); + }).to.throw(TypeError, "GLUON: Embed title must be provided."); + }); + it("should not allow the character limit to be exceeded", function () { + const embed = new Embed(); + embed.setTitle("a".repeat(LIMITS.MAX_EMBED_TITLE + 1)); + expect(embed.title.length).to.equal(LIMITS.MAX_EMBED_TITLE); + }); + }); + + context("check setDescription", function () { + it("should have method setDescription", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setDescription"); + }); + it("should set the description of the embed", function () { + const embed = new Embed(); + embed.setDescription("description"); + expect(embed.description).to.equal("description"); + }); + it("should throw an error if the description is empty", function () { + const embed = new Embed(); + expect(() => { + embed.setDescription(undefined); + }).to.throw(TypeError, "GLUON: Embed description must be provided."); + }); + it("should not allow the character limit to be exceeded", function () { + const embed = new Embed(); + embed.setDescription("a".repeat(LIMITS.MAX_EMBED_DESCRIPTION + 1)); + expect(embed.description.length).to.equal(LIMITS.MAX_EMBED_DESCRIPTION); + }); + }); + + context("check setURL", function () { + it("should have method setURL", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setURL"); + }); + it("should set the url of the embed", function () { + const embed = new Embed(); + embed.setURL("url"); + expect(embed.url).to.equal("url"); + }); + it("should throw an error if the url is empty", function () { + const embed = new Embed(); + expect(() => { + embed.setURL(undefined); + }).to.throw(TypeError, "GLUON: Embed url must be provided."); + }); + }); + + context("check setColor", function () { + it("should have method setColor", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setColor"); + }); + it("should set the color of the embed", function () { + const embed = new Embed(); + embed.setColor("#fcfcfc"); + expect(embed.color).to.equal(hexToInt("fcfcfc")); + }); + it("should throw an error if the color is empty", function () { + const embed = new Embed(); + expect(() => { + embed.setColor(undefined); + }).to.throw(TypeError, "GLUON: Embed colour must be provided."); + }); + }); + + context("check setTimestamp", function () { + it("should have method setTimestamp", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setTimestamp"); + }); + it("should set the timestamp of the embed", function () { + const embed = new Embed(); + embed.setTimestamp(123456); + expect(embed.timestamp).to.equal(new Date(123456 * 1000).toISOString()); + }); + }); + + context("check setFooter", function () { + it("should have method setFooter", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setFooter"); + }); + it("should set the footer of the embed", function () { + const embed = new Embed(); + embed.setFooter("footer", "icon"); + expect(embed.footer.text).to.equal("footer"); + expect(embed.footer.icon_url).to.equal("icon"); + }); + it("should throw an error if the text is empty", function () { + const embed = new Embed(); + expect(() => { + embed.setFooter(undefined, "icon"); + }).to.throw(TypeError, "GLUON: Embed footer text must be provided."); + }); + it("should not allow the character limit to be exceeded", function () { + const embed = new Embed(); + embed.setFooter("a".repeat(LIMITS.MAX_EMBED_FOOTER_TEXT + 1)); + expect(embed.footer.text.length).to.equal(LIMITS.MAX_EMBED_FOOTER_TEXT); + }); + }); + + context("check setImage", function () { + it("should have method setImage", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setImage"); + }); + it("should set the image of the embed", function () { + const embed = new Embed(); + embed.setImage("image"); + expect(embed.image.url).to.equal("image"); + }); + }); + + context("check setThumbnail", function () { + it("should have method setThumbnail", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setThumbnail"); + }); + it("should set the thumbnail of the embed", function () { + const embed = new Embed(); + embed.setThumbnail("thumbnail"); + expect(embed.thumbnail.url).to.equal("thumbnail"); + }); + it("should throw an error if the url is empty", function () { + const embed = new Embed(); + expect(() => { + embed.setThumbnail(undefined); + }).to.throw(TypeError, "GLUON: Embed thumbnail url must be provided."); + }); + }); + + context("check setAuthor", function () { + it("should have method setAuthor", function () { + const embed = new Embed(); + expect(embed).to.respondTo("setAuthor"); + }); + it("should set the author of the embed", function () { + const embed = new Embed(); + embed.setAuthor("author"); + expect(embed.author.name).to.equal("author"); + }); + it("should throw an error if the name is empty", function () { + const embed = new Embed(); + expect(() => { + embed.setAuthor(undefined); + }).to.throw(TypeError, "GLUON: Embed author name must be provided."); + }); + it("should set the author url of the embed", function () { + const embed = new Embed(); + embed.setAuthor("author", "url"); + expect(embed.author.url).to.equal("url"); + }); + it("should not allow the character limit to be exceeded", function () { + const embed = new Embed(); + embed.setAuthor("a".repeat(LIMITS.MAX_EMBED_AUTHOR_NAME + 1)); + expect(embed.author.name.length).to.equal(LIMITS.MAX_EMBED_AUTHOR_NAME); + }); + }); + + context("check addField", function () { + it("should have method addField", function () { + const embed = new Embed(); + expect(embed).to.respondTo("addField"); + }); + it("should add a field to the embed", function () { + const embed = new Embed(); + embed.addField("field", "fieldValue"); + expect(embed.fields.find((f) => f.name == "field").name).to.equal( + "field" + ); + expect(embed.fields.find((f) => f.name == "field").value).to.equal( + "fieldValue" + ); + }); + it("should not add a field if the field is empty", function () { + const embed = new Embed(); + expect(() => { + embed.addField(undefined, "fieldValue"); + }).to.throw( + TypeError, + "GLUON: Embed field name and value must be provided." + ); + expect(() => { + embed.addField("field", undefined); + }).to.throw( + TypeError, + "GLUON: Embed field name and value must be provided." + ); + expect(() => { + embed.addField(undefined, undefined); + }).to.throw( + TypeError, + "GLUON: Embed field name and value must be provided." + ); + }); + it("should throw an error if too many fields are added", function () { + const embed = new Embed(); + for (let i = 0; i < 25; i++) { + embed.addField("field", "fieldValue"); + } + expect(() => { + embed.addField("field", "fieldValue"); + }).to.throw( + RangeError, + `GLUON: Embed fields cannot exceed ${LIMITS.MAX_EMBED_FIELDS} fields.` + ); + }); + it("should not exceed the value limit", function () { + const embed = new Embed(); + embed.addField("field", "a".repeat(LIMITS.MAX_EMBED_FIELD_VALUE + 1)); + expect(embed.fields.find((f) => f.name == "field").value.length).to.equal( + LIMITS.MAX_EMBED_FIELD_VALUE + ); + }); + it("should not exceed the name limit", function () { + const embed = new Embed(); + embed.addField("a".repeat(256), "fieldValue"); + expect( + embed.fields.find((f) => f.value == "fieldValue").name.length + ).to.equal(LIMITS.MAX_EMBED_FIELD_NAME); + }); + }); + + context("check toString", function () { + it("should have method toString", function () { + const embed = new Embed(); + expect(embed).to.respondTo("toString"); + }); + it("should return the embed as a string", function () { + const embed = new Embed(); + embed.setTitle("title"); + embed.setDescription("description"); + embed.setURL("url"); + embed.setColor("color"); + embed.setTimestamp(123456); + embed.setFooter("footer"); + embed.setImage("image"); + embed.setThumbnail("thumbnail"); + embed.setAuthor("author"); + embed.addField("field", "fieldValue"); + expect(embed.toString()).to.equal( + "## title\n\ndescription\n\n**field**:\nfieldValue\nfooter" + ); + }); + }); + + context("check embed character count", function () { + it("should return the correct character count", function () { + const embed = new Embed(); + embed.setTitle("title"); + embed.setDescription("description"); + embed.setURL("url"); + embed.setColor("color"); + embed.setTimestamp(123456); + embed.setFooter("footer"); + embed.setImage("image"); + embed.setThumbnail("thumbnail"); + embed.setAuthor("author"); + embed.addField("field", "fieldValue"); + expect(embed.characterCount).to.equal(43); + }); + }); + + context("check toJSON method", function () { + it("should have method toJSON", function () { + const embed = new Embed(); + expect(embed).to.respondTo("toJSON"); + }); + it("should return the embed as an object", function () { + const embed = new Embed(); + embed.setTitle("title"); + embed.setDescription("description"); + embed.setURL("url"); + embed.setColor("color"); + embed.setTimestamp(123456); + embed.setFooter("footer"); + embed.setImage("image"); + embed.setThumbnail("thumbnail"); + embed.setAuthor("author"); + embed.addField("field", "fieldValue"); + expect(embed.toJSON()).to.eql({ + type: "rich", + title: "title", + description: "description", + url: "url", + color: hexToInt("color"), + timestamp: new Date(123456 * 1000).toISOString(), + footer: { + text: "footer", + }, + image: { + url: "image", + }, + thumbnail: { + url: "thumbnail", + }, + author: { + name: "author", + }, + fields: [ + { + name: "field", + value: "fieldValue", + inline: false + }, + ], + }); + }); + }); +});