ChannelPagination.test.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. import { ButtonInteraction, InteractionCollector, Message, MessageActionRow, MessageButton, MessageComponentCollectorOptions, MessageEmbed, TextChannel } from "discord.js";
  2. import ChannelPagination from "../../src/Classes/Paginations/ChannelPagination";
  3. import FirstPageButton from "../../src/Classes/Buttons/FirstPageButton";
  4. import LastPageButton from "../../src/Classes/Buttons/LastPageButton";
  5. import PreviousPageButton from "../../src/Classes/Buttons/PreviousPageButton";
  6. import NextPageButton from "../../src/Classes/Buttons/NextPageButton";
  7. import StopButton from "../../src/Classes/Buttons/StopButton";
  8. describe("Pagination that is sent to a channel", () => {
  9. test("should initialize with default values.", () => {
  10. const pagination = new ChannelPagination();
  11. expect(pagination.messageOptions).toBeNull();
  12. });
  13. test("should initialize from another ChannelPagination.", () => {
  14. const pagination = new ChannelPagination()
  15. .setMessageOptions({allowedMentions: {repliedUser: false}})
  16. .setFilterOptions({noAccessReply: true, singleUserAccess: true}),
  17. { filterOptions, messageOptions } = pagination,
  18. newPagination = new ChannelPagination(pagination);
  19. expect(newPagination.filterOptions).toBe(filterOptions);
  20. expect(newPagination.messageOptions).toBe(messageOptions);
  21. });
  22. test("should set messageOptions and clear embeds and components fields.", () => {
  23. const pagination = new ChannelPagination(),
  24. messageOptions = {components: [new MessageActionRow<MessageButton>()], embeds: [new MessageEmbed()], allowedMentions: {repliedUser: false}}
  25. expect(pagination.setMessageOptions(messageOptions).messageOptions).toStrictEqual({components: [], embeds: [], allowedMentions: {repliedUser: false}});
  26. });
  27. test("sending should fail if there are no embeds.", () => {
  28. const pagination = new ChannelPagination()
  29. .setButtons(new FirstPageButton(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER")))
  30. .setTime(60000);
  31. const channelMock:TextChannel = ({} as unknown) as TextChannel;
  32. expect(async () => await pagination.send(channelMock)).rejects.toThrow("Pagination should have at least one button, page and settep up time.");
  33. });
  34. test("sending should fail if there are no buttons.", () => {
  35. const pagination = new ChannelPagination()
  36. .setEmbeds(new MessageEmbed().setDescription("description"))
  37. .setTime(60000);
  38. const channelMock:TextChannel = ({} as unknown) as TextChannel;
  39. expect(async () => await pagination.send(channelMock)).rejects.toThrow("Pagination should have at least one button, page and settep up time.");
  40. });
  41. test("sending should fail if there are no time setted up.", () => {
  42. const pagination = new ChannelPagination()
  43. .setButtons(new FirstPageButton(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER")))
  44. .setEmbeds(new MessageEmbed().setDescription("description"));
  45. const channelMock:TextChannel = ({} as unknown) as TextChannel;
  46. expect(async () => await pagination.send(channelMock)).rejects.toThrow("Pagination should have at least one button, page and settep up time.");
  47. });
  48. test("should send a message.", async () => {
  49. const pagination = new ChannelPagination()
  50. .setButtons(new FirstPageButton(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER")))
  51. .setEmbeds(new MessageEmbed().setDescription("description"))
  52. .setTime(60000),
  53. interactionCollectorMock:InteractionCollector<ButtonInteraction> = ({on: jest.fn()} as unknown) as InteractionCollector<ButtonInteraction>,
  54. messageMock:Message = ({createMessageComponentCollector: jest.fn(() => interactionCollectorMock)} as unknown) as Message,
  55. channelMock:TextChannel = ({send: jest.fn(() => messageMock)} as unknown) as TextChannel;
  56. await pagination.send(channelMock);
  57. expect(channelMock.send).toHaveBeenCalledWith({
  58. embeds: [new MessageEmbed().setDescription("description")],
  59. components: [new MessageActionRow().setComponents(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER").setDisabled(true))]
  60. });
  61. });
  62. test("should execute afterSending action.", async () => {
  63. const actionAfterSending = jest.fn(),
  64. pagination = new ChannelPagination()
  65. .setAfterSendingAction(actionAfterSending)
  66. .setButtons(new FirstPageButton(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER")))
  67. .setEmbeds(new MessageEmbed().setDescription("description"))
  68. .setTime(60000),
  69. interactionCollectorMock:InteractionCollector<ButtonInteraction> = ({on: jest.fn()} as unknown) as InteractionCollector<ButtonInteraction>,
  70. messageMock:Message = ({createMessageComponentCollector: jest.fn(() => interactionCollectorMock)} as unknown) as Message,
  71. channelMock:TextChannel = ({send: jest.fn(() => messageMock)} as unknown) as TextChannel;
  72. await pagination.send(channelMock);
  73. expect(actionAfterSending).toHaveBeenCalledWith(messageMock);
  74. });
  75. test("all methods that change class properties should throw errors.", async () => {
  76. const pagination = new ChannelPagination()
  77. .setButtons(new FirstPageButton(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER")))
  78. .setEmbeds(new MessageEmbed().setDescription("description"))
  79. .setTime(60000),
  80. interactionCollectorMock:InteractionCollector<ButtonInteraction> = ({on: jest.fn()} as unknown) as InteractionCollector<ButtonInteraction>,
  81. messageMock:Message = ({createMessageComponentCollector: jest.fn(() => interactionCollectorMock)} as unknown) as Message,
  82. channelMock:TextChannel = ({send: jest.fn(() => messageMock)} as unknown) as TextChannel;
  83. await pagination.send(channelMock);
  84. await expect(async () => await pagination.send(channelMock)).rejects.toThrow("The pagination is already sent.");
  85. expect(() => pagination.setTime(60000)).toThrow("The pagination is already sent.");
  86. expect(() => pagination.setOnStopAction(() => {})).toThrow("The pagination is already sent.");
  87. expect(() => pagination.setMessageOptions({stickers: []})).toThrow("The pagination is already sent.");
  88. expect(() => pagination.setEmbeds(new MessageEmbed().setDescription("description"))).toThrow("The pagination is already sent.");
  89. expect(() => pagination.setCollectorOptions({maxIdleTime: 10})).toThrow("The pagination is already sent.");
  90. expect(() => pagination.setButtons(new FirstPageButton(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER")))).toThrow("The pagination is already sent.");
  91. expect(() => pagination.setAfterSendingAction(() => {})).toThrow("The pagination is already sent.");
  92. expect(() => pagination.removeEmbeds(1)).toThrow("The pagination is already sent.");
  93. expect(() => pagination.insertEmbeds(new MessageEmbed().setDescription("description"))).toThrow("The pagination is already sent.");
  94. expect(() => pagination.setFilterOptions({singleUserAccess: true})).toThrow("The pagination is already sent.");
  95. });
  96. test("filter should filter out messages with another ids.", async () => {
  97. const pagination = new ChannelPagination()
  98. .setButtons(new FirstPageButton(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER")))
  99. .setEmbeds(new MessageEmbed().setDescription("description"))
  100. .setTime(60000),
  101. interactionCollectorMock:InteractionCollector<ButtonInteraction> = ({on: jest.fn(), constructor: jest.fn()} as unknown) as InteractionCollector<ButtonInteraction>,
  102. collectorMock = jest.fn((data:MessageComponentCollectorOptions<ButtonInteraction>) => interactionCollectorMock),
  103. messageMock:Message = ({createMessageComponentCollector: collectorMock, id: 1} as unknown) as Message,
  104. channelMock:TextChannel = ({send: jest.fn(() => messageMock)} as unknown) as TextChannel;
  105. await pagination.send(channelMock);
  106. if (!collectorMock.mock.calls[0][0].filter)
  107. throw new Error("Filter should be defined!");
  108. const interactionMock:ButtonInteraction = ({message: {id: 2}} as unknown) as ButtonInteraction,
  109. result = await collectorMock.mock.calls[0][0].filter(interactionMock);
  110. expect(result).toBeFalsy();
  111. });
  112. test("should set interactionCollector events' scripts.", async () => {
  113. const pagination = new ChannelPagination()
  114. .setButtons(new FirstPageButton(new MessageButton().setCustomId("testid").setEmoji("▶").setStyle("DANGER")))
  115. .setEmbeds(new MessageEmbed().setDescription("description"))
  116. .setTime(60000),
  117. mockOn = jest.fn(),
  118. interactionCollectorMock:InteractionCollector<ButtonInteraction> = ({on: mockOn} as unknown) as InteractionCollector<ButtonInteraction>,
  119. messageMock:Message = ({createMessageComponentCollector: jest.fn(() => interactionCollectorMock)} as unknown) as Message,
  120. channelMock:TextChannel = ({send: jest.fn(() => messageMock)} as unknown) as TextChannel;
  121. await pagination.send(channelMock);
  122. expect(mockOn).toHaveBeenCalledTimes(2);
  123. expect(mockOn.mock.calls[0][0]).toBe("collect");
  124. expect(mockOn.mock.calls[0][1]).toBeInstanceOf(Function);
  125. expect(mockOn.mock.calls[1][0]).toBe("end");
  126. expect(mockOn.mock.calls[1][1]).toBeInstanceOf(Function);
  127. });
  128. test("should edit message on collect.", async () => {
  129. const buttonStyles =
  130. [
  131. new MessageButton().setCustomId("first").setLabel("First").setStyle("SUCCESS"),
  132. new MessageButton().setCustomId("prev").setLabel("Previous").setStyle("PRIMARY"),
  133. new MessageButton().setCustomId("stop").setLabel("Stop").setStyle("DANGER"),
  134. new MessageButton().setCustomId("next").setLabel("Next").setStyle("PRIMARY"),
  135. new MessageButton().setCustomId("last").setLabel("Last").setStyle("SUCCESS")
  136. ],
  137. buttons =
  138. [
  139. new FirstPageButton(buttonStyles[0]),
  140. new PreviousPageButton(buttonStyles[1]),
  141. new StopButton(buttonStyles[2]),
  142. new NextPageButton(buttonStyles[3]),
  143. new LastPageButton(buttonStyles[4])
  144. ],
  145. embeds =
  146. [
  147. new MessageEmbed().setColor("RANDOM").setDescription("First page!"),
  148. new MessageEmbed().setColor("RANDOM").setDescription("Wow! It's second page!"),
  149. new MessageEmbed().setColor("RANDOM").setDescription("Unbelivable! Third class page!"),
  150. new MessageEmbed().setColor("RANDOM").setDescription("Not possible! Fourth page!"),
  151. new MessageEmbed().setColor("RANDOM").setDescription("Not probable! Special fifth page!"),
  152. new MessageEmbed().setColor("RANDOM").setDescription("Progress! It's page with number six that is stored with number five!"),
  153. new MessageEmbed().setColor("RANDOM").setDescription("You're feeling with determination because of the seven page!"),
  154. new MessageEmbed().setColor("RANDOM").setDescription("You shall not pass! It's the last and the latest page!"),
  155. ],
  156. pagination = new ChannelPagination()
  157. .setFilterOptions({resetTimer: true})
  158. .setButtons(buttons)
  159. .setEmbeds(embeds)
  160. .setTime(60000),
  161. mockOn = jest.fn(),
  162. editMock = jest.fn(),
  163. interactionCollectorMock:InteractionCollector<ButtonInteraction> = ({on: mockOn, resetTimer: jest.fn()} as unknown) as InteractionCollector<ButtonInteraction>,
  164. messageMock:Message = ({createMessageComponentCollector: jest.fn(() => interactionCollectorMock)} as unknown) as Message,
  165. channelMock:TextChannel = ({send: jest.fn(() => messageMock)} as unknown) as TextChannel;
  166. await pagination.send(channelMock);
  167. const collectEvent = mockOn.mock.calls[0][1];
  168. let interactionMock:ButtonInteraction = ({customId: "last", deferUpdate: jest.fn(), editReply: editMock} as unknown) as ButtonInteraction;
  169. await collectEvent(interactionMock);
  170. interactionMock = ({customId: "prev", deferUpdate: jest.fn(), editReply: editMock} as unknown) as ButtonInteraction;
  171. await collectEvent(interactionMock);
  172. interactionMock = ({customId: "next", deferUpdate: jest.fn(), editReply: editMock} as unknown) as ButtonInteraction;
  173. await collectEvent(interactionMock);
  174. interactionMock = ({customId: "first", deferUpdate: jest.fn(), editReply: editMock} as unknown) as ButtonInteraction;
  175. await collectEvent(interactionMock);
  176. expect(editMock.mock.calls[0][0]).toStrictEqual({embeds: [embeds[embeds.length - 1]], components: [new MessageActionRow().setComponents([
  177. buttonStyles[0],
  178. buttonStyles[1],
  179. buttonStyles[2],
  180. buttonStyles[3].setDisabled(true),
  181. buttonStyles[4].setDisabled(true)
  182. ])]});
  183. expect(editMock.mock.calls[1][0]).toStrictEqual({embeds: [embeds[embeds.length - 2]], components: [new MessageActionRow().setComponents([
  184. buttonStyles[0],
  185. buttonStyles[1],
  186. buttonStyles[2],
  187. buttonStyles[3],
  188. buttonStyles[4]
  189. ])]});
  190. expect(editMock.mock.calls[2][0]).toStrictEqual({embeds: [embeds[embeds.length - 1]], components: [new MessageActionRow().setComponents([
  191. buttonStyles[0],
  192. buttonStyles[1],
  193. buttonStyles[2],
  194. buttonStyles[3].setDisabled(true),
  195. buttonStyles[4].setDisabled(true)
  196. ])]});
  197. expect(editMock.mock.calls[3][0]).toStrictEqual({embeds: [embeds[0]], components: [new MessageActionRow().setComponents([
  198. buttonStyles[0].setDisabled(true),
  199. buttonStyles[1].setDisabled(true),
  200. buttonStyles[2],
  201. buttonStyles[3],
  202. buttonStyles[4]
  203. ])]});
  204. });
  205. test("should stop pagination and trigger onStop action if stop button is triggered.", async () => {
  206. const buttons =
  207. [
  208. new FirstPageButton(new MessageButton().setCustomId("first").setLabel("First").setStyle("SUCCESS")),
  209. new PreviousPageButton(new MessageButton().setCustomId("prev").setLabel("Previous").setStyle("PRIMARY")),
  210. new StopButton(new MessageButton().setCustomId("stop").setLabel("Stop").setStyle("DANGER")),
  211. new NextPageButton(new MessageButton().setCustomId("next").setLabel("Next").setStyle("PRIMARY")),
  212. new LastPageButton(new MessageButton().setCustomId("last").setLabel("Last").setStyle("SUCCESS"))
  213. ],
  214. embeds =
  215. [
  216. new MessageEmbed().setColor("RANDOM").setDescription("First page!"),
  217. new MessageEmbed().setColor("RANDOM").setDescription("Wow! It's second page!"),
  218. new MessageEmbed().setColor("RANDOM").setDescription("Unbelivable! Third class page!"),
  219. new MessageEmbed().setColor("RANDOM").setDescription("Not possible! Fourth page!"),
  220. new MessageEmbed().setColor("RANDOM").setDescription("Not probable! Special fifth page!"),
  221. new MessageEmbed().setColor("RANDOM").setDescription("Progress! It's page with number six that is stored with number five!"),
  222. new MessageEmbed().setColor("RANDOM").setDescription("You're feeling with determination because of the seven page!"),
  223. new MessageEmbed().setColor("RANDOM").setDescription("You shall not pass! It's the last and the latest page!"),
  224. ],
  225. pagination = new ChannelPagination()
  226. .setFilterOptions({resetTimer: true, removeButtonsOnEnd: true})
  227. .setOnStopAction(jest.fn())
  228. .setButtons(buttons)
  229. .setEmbeds(embeds)
  230. .setTime(60000),
  231. mockOn = jest.fn(),
  232. editMock = jest.fn(),
  233. interactionCollectorMock:InteractionCollector<ButtonInteraction> = ({on: mockOn, stop: jest.fn()} as unknown) as InteractionCollector<ButtonInteraction>,
  234. messageMock:Message = Object.assign(Object.create(Message.prototype), {createMessageComponentCollector: jest.fn(() => interactionCollectorMock)}),
  235. channelMock:TextChannel = ({send: jest.fn(() => messageMock)} as unknown) as TextChannel;
  236. jest.spyOn(Message.prototype, 'editable', 'get').mockImplementation(() => true);
  237. jest.spyOn(Message.prototype, 'edit').mockImplementation(editMock);
  238. await pagination.send(channelMock);
  239. const collectEvent = mockOn.mock.calls[0][1],
  240. stopEvent = mockOn.mock.calls[1][1],
  241. interactionMock:ButtonInteraction = ({customId: "stop", deferUpdate: jest.fn()} as unknown) as ButtonInteraction;
  242. await collectEvent(interactionMock);
  243. expect(interactionCollectorMock.stop).toHaveBeenCalled();
  244. await stopEvent(new Map(), 'user');
  245. expect(editMock).toHaveBeenCalled();
  246. expect(editMock.mock.calls[0][0]).toStrictEqual({components: []});
  247. expect(pagination.onStop).toHaveBeenCalledWith('user');
  248. });
  249. });