From 3ad6fc830b400d922ca1587e71d400ca34f7e4d5 Mon Sep 17 00:00:00 2001 From: Lambo <12111454+LamboCreeper@users.noreply.github.com> Date: Sun, 11 Aug 2024 18:37:22 +0100 Subject: [PATCH] Add tsyringe to handle DI (#298) --- .eslintrc.js | 4 +- package-lock.json | 15 ++-- package.json | 7 +- src/app.ts | 10 ++- src/commands/AdventOfCodeCommand.ts | 22 ++--- src/commands/GitHubCommand.ts | 13 ++- src/commands/IssuesCommand.ts | 17 ++-- src/commands/SearchCommand.ts | 15 ++-- .../handlers/DiscordMessageLinkHandler.ts | 11 ++- src/services/AdventOfCodeService.ts | 20 ++--- src/services/GitHubService.ts | 19 +---- src/services/InstantAnswerService.ts | 19 +---- src/services/MessagePreviewService.ts | 17 +--- src/services/MineSweeperService.ts | 84 ------------------- src/services/WebsiteUserService.ts | 22 ----- test/appTest.ts | 8 +- test/commands/AdventOfCodeCommandTest.ts | 84 +++++++------------ test/commands/GitHubCommandTest.ts | 23 +++-- test/commands/InspectCommandTest.ts | 2 +- test/commands/IssuesCommandTest.ts | 24 +++--- test/commands/SearchCommandTest.ts | 18 ++-- .../handlers/DiscordMessageLinkHandlerTest.ts | 36 ++++---- test/services/AdventOfCodeServiceTest.ts | 31 ++++--- test/services/GitHubServiceTest.ts | 10 +-- test/services/InstantAnswerServiceTest.ts | 6 +- test/services/MessagePreviewServiceTest.ts | 12 +-- test/services/MineSweeperServiceTest.ts | 49 ----------- test/services/WebsiteUserServiceTest.ts | 40 --------- test/test-setup.ts | 1 + 29 files changed, 205 insertions(+), 434 deletions(-) delete mode 100644 src/services/MineSweeperService.ts delete mode 100644 src/services/WebsiteUserService.ts delete mode 100644 test/services/MineSweeperServiceTest.ts delete mode 100644 test/services/WebsiteUserServiceTest.ts create mode 100644 test/test-setup.ts diff --git a/.eslintrc.js b/.eslintrc.js index 7a2a7a65..49b544d9 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -12,6 +12,8 @@ module.exports = { } }, rules: { + "no-useless-constructor": "off", + "no-empty-function": "off", "new-cap": "off", "no-unused-vars": "off", "no-invalid-this": "off", @@ -27,4 +29,4 @@ module.exports = { extends: [ "eslint-config-codesupport" ], -}; \ No newline at end of file +}; diff --git a/package-lock.json b/package-lock.json index 7a08bc0f..b881bd58 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,7 +17,8 @@ "discordx": "^11.7.6", "dotenv": "^16.3.1", "node-schedule": "^2.1.0", - "reflect-metadata": "^0.1.13" + "reflect-metadata": "^0.1.13", + "tsyringe": "^4.8.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", @@ -4849,9 +4850,9 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "node_modules/tsyringe": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.7.0.tgz", - "integrity": "sha512-ncFDM1jTLsok4ejMvSW5jN1VGPQD48y2tfAR0pdptWRKYX4bkbqPt92k7KJ5RFJ1KV36JEs/+TMh7I6OUgj74g==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.8.0.tgz", + "integrity": "sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==", "dependencies": { "tslib": "^1.9.3" }, @@ -8775,9 +8776,9 @@ "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, "tsyringe": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.7.0.tgz", - "integrity": "sha512-ncFDM1jTLsok4ejMvSW5jN1VGPQD48y2tfAR0pdptWRKYX4bkbqPt92k7KJ5RFJ1KV36JEs/+TMh7I6OUgj74g==", + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/tsyringe/-/tsyringe-4.8.0.tgz", + "integrity": "sha512-YB1FG+axdxADa3ncEtRnQCFq/M0lALGLxSZeVNbTU8NqhOVc51nnv2CISTcvc1kyv6EGPtXVr0v6lWeDxiijOA==", "requires": { "tslib": "^1.9.3" }, diff --git a/package.json b/package.json index 71c7f4d5..45003862 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,8 @@ "build": "tsc", "dev": "cross-env NODE_ENV=dev nodemon --watch src --ext ts --exec 'ts-node -r dotenv/config ./src/index.ts'", "coverage": "nyc --reporter=lcov --reporter=text-summary npm test", - "test": "ts-mocha test/**/*Test.ts test/**/**/*Test.ts test/appTest.ts --exit", - "test:debug": "ts-mocha test/**/*Test.ts test/**/**/*Test.ts test/appTest.ts --timeout 999999999 --exit", + "test": "ts-mocha test/**/*Test.ts test/**/**/*Test.ts test/appTest.ts --require=test/test-setup.ts --exit", + "test:debug": "ts-mocha test/**/*Test.ts test/**/**/*Test.ts test/appTest.ts --timeout 999999999 --require=test/test-setup.ts --exit", "lint": "eslint src test --ext .js,.ts" }, "repository": { @@ -40,7 +40,8 @@ "discordx": "^11.7.6", "dotenv": "^16.3.1", "node-schedule": "^2.1.0", - "reflect-metadata": "^0.1.13" + "reflect-metadata": "^0.1.13", + "tsyringe": "^4.8.0" }, "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", diff --git a/src/app.ts b/src/app.ts index eb2f4627..cb9aec16 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,12 +1,14 @@ import "reflect-metadata"; import axios from "axios"; -import { Client } from "discordx"; +import {Client, DIService, tsyringeDependencyRegistryEngine} from "discordx"; import { TextChannel, Snowflake } from "discord.js"; import { config as env } from "dotenv"; import DirectoryUtils from "./utils/DirectoryUtils"; import DiscordUtils from "./utils/DiscordUtils"; import getConfigValue from "./utils/getConfigValue"; import Schedule from "./decorators/Schedule"; +import {container} from "tsyringe"; +import {setupCache} from "axios-cache-interceptor"; if (process.env.NODE_ENV !== getConfigValue("PRODUCTION_ENV")) { env({ @@ -28,6 +30,10 @@ class App { intents: DiscordUtils.getAllIntentsApartFromPresence(), silent: false }); + + container.register("AXIOS_CACHED_INSTANCE", setupCache(axios)); + + DIService.engine = tsyringeDependencyRegistryEngine.setInjector(container); } @Schedule("*/5 * * * *") @@ -76,4 +82,4 @@ class App { } } -export default App; \ No newline at end of file +export default App; diff --git a/src/commands/AdventOfCodeCommand.ts b/src/commands/AdventOfCodeCommand.ts index b6ebeeef..7d4d272f 100644 --- a/src/commands/AdventOfCodeCommand.ts +++ b/src/commands/AdventOfCodeCommand.ts @@ -3,17 +3,22 @@ import AdventOfCodeService from "../services/AdventOfCodeService"; import { AOCMember } from "../interfaces/AdventOfCode"; import getConfigValue from "../utils/getConfigValue"; import GenericObject from "../interfaces/GenericObject"; -import {Discord, Slash, SlashOption} from "discordx"; +import { Discord, Slash, SlashOption } from "discordx"; +import { injectable as Injectable } from "tsyringe"; @Discord() +@Injectable() class AdventOfCodeCommand { + constructor( + private readonly adventOfCodeService: AdventOfCodeService, + ) {} + @Slash({ name: "aoc", description: "Advent Of Code" }) async onInteract( @SlashOption({ name: "year", description: "AOC year", type: ApplicationCommandOptionType.Number, minValue: 2015, required: false }) year: number | undefined, @SlashOption({ name: "name", description: "User's name", type: ApplicationCommandOptionType.String, required: false }) name: string | undefined, interaction: CommandInteraction ): Promise { - const adventOfCodeService = AdventOfCodeService.getInstance(); const embed = new EmbedBuilder(); const button = new ButtonBuilder(); let yearToQuery = this.getYear(); @@ -37,12 +42,7 @@ class AdventOfCodeCommand { if (!!name) { try { - const [position, user] = await adventOfCodeService.getSinglePlayer(getConfigValue("ADVENT_OF_CODE_LEADERBOARD"), yearToQuery, name); - - if (!user) { - await interaction.reply({embeds: [this.errorEmbed("Could not get the user requested\nPlease make sure you typed the name correctly")], ephemeral: true}); - return; - } + const [position, user] = await this.adventOfCodeService.getSinglePlayer(getConfigValue("ADVENT_OF_CODE_LEADERBOARD"), yearToQuery, name); embed.setTitle("Advent Of Code"); embed.setDescription(description); @@ -57,13 +57,13 @@ class AdventOfCodeCommand { await interaction.reply({embeds: [embed], components: [row] }); return; } catch { - await interaction.reply({embeds: [this.errorEmbed("Could not get the statistics for Advent Of Code.")], ephemeral: true}); + await interaction.reply({embeds: [this.errorEmbed("Could not get the user requested\nPlease make sure you typed the name correctly")], ephemeral: true}); return; } } try { - const members = await adventOfCodeService.getSortedPlayerList(getConfigValue("ADVENT_OF_CODE_LEADERBOARD"), yearToQuery); + const members = await this.adventOfCodeService.getSortedPlayerList(getConfigValue("ADVENT_OF_CODE_LEADERBOARD"), yearToQuery); const playerList = this.generatePlayerList(members, getConfigValue("ADVENT_OF_CODE_RESULTS_PER_PAGE")); embed.setTitle("Advent Of Code"); @@ -141,4 +141,4 @@ class AdventOfCodeCommand { } } -export default AdventOfCodeCommand; \ No newline at end of file +export default AdventOfCodeCommand; diff --git a/src/commands/GitHubCommand.ts b/src/commands/GitHubCommand.ts index 399a4580..79eaf1c9 100644 --- a/src/commands/GitHubCommand.ts +++ b/src/commands/GitHubCommand.ts @@ -3,9 +3,15 @@ import GitHubService from "../services/GitHubService"; import getConfigValue from "../utils/getConfigValue"; import GenericObject from "../interfaces/GenericObject"; import {Discord, Slash, SlashOption} from "discordx"; +import { injectable as Injectable } from "tsyringe"; @Discord() +@Injectable() class GitHubCommand { + constructor( + private readonly githubService: GitHubService + ) {} + @Slash({ name: "github", description: "Shows information about a GitHub repository" }) async onInteract( @SlashOption({ name: "user", description: "Github user/account", type: ApplicationCommandOptionType.String, required: true }) user: string, @@ -15,9 +21,8 @@ class GitHubCommand { const embed = new EmbedBuilder(); try { - const GitHub = GitHubService.getInstance(); - const res = await GitHub.getRepository(user, repo); - const resPR = await GitHub.getPullRequest(user, repo); + const res = await this.githubService.getRepository(user, repo); + const resPR = await this.githubService.getPullRequest(user, repo); let desc = `[View on GitHub](${res.url})`; @@ -45,4 +50,4 @@ class GitHubCommand { } } -export default GitHubCommand; \ No newline at end of file +export default GitHubCommand; diff --git a/src/commands/IssuesCommand.ts b/src/commands/IssuesCommand.ts index 6d5b5448..1d1f3515 100644 --- a/src/commands/IssuesCommand.ts +++ b/src/commands/IssuesCommand.ts @@ -1,5 +1,6 @@ -import {ColorResolvable, CommandInteraction, EmbedBuilder, ApplicationCommandOptionType} from "discord.js"; -import {Discord, Slash, SlashOption} from "discordx"; +import { ColorResolvable, CommandInteraction, EmbedBuilder, ApplicationCommandOptionType } from "discord.js"; +import { Discord, Slash, SlashOption } from "discordx"; +import { injectable as Injectable } from "tsyringe"; import GitHubService from "../services/GitHubService"; import GitHubIssue from "../interfaces/GitHubIssue"; import DateUtils from "../utils/DateUtils"; @@ -8,7 +9,12 @@ import getConfigValue from "../utils/getConfigValue"; import GenericObject from "../interfaces/GenericObject"; @Discord() +@Injectable() class IssuesCommand { + constructor( + private readonly gitHubService: GitHubService + ) {} + @Slash({ name: "issues", description: "Shows the open issues on a GitHub repository" }) async onInteract( @SlashOption({ name: "user", description: "Github user/account", type: ApplicationCommandOptionType.String, required: true }) user: string, @@ -18,9 +24,8 @@ class IssuesCommand { const embed = new EmbedBuilder(); try { - const GitHub = GitHubService.getInstance(); - const resIssues = await GitHub.getIssues(user, repoName); - const resRep = await GitHub.getRepository(user, repoName); + const resIssues = await this.gitHubService.getIssues(user, repoName); + const resRep = await this.gitHubService.getRepository(user, repoName); if (resIssues.length) { const issues = resIssues.slice(0, 3); @@ -56,4 +61,4 @@ class IssuesCommand { } } -export default IssuesCommand; \ No newline at end of file +export default IssuesCommand; diff --git a/src/commands/SearchCommand.ts b/src/commands/SearchCommand.ts index 05118f9c..387d7d7b 100644 --- a/src/commands/SearchCommand.ts +++ b/src/commands/SearchCommand.ts @@ -1,11 +1,17 @@ -import {ColorResolvable, CommandInteraction, EmbedBuilder, ApplicationCommandOptionType} from "discord.js"; -import {Discord, Slash, SlashOption} from "discordx"; +import { ColorResolvable, CommandInteraction, EmbedBuilder, ApplicationCommandOptionType } from "discord.js"; +import { Discord, Slash, SlashOption } from "discordx"; +import { injectable as Injectable } from "tsyringe"; import InstantAnswerService from "../services/InstantAnswerService"; import getConfigValue from "../utils/getConfigValue"; import GenericObject from "../interfaces/GenericObject"; @Discord() +@Injectable() class SearchCommand { + constructor( + private readonly instantAnswerService: InstantAnswerService + ) {} + @Slash({ name: "search", description: "Search using the DuckDuckGo API" }) async onInteract( @SlashOption({ name: "query", description: "Search query", type: ApplicationCommandOptionType.String, required: true }) query: string, @@ -14,8 +20,7 @@ class SearchCommand { const embed = new EmbedBuilder(); try { - const InstantAnswer = InstantAnswerService.getInstance(); - const res = await InstantAnswer.query(query.replace(" ", "+")); + const res = await this.instantAnswerService.query(query.replace(" ", "+")); if (res !== null) { const [baseURL] = res.url.match(/[a-z]*\.[a-z]*(\.[a-z]*)*/) || []; @@ -39,4 +44,4 @@ class SearchCommand { } } -export default SearchCommand; \ No newline at end of file +export default SearchCommand; diff --git a/src/event/handlers/DiscordMessageLinkHandler.ts b/src/event/handlers/DiscordMessageLinkHandler.ts index c9a75769..a19c6f24 100644 --- a/src/event/handlers/DiscordMessageLinkHandler.ts +++ b/src/event/handlers/DiscordMessageLinkHandler.ts @@ -1,9 +1,13 @@ import EventHandler from "../../abstracts/EventHandler"; import { Message, Events } from "discord.js"; +import { injectable as Injectable } from "tsyringe"; import MessagePreviewService from "../../services/MessagePreviewService"; +@Injectable() class DiscordMessageLinkHandler extends EventHandler { - constructor() { + constructor( + private readonly messagePreviewService: MessagePreviewService + ) { super(Events.MessageCreate); } @@ -19,13 +23,12 @@ class DiscordMessageLinkHandler extends EventHandler { if (message.content.charAt(index - 1) !== "<" || message.content.charAt(index + link.length) !== ">") { link = link.replace(/app/, "").replace(/ptb\./, ""); - const messagePreviewService = MessagePreviewService.getInstance(); - await messagePreviewService.generatePreview(link, message); + await this.messagePreviewService.generatePreview(link, message); } } } } } -export default DiscordMessageLinkHandler; \ No newline at end of file +export default DiscordMessageLinkHandler; diff --git a/src/services/AdventOfCodeService.ts b/src/services/AdventOfCodeService.ts index 84d0b773..682e1514 100644 --- a/src/services/AdventOfCodeService.ts +++ b/src/services/AdventOfCodeService.ts @@ -1,19 +1,13 @@ -import axios from "axios"; -import { setupCache } from "axios-cache-interceptor"; +import {AxiosCacheInstance } from "axios-cache-interceptor"; +import { injectable as Injectable, inject as Inject } from "tsyringe"; import { AOCLeaderBoard, AOCMember } from "../interfaces/AdventOfCode"; +@Injectable() export default class AdventOfCodeService { - // eslint-disable-next-line no-use-before-define - private static instance: AdventOfCodeService; - private api = setupCache(axios); - - static getInstance(): AdventOfCodeService { - if (!this.instance) { - this.instance = new AdventOfCodeService(); - } - - return this.instance; - } + constructor( + @Inject("AXIOS_CACHED_INSTANCE") + private readonly api: AxiosCacheInstance + ) {} async getLeaderBoard(leaderBoard: string, year: number): Promise { const { ADVENT_OF_CODE_TOKEN } = process.env; diff --git a/src/services/GitHubService.ts b/src/services/GitHubService.ts index 500edfc3..810cfc53 100644 --- a/src/services/GitHubService.ts +++ b/src/services/GitHubService.ts @@ -1,24 +1,11 @@ import axios from "axios"; +import { injectable as Injectable } from "tsyringe"; import GitHubRepository from "../interfaces/GitHubRepository"; import GitHubPullRequest from "../interfaces/GitHubPullRequest"; import GitHubIssue from "../interfaces/GitHubIssue"; +@Injectable() class GitHubService { - // eslint-disable-next-line no-use-before-define - private static instance: GitHubService; - - /* eslint-disable */ - private constructor() { } - /* eslint-enable */ - - static getInstance(): GitHubService { - if (!this.instance) { - this.instance = new GitHubService(); - } - - return this.instance; - } - async getRepository(user: string, repo: string): Promise { const url = `https://api.github.com/repos/${user}/${repo}`; const { status, data } = await axios.get(url); @@ -81,4 +68,4 @@ class GitHubService { } } -export default GitHubService; \ No newline at end of file +export default GitHubService; diff --git a/src/services/InstantAnswerService.ts b/src/services/InstantAnswerService.ts index f639c384..5db5b83e 100644 --- a/src/services/InstantAnswerService.ts +++ b/src/services/InstantAnswerService.ts @@ -1,23 +1,10 @@ import axios from "axios"; +import { injectable as Injectable } from "tsyringe"; import InstantAnswer from "../interfaces/InstantAnswer"; import getConfigValue from "../utils/getConfigValue"; +@Injectable() class InstantAnswerService { - // eslint-disable-next-line no-use-before-define - private static instance: InstantAnswerService; - - /* eslint-disable */ - private constructor() {} - /* eslint-enable */ - - static getInstance(): InstantAnswerService { - if (!this.instance) { - this.instance = new InstantAnswerService(); - } - - return this.instance; - } - async query(query: string): Promise { const url = `https://api.duckduckgo.com/?q=${query}&format=json&t=codesupport-discord-bot`; const { status, data } = await axios.get(url); @@ -50,4 +37,4 @@ class InstantAnswerService { } } -export default InstantAnswerService; \ No newline at end of file +export default InstantAnswerService; diff --git a/src/services/MessagePreviewService.ts b/src/services/MessagePreviewService.ts index bd82bd94..cfb2d2aa 100644 --- a/src/services/MessagePreviewService.ts +++ b/src/services/MessagePreviewService.ts @@ -1,24 +1,11 @@ import { Message, TextChannel, EmbedBuilder, ColorResolvable, Snowflake } from "discord.js"; +import { injectable as Injectable } from "tsyringe"; import DateUtils from "../utils/DateUtils"; import getConfigValue from "../utils/getConfigValue"; import { logger } from "../logger"; +@Injectable() class MessagePreviewService { - // eslint-disable-next-line no-use-before-define - private static instance: MessagePreviewService; - - /* eslint-disable */ - private constructor() { } - /* eslint-enable */ - - static getInstance(): MessagePreviewService { - if (!this.instance) { - this.instance = new MessagePreviewService(); - } - - return this.instance; - } - async generatePreview(link: string, callingMessage: Message): Promise { const msgArray = this.stripLink(link); diff --git a/src/services/MineSweeperService.ts b/src/services/MineSweeperService.ts deleted file mode 100644 index 4ad20b70..00000000 --- a/src/services/MineSweeperService.ts +++ /dev/null @@ -1,84 +0,0 @@ -import NumberUtils from "../utils/NumberUtils"; - -class MineSweeperService { - // eslint-disable-next-line no-use-before-define - private static instance: MineSweeperService; - private static readonly GRID_ROWS = 11; - private static readonly GRID_COLUMNS = 11; - private static readonly BOMB = ":boom:"; - - // eslint-disable-next-line - private constructor() {} - - static getInstance(): MineSweeperService { - if (!this.instance) { - this.instance = new MineSweeperService(); - } - - return this.instance; - } - - public generateGame(ratio: number): string { - const bombCount = MineSweeperService.GRID_ROWS * MineSweeperService.GRID_COLUMNS / ratio; - const emptyGrid = Array.from(Array(MineSweeperService.GRID_ROWS), () => new Array(MineSweeperService.GRID_COLUMNS).fill(0)); - - const [gridWithBombs, bombPositions] = this.placeBombs(emptyGrid, bombCount); - - const gameGrid = this.calculateBombSurrounding(gridWithBombs, bombPositions); - - return this.formatGameResult(gameGrid); - } - - private placeBombs(grid: number[][], bombCount: number): number[][][] { - const bombPositions = []; - let i = 0; - - while (i < bombCount) { - const randomRow = NumberUtils.getRandomNumberInRange(0, MineSweeperService.GRID_ROWS - 1); - const randomColumn = NumberUtils.getRandomNumberInRange(0, MineSweeperService.GRID_COLUMNS - 1); - - // eslint-disable-next-line no-continue - if (grid[randomRow][randomColumn] === -1) continue; - - grid[randomRow][randomColumn] = -1; - bombPositions.push([randomRow, randomColumn]); - i++; - } - - return [grid, bombPositions]; - } - - private calculateBombSurrounding(grid: number[][], bombPositions: number[][]): number[][] { - const bombSurroundingPositions = [[0, -1], [0, 1], [-1, -1], [-1, 0], [-1, 1], [1, -1], [1, 0], [1, 1]]; - - bombPositions.map(([rowI, colI]) => { - // Check around every bomb and add +1 to cell counter - bombSurroundingPositions.map(([rowPosition, colPosition]) => { - if (typeof grid[rowI + rowPosition] === "undefined" || typeof grid[rowI + rowPosition][colI + colPosition] === "undefined") return; - if (grid[rowI + rowPosition][colI + colPosition] === -1) return; - - grid[rowI + rowPosition][colI + colPosition] += 1; - }); - }); - - return grid; - } - - private formatGameResult(grid: number[][]): string { - const numbers = [":zero:", ":one:", ":two:", ":three:", ":four:", ":five:", ":six:", ":seven:", ":eight:"]; - let stringGrid = ""; - - const formattedGrid: string[][] = grid.map((row, rowI) => row.map((col, colI) => { - if (grid[rowI][colI] !== -1) return numbers[grid[rowI][colI]]; - return MineSweeperService.BOMB; - })); - - formattedGrid.map((row, index) => { - stringGrid += `||${formattedGrid[index].join("||||")}||\n`; - }); - - return stringGrid; - } -} - -export default MineSweeperService; \ No newline at end of file diff --git a/src/services/WebsiteUserService.ts b/src/services/WebsiteUserService.ts deleted file mode 100644 index b78760b3..00000000 --- a/src/services/WebsiteUserService.ts +++ /dev/null @@ -1,22 +0,0 @@ -class WebsiteUserService { - // eslint-disable-next-line no-use-before-define - private static instance: WebsiteUserService; - - /* eslint-disable */ - private constructor() { } - /* eslint-enable */ - - static getInstance(): WebsiteUserService { - if (!this.instance) { - this.instance = new WebsiteUserService(); - } - - return this.instance; - } - - public buildProfileURL(user: string): string { - return `https://codesupport.dev/profile/${user.toLowerCase()}`; - } -} - -export default WebsiteUserService; \ No newline at end of file diff --git a/test/appTest.ts b/test/appTest.ts index 0fe6b6cc..3214f1a0 100644 --- a/test/appTest.ts +++ b/test/appTest.ts @@ -5,8 +5,11 @@ import { BaseMocks } from "@lambocreeper/mock-discord.js"; import axios from "axios"; import App from "../src/app"; import DirectoryUtils from "../src/utils/DirectoryUtils"; -import MockHandler from "./MockHandler"; import { AUTHENTICATION_MESSAGE_CHANNEL, AUTHENTICATION_MESSAGE_ID, PRODUCTION_ENV } from "../src/config.json"; +import type { AxiosCacheInstance } from "axios-cache-interceptor"; + +// @ts-ignore +import MockHandler from "./MockHandler"; describe("App", () => { let sandbox: SinonSandbox; @@ -16,6 +19,9 @@ describe("App", () => { beforeEach(() => { sandbox = createSandbox(); + // @ts-ignore + (axios as unknown as AxiosCacheInstance).defaults.cache = undefined; + loginStub = sandbox.stub(Client.prototype, "login"); getStub = sandbox.stub(axios, "get").resolves(); diff --git a/test/commands/AdventOfCodeCommandTest.ts b/test/commands/AdventOfCodeCommandTest.ts index 21b866ce..6373ebd4 100644 --- a/test/commands/AdventOfCodeCommandTest.ts +++ b/test/commands/AdventOfCodeCommandTest.ts @@ -1,47 +1,41 @@ -import { createSandbox, SinonSandbox } from "sinon"; +import { createSandbox, SinonSandbox, SinonStubbedInstance } from "sinon"; import { expect } from "chai"; import { BaseMocks } from "@lambocreeper/mock-discord.js"; import AdventOfCodeCommand from "../../src/commands/AdventOfCodeCommand"; import AdventOfCodeService from "../../src/services/AdventOfCodeService"; import { EMBED_COLOURS, ADVENT_OF_CODE_INVITE, ADVENT_OF_CODE_LEADERBOARD, ADVENT_OF_CODE_RESULTS_PER_PAGE } from "../../src/config.json"; -import { AOCLeaderBoard } from "../../src/interfaces/AdventOfCode"; +import { AOCMember } from "../../src/interfaces/AdventOfCode"; import NumberUtils from "../../src/utils/NumberUtils"; -const AOCMockData: AOCLeaderBoard = { - event: "2021", - owner_id: "490120", - members: { - 490120: { - completion_day_level: { - 1: { - 1: { - get_star_ts: "1606816563" - } - } - }, - local_score: 26, - global_score: 0, - name: "Lambo", - id: "490120", - stars: 3, - last_star_ts: "1606899444" +const AOCMockData: AOCMember[] = [{ + completion_day_level: { + 1: { + 1: { + get_star_ts: "1606816563" + } } - } -}; + }, + local_score: 26, + global_score: 0, + name: "Lambo", + id: "490120", + stars: 3, + last_star_ts: "1606899444" +}]; describe("AdventOfCodeCommand", () => { describe("onInteract()", () => { let sandbox: SinonSandbox; let command: AdventOfCodeCommand; - let AOC: AdventOfCodeService; + let AOC: SinonStubbedInstance; let interaction: any; let replyStub: sinon.SinonStub; beforeEach(() => { sandbox = createSandbox(); - command = new AdventOfCodeCommand(); - AOC = AdventOfCodeService.getInstance(); + AOC = sandbox.createStubInstance(AdventOfCodeService); + command = new AdventOfCodeCommand(AOC); replyStub = sandbox.stub().resolves(); interaction = { reply: replyStub, @@ -50,17 +44,13 @@ describe("AdventOfCodeCommand", () => { }); it("sends a message to the channel", async () => { - sandbox.stub(AOC, "getLeaderBoard"); - sandbox.stub(AOC, "getSortedPlayerList"); - sandbox.stub(AOC, "getSinglePlayer"); - await command.onInteract(2021, "Lambo", interaction); expect(replyStub.calledOnce).to.be.true; }); it("sends an error message when the year is too far into the feature", async () => { - sandbox.stub(AOC, "getLeaderBoard").throws(); + AOC.getSortedPlayerList.throws(); sandbox.stub(command, "getYear").returns(2019); await command.onInteract(2021, undefined, interaction); @@ -74,7 +64,7 @@ describe("AdventOfCodeCommand", () => { }); it("should query the year 2018 from the AOC Service", async () => { - const serviceMock = sandbox.stub(AOC, "getLeaderBoard").resolves({ members: {}, event: "2018", owner_id: "12345" }); + const serviceMock = AOC.getSortedPlayerList.resolves([]); sandbox.stub(command, "getYear").returns(2020); @@ -86,7 +76,7 @@ describe("AdventOfCodeCommand", () => { }); it("sends a message with the current score", async () => { - sandbox.stub(AOC, "getLeaderBoard").resolves(AOCMockData); + AOC.getSortedPlayerList.resolves(AOCMockData); sandbox.stub(command, "getYear").returns(2019); await command.onInteract(undefined, undefined, interaction); @@ -105,7 +95,7 @@ describe("AdventOfCodeCommand", () => { }); it("gives an error when the wrong acces token/id is provided", async () => { - sandbox.stub(AOC, "getLeaderBoard").throws(); + AOC.getSortedPlayerList.throws(); await command.onInteract(undefined, undefined, interaction); @@ -119,7 +109,9 @@ describe("AdventOfCodeCommand", () => { }); it("gives back one user when giving a username argument", async () => { - sandbox.stub(AOC, "getLeaderBoard").resolves(AOCMockData); + AOC.getSortedPlayerList.resolves(AOCMockData); + AOC.getSinglePlayer.resolves([1, AOCMockData[0]]); + sandbox.stub(command, "getYear").returns(2021); await command.onInteract(undefined, "Lambo", interaction); @@ -144,7 +136,7 @@ describe("AdventOfCodeCommand", () => { }); it("should give an error when the user doesn't exist", async () => { - sandbox.stub(AOC, "getLeaderBoard").resolves(AOCMockData); + AOC.getSortedPlayerList.resolves(AOCMockData); await command.onInteract(undefined, "Bob", interaction); @@ -157,22 +149,8 @@ describe("AdventOfCodeCommand", () => { expect(replyStub.getCall(0).firstArg.ephemeral).to.be.true; }); - it("gives an error when the name parameter is given but wrong acces token/id is provided", async () => { - sandbox.stub(AOC, "getSinglePlayer").throws(); - - await command.onInteract(undefined, "Lambo", interaction); - - const embed = replyStub.getCall(0).firstArg.embeds[0]; - - expect(replyStub.calledOnce).to.be.true; - expect(embed.data.title).to.equal("Error"); - expect(embed.data.description).to.equal("Could not get the statistics for Advent Of Code."); - expect(embed.data.color).to.equal(NumberUtils.hexadecimalToInteger(EMBED_COLOURS.ERROR.toLowerCase())); - expect(replyStub.getCall(0).firstArg.ephemeral).to.be.true; - }); - it("requesting a different year than current", async () => { - const APIMock = sandbox.stub(AOC, "getLeaderBoard").resolves(AOCMockData); + const APIMock = AOC.getSortedPlayerList.resolves(AOCMockData); sandbox.stub(command, "getYear").returns(2020); @@ -193,7 +171,7 @@ describe("AdventOfCodeCommand", () => { }); it("gives back user from requested year", async () => { - const APIMock = sandbox.stub(AOC, "getLeaderBoard").resolves(AOCMockData); + AOC.getSinglePlayer.resolves([1, AOCMockData[0]]); sandbox.stub(command, "getYear").returns(2021); @@ -203,7 +181,7 @@ describe("AdventOfCodeCommand", () => { const button = replyStub.getCall(0).firstArg.components[0].components[0]; expect(replyStub.calledOnce).to.be.true; - expect(APIMock.getCall(0).args[1]).to.equal(2018); + expect(AOC.getSinglePlayer.getCall(0).args[1]).to.equal(2018); expect(embed.data.title).to.equal("Advent Of Code"); expect(embed.data.description).to.equal(`Invite Code: \`${ADVENT_OF_CODE_INVITE}\``); expect(embed.data.fields[0].name).to.equal("Scores of Lambo in 2018"); @@ -223,4 +201,4 @@ describe("AdventOfCodeCommand", () => { sandbox.restore(); }); }); -}); \ No newline at end of file +}); diff --git a/test/commands/GitHubCommandTest.ts b/test/commands/GitHubCommandTest.ts index 66df9b40..94a2fefe 100644 --- a/test/commands/GitHubCommandTest.ts +++ b/test/commands/GitHubCommandTest.ts @@ -1,4 +1,4 @@ -import { createSandbox, SinonSandbox } from "sinon"; +import {createSandbox, SinonSandbox, SinonStubbedInstance} from "sinon"; import { expect } from "chai"; import { BaseMocks } from "@lambocreeper/mock-discord.js"; @@ -11,14 +11,14 @@ describe("GitHubCommand", () => { describe("onInteract()", () => { let sandbox: SinonSandbox; let command: GitHubCommand; - let gitHub: GitHubService; + let gitHub: SinonStubbedInstance; let replyStub: sinon.SinonStub; let interaction: any; beforeEach(() => { sandbox = createSandbox(); - command = new GitHubCommand(); - gitHub = GitHubService.getInstance(); + gitHub = sandbox.createStubInstance(GitHubService); + command = new GitHubCommand(gitHub); replyStub = sandbox.stub().resolves(); interaction = { reply: replyStub, @@ -27,17 +27,14 @@ describe("GitHubCommand", () => { }); it("sends a message to the channel", async () => { - sandbox.stub(gitHub, "getRepository"); - sandbox.stub(gitHub, "getPullRequest"); - await command.onInteract("user", "repo", interaction); expect(replyStub.calledOnce).to.be.true; }); it("states it had a problem with the request to GitHub", async () => { - sandbox.stub(gitHub, "getRepository").resolves(undefined); - sandbox.stub(gitHub, "getPullRequest").resolves(undefined); + gitHub.getRepository.resolves(undefined); + gitHub.getPullRequest.resolves(undefined); await command.onInteract("thisuserdoesnotexist", "thisrepodoesnotexist", interaction); @@ -53,7 +50,7 @@ describe("GitHubCommand", () => { }); it("states the result from the github service", async () => { - sandbox.stub(gitHub, "getRepository").resolves({ + gitHub.getRepository.resolves({ user: "user", repo: "repo", description: "This is the description", @@ -65,7 +62,7 @@ describe("GitHubCommand", () => { watchers: 3 }); - sandbox.stub(gitHub, "getPullRequest").resolves( + gitHub.getPullRequest.resolves( [{ title: "This is the title", description: "This is the description", @@ -97,7 +94,7 @@ describe("GitHubCommand", () => { }); it("states the result from the github service with an empty repo description", async () => { - sandbox.stub(gitHub, "getRepository").resolves({ + gitHub.getRepository.resolves({ user: "user", repo: "repo", description: undefined, @@ -109,7 +106,7 @@ describe("GitHubCommand", () => { watchers: 3 }); - sandbox.stub(gitHub, "getPullRequest").resolves( + gitHub.getPullRequest.resolves( [{ title: "This is the title", description: "This is the description", diff --git a/test/commands/InspectCommandTest.ts b/test/commands/InspectCommandTest.ts index 4978b035..e1d807b9 100644 --- a/test/commands/InspectCommandTest.ts +++ b/test/commands/InspectCommandTest.ts @@ -1,6 +1,6 @@ import { createSandbox, SinonSandbox } from "sinon"; import { expect } from "chai"; -import { Collection, EmbedField, GuildMember, GuildMemberRoleManager, Role, time, TimestampStyles } from "discord.js"; +import { Collection, EmbedField, Role, time, TimestampStyles } from "discord.js"; import { BaseMocks } from "@lambocreeper/mock-discord.js"; import InspectCommand from "../../src/commands/InspectCommand"; diff --git a/test/commands/IssuesCommandTest.ts b/test/commands/IssuesCommandTest.ts index 7d27eb5d..a2d6228c 100644 --- a/test/commands/IssuesCommandTest.ts +++ b/test/commands/IssuesCommandTest.ts @@ -1,4 +1,4 @@ -import { createSandbox, SinonSandbox } from "sinon"; +import { createSandbox, SinonSandbox, SinonStubbedInstance } from "sinon"; import {CommandInteraction} from "discord.js"; import { expect } from "chai"; import { BaseMocks } from "@lambocreeper/mock-discord.js"; @@ -13,22 +13,22 @@ describe("IssuesCommand", () => { let command: IssuesCommand; let interaction: CommandInteraction; let replyStub: sinon.SinonStub; - let gitHub: GitHubService; + let gitHub: SinonStubbedInstance; beforeEach(() => { sandbox = createSandbox(); - command = new IssuesCommand(); + gitHub = sandbox.createStubInstance(GitHubService); + command = new IssuesCommand(gitHub); replyStub = sandbox.stub().resolves(); interaction = { reply: replyStub, user: BaseMocks.getGuildMember() }; - gitHub = GitHubService.getInstance(); }); it("sends a message to the channel", async () => { - sandbox.stub(gitHub, "getIssues"); - sandbox.stub(gitHub, "getRepository"); + gitHub.getIssues; + gitHub.getRepository; await command.onInteract("user", "repo", interaction); @@ -36,8 +36,8 @@ describe("IssuesCommand", () => { }); it("states it had a problem with the request to GitHub", async () => { - sandbox.stub(gitHub, "getIssues").resolves(undefined); - sandbox.stub(gitHub, "getRepository").resolves(undefined); + gitHub.getIssues.resolves(undefined); + gitHub.getRepository.resolves(undefined); await command.onInteract("thisuserdoesnotexist", "thisrepodoesnotexist", interaction); @@ -52,11 +52,11 @@ describe("IssuesCommand", () => { }); it("states no issues have been found", async () => { - sandbox.stub(gitHub, "getIssues").resolves( + gitHub.getIssues.resolves( [] ); - sandbox.stub(gitHub, "getRepository").resolves({ + gitHub.getRepository.resolves({ user: "user", repo: "repo", description: "This is the description", @@ -79,7 +79,7 @@ describe("IssuesCommand", () => { }); it("states the result from the github service", async () => { - sandbox.stub(gitHub, "getIssues").resolves( + gitHub.getIssues.resolves( [{ title: "This is the title", number: 69, @@ -90,7 +90,7 @@ describe("IssuesCommand", () => { }] ); - sandbox.stub(gitHub, "getRepository").resolves({ + gitHub.getRepository.resolves({ user: "user", repo: "repo", description: "This is the description", diff --git a/test/commands/SearchCommandTest.ts b/test/commands/SearchCommandTest.ts index 045f2fa1..32a1ae9e 100644 --- a/test/commands/SearchCommandTest.ts +++ b/test/commands/SearchCommandTest.ts @@ -1,6 +1,6 @@ -import { createSandbox, SinonSandbox } from "sinon"; +import { createSandbox, SinonSandbox, SinonStubbedInstance } from "sinon"; import { expect } from "chai"; -import {CommandInteraction} from "discord.js"; +import { CommandInteraction } from "discord.js"; import { BaseMocks } from "@lambocreeper/mock-discord.js"; import SearchCommand from "../../src/commands/SearchCommand"; import InstantAnswerService from "../../src/services/InstantAnswerService"; @@ -13,21 +13,21 @@ describe("SearchCommand", () => { let command: SearchCommand; let interaction: CommandInteraction; let replyStub: sinon.SinonStub; - let instantAnswer: InstantAnswerService; + let instantAnswer: SinonStubbedInstance; beforeEach(() => { sandbox = createSandbox(); - command = new SearchCommand(); + instantAnswer = sandbox.createStubInstance(InstantAnswerService); + command = new SearchCommand(instantAnswer); replyStub = sandbox.stub().resolves(); interaction = { reply: replyStub, user: BaseMocks.getGuildMember() }; - instantAnswer = InstantAnswerService.getInstance(); }); it("sends a message to the channel", async () => { - sandbox.stub(instantAnswer, "query"); + instantAnswer.query; await command.onInteract("1", interaction); @@ -35,7 +35,7 @@ describe("SearchCommand", () => { }); it("states it can not query duckduckgo if the result isn't found", async () => { - sandbox.stub(instantAnswer, "query").resolves(null); + instantAnswer.query.resolves(null); await command.onInteract("thisruledoesnotexist", interaction); @@ -48,7 +48,7 @@ describe("SearchCommand", () => { }); it("states the result from the instant answer service", async () => { - sandbox.stub(instantAnswer, "query").resolves({ + instantAnswer.query.resolves({ heading: "Example Heading", description: "Example Description", url: "https://example.com" @@ -66,7 +66,7 @@ describe("SearchCommand", () => { }); it("correctly renders URLs from websites with subdomains", async () => { - sandbox.stub(instantAnswer, "query").resolves({ + instantAnswer.query.resolves({ heading: "Capybara", description: "The capybara is an adorable rodent.", url: "https://en.wikipedia.org/wiki/Capybara" diff --git a/test/event/handlers/DiscordMessageLinkHandlerTest.ts b/test/event/handlers/DiscordMessageLinkHandlerTest.ts index 11aba817..893a6222 100644 --- a/test/event/handlers/DiscordMessageLinkHandlerTest.ts +++ b/test/event/handlers/DiscordMessageLinkHandlerTest.ts @@ -1,6 +1,6 @@ import { expect } from "chai"; import { Events, Message, TextChannel } from "discord.js"; -import { SinonSandbox, createSandbox } from "sinon"; +import {SinonSandbox, createSandbox, SinonStubbedInstance} from "sinon"; import { CustomMocks } from "@lambocreeper/mock-discord.js"; import EventHandler from "../../../src/abstracts/EventHandler"; @@ -10,87 +10,79 @@ import DiscordMessageLinkHandler from "../../../src/event/handlers/DiscordMessag describe("DiscordMessageLinkHandler", () => { describe("Constructor()", () => { it("creates a handler for messageCreate", () => { - const handler = new DiscordMessageLinkHandler(); + const sandbox = createSandbox(); + const handler = new DiscordMessageLinkHandler(sandbox.createStubInstance(MessagePreviewService)); expect(handler.getEvent()).to.equal(Events.MessageCreate); + sandbox.restore(); }); }); describe("handle()", () => { let sandbox: SinonSandbox; let handler: EventHandler; + let messagePreviewServiceMock: SinonStubbedInstance; let message: Message; beforeEach(() => { sandbox = createSandbox(); - handler = new DiscordMessageLinkHandler(); + messagePreviewServiceMock = sandbox.createStubInstance(MessagePreviewService); + handler = new DiscordMessageLinkHandler(messagePreviewServiceMock); message = CustomMocks.getMessage({}, { channel: CustomMocks.getTextChannel() }); }); it("sends a message in message channel when contains discord message link mid sentence", async () => { - const generatePreviewMock = sandbox.stub(MessagePreviewService.prototype, "generatePreview"); - message.content = "aaaaaaaaa\nhttps://ptb.discordapp.com/channels/240880736851329024/518817917438001152/732711501345062982 aaaa"; await handler.handle(message); - expect(generatePreviewMock.called).to.be.true; + expect(messagePreviewServiceMock.generatePreview.called).to.be.true; }); it("sends a message in message channel when contains discord message link", async () => { - const generatePreviewMock = sandbox.stub(MessagePreviewService.prototype, "generatePreview"); - message.content = "https://ptb.discordapp.com/channels/240880736851329024/518817917438001152/732711501345062982"; await handler.handle(message); - expect(generatePreviewMock.called).to.be.true; + expect(messagePreviewServiceMock.generatePreview.called).to.be.true; }); it("sends a single message in message channel when contains multiple discord message links however one is escaped", async () => { - const generatePreviewMock = sandbox.stub(MessagePreviewService.prototype, "generatePreview"); - message.content = "https://ptb.discordapp.com/channels/240880736851329024/518817917438001152/732711501345062982 "; await handler.handle(message); - expect(generatePreviewMock.calledOnce).to.be.true; + expect(messagePreviewServiceMock.generatePreview.called).to.be.true; }); it("sends multiple messages in message channel when contains multiple discord message link", async () => { - const generatePreviewMock = sandbox.stub(MessagePreviewService.prototype, "generatePreview"); - message.content = "https://ptb.discordapp.com/channels/240880736851329024/518817917438001152/732711501345062982 https://ptb.discordapp.com/channels/240880736851329024/518817917438001152/732711501345062982"; await handler.handle(message); - expect(generatePreviewMock.calledTwice).to.be.true; + expect(messagePreviewServiceMock.generatePreview.called).to.be.true; }); it("does not send a message if the message starts with < and ends with >", async () => { - const generatePreviewMock = sandbox.stub(MessagePreviewService.prototype, "generatePreview"); - message.content = ""; await handler.handle(message); - expect(generatePreviewMock.called).to.be.false; + expect(messagePreviewServiceMock.generatePreview.called).to.be.false; }); it("does not send a message if the url was escaped mid sentence", async () => { - const generatePreviewMock = sandbox.stub(MessagePreviewService.prototype, "generatePreview"); - message.content = "placeholderText placeholderText"; await handler.handle(message); - expect(generatePreviewMock.called).to.be.false; + expect(messagePreviewServiceMock.generatePreview.called).to.be.false; }); afterEach(() => { sandbox.restore(); }); }); -}); \ No newline at end of file +}); diff --git a/test/services/AdventOfCodeServiceTest.ts b/test/services/AdventOfCodeServiceTest.ts index 70600a07..811ae68c 100644 --- a/test/services/AdventOfCodeServiceTest.ts +++ b/test/services/AdventOfCodeServiceTest.ts @@ -1,7 +1,9 @@ -import { createSandbox, SinonSandbox } from "sinon"; +import {createSandbox, SinonSandbox, SinonStubbedInstance} from "sinon"; import { expect } from "chai"; import AdventOfCodeService from "../../src/services/AdventOfCodeService"; +import {AxiosCacheInstance} from "axios-cache-interceptor"; +import {Axios} from "axios"; const mockAPIData = { event: "2021", @@ -55,23 +57,28 @@ const mockAPIData = { describe("AdventOfCodeService", () => { describe("::getInstance()", () => { it("creates an instance of AdventOfCodeService", () => { - const service = AdventOfCodeService.getInstance(); + const sandbox = createSandbox(); + const api = sandbox.createStubInstance(Axios as AxiosCacheInstance); + const service = new AdventOfCodeService(api); expect(service).to.be.instanceOf(AdventOfCodeService); + sandbox.restore(); }); }); describe("getLeaderBoard()", () => { let sandbox: SinonSandbox; + let api: SinonStubbedInstance; let aoc: AdventOfCodeService; beforeEach(() => { sandbox = createSandbox(); - aoc = AdventOfCodeService.getInstance(); + api = sandbox.createStubInstance(Axios as AxiosCacheInstance); + aoc = new AdventOfCodeService(api); }); it("performs a GET request to the Advent Of Code Api", async () => { - const axiosGet = sandbox.stub(aoc.api, "get").resolves({ + const axiosGet = api.get.resolves({ status: 200, data: mockAPIData }); @@ -83,7 +90,7 @@ describe("AdventOfCodeService", () => { }); it("throws an error if the API responds when not authorized", async () => { - const axiosGet = sandbox.stub(aoc.api, "get").resolves({ + const axiosGet = api.get.resolves({ status: 500, data: {} }); @@ -105,15 +112,17 @@ describe("AdventOfCodeService", () => { describe("getSinglePlayer()", () => { let sandbox: SinonSandbox; + let apiMock: SinonStubbedInstance; let aoc: AdventOfCodeService; beforeEach(() => { sandbox = createSandbox(); - aoc = AdventOfCodeService.getInstance(); + apiMock = sandbox.createStubInstance(Axios as AxiosCacheInstance); + aoc = new AdventOfCodeService(apiMock); }); it("performs a GET request to the Advent Of Code Api", async () => { - const axiosGet = sandbox.stub(aoc.api, "get").resolves({ + const axiosGet = apiMock.get.resolves({ status: 200, data: mockAPIData }); @@ -124,7 +133,7 @@ describe("AdventOfCodeService", () => { }); it("returns the position and the member when the user exist on the leaderboard", async () => { - sandbox.stub(aoc.api, "get").resolves({ + apiMock.get.resolves({ status: 200, data: mockAPIData }); @@ -136,7 +145,7 @@ describe("AdventOfCodeService", () => { }); it("finds the player if the name is weirdly capitalized", async () => { - sandbox.stub(aoc.api, "get").resolves({ + apiMock.get.resolves({ status: 200, data: mockAPIData }); @@ -148,7 +157,7 @@ describe("AdventOfCodeService", () => { }); it("finds the player when there are spaces in the name", async () => { - sandbox.stub(aoc.api, "get").resolves({ + apiMock.get.resolves({ status: 200, data: mockAPIData }); @@ -160,7 +169,7 @@ describe("AdventOfCodeService", () => { }); it("returns 0 and undefined when the user does not exist on the leaderboard", async () => { - sandbox.stub(aoc.api, "get").resolves({ + apiMock.get.resolves({ status: 200, data: mockAPIData }); diff --git a/test/services/GitHubServiceTest.ts b/test/services/GitHubServiceTest.ts index a791e9d5..e47ff199 100644 --- a/test/services/GitHubServiceTest.ts +++ b/test/services/GitHubServiceTest.ts @@ -7,7 +7,7 @@ import GitHubService from "../../src/services/GitHubService"; describe("GitHubService", () => { describe("::getInstance()", () => { it("returns an instance of GitHubService", () => { - const service = GitHubService.getInstance(); + const service = new GitHubService(); expect(service).to.be.instanceOf(GitHubService); }); @@ -19,7 +19,7 @@ describe("GitHubService", () => { beforeEach(() => { sandbox = createSandbox(); - gitHub = GitHubService.getInstance(); + gitHub = new GitHubService(); }); it("performs a GET request to the GitHub API", async () => { @@ -72,7 +72,7 @@ describe("GitHubService", () => { beforeEach(() => { sandbox = createSandbox(); - gitHub = GitHubService.getInstance(); + gitHub = new GitHubService(); }); it("performs a GET request to the GitHub pulls API", async () => { @@ -116,7 +116,7 @@ describe("GitHubService", () => { beforeEach(() => { sandbox = createSandbox(); - gitHub = GitHubService.getInstance(); + gitHub = new GitHubService(); }); it("performs a GET request to the GitHub issues API", async () => { @@ -156,4 +156,4 @@ describe("GitHubService", () => { sandbox.restore(); }); }); -}); \ No newline at end of file +}); diff --git a/test/services/InstantAnswerServiceTest.ts b/test/services/InstantAnswerServiceTest.ts index c0328e17..3ebdfc1a 100644 --- a/test/services/InstantAnswerServiceTest.ts +++ b/test/services/InstantAnswerServiceTest.ts @@ -7,7 +7,7 @@ import InstantAnswerService from "../../src/services/InstantAnswerService"; describe("InstantAnswerService", () => { describe("::getInstance()", () => { it("returns an instance of InstantAnswerService", () => { - const service = InstantAnswerService.getInstance(); + const service = new InstantAnswerService(); expect(service).to.be.instanceOf(InstantAnswerService); }); @@ -19,7 +19,7 @@ describe("InstantAnswerService", () => { beforeEach(() => { sandbox = createSandbox(); - instantAnswer = InstantAnswerService.getInstance(); + instantAnswer = new InstantAnswerService(); }); it("makes a GET request to the DuckDuckGo API", async () => { @@ -56,4 +56,4 @@ describe("InstantAnswerService", () => { sandbox.restore(); }); }); -}); \ No newline at end of file +}); diff --git a/test/services/MessagePreviewServiceTest.ts b/test/services/MessagePreviewServiceTest.ts index ea8e714f..3f1a99e5 100644 --- a/test/services/MessagePreviewServiceTest.ts +++ b/test/services/MessagePreviewServiceTest.ts @@ -8,7 +8,7 @@ import { BaseMocks, CustomMocks } from "@lambocreeper/mock-discord.js"; describe("MessagePreviewService", () => { describe("::getInstance()", () => { it("returns an instance of MessagePreviewService", () => { - const service = MessagePreviewService.getInstance(); + const service = new MessagePreviewService(); expect(service).to.be.instanceOf(MessagePreviewService); }); @@ -27,7 +27,7 @@ describe("MessagePreviewService", () => { beforeEach(() => { sandbox = createSandbox(); - messagePreview = MessagePreviewService.getInstance(); + messagePreview = new MessagePreviewService(); const guild = CustomMocks.getGuild({ id: "guild-id", @@ -109,7 +109,7 @@ describe("MessagePreviewService", () => { beforeEach(() => { sandbox = createSandbox(); - messagePreview = MessagePreviewService.getInstance(); + messagePreview = new MessagePreviewService(); message = CustomMocks.getMessage(); }); @@ -133,7 +133,7 @@ describe("MessagePreviewService", () => { beforeEach(() => { sandbox = createSandbox(); - messagePreview = MessagePreviewService.getInstance(); + messagePreview = new MessagePreviewService(); link = "https://ptb.discordapp.com/channels/240880736851329024/518817917438001152/732711501345062982"; }); @@ -156,7 +156,7 @@ describe("MessagePreviewService", () => { beforeEach(() => { sandbox = createSandbox(); - messagePreview = MessagePreviewService.getInstance(); + messagePreview = new MessagePreviewService(); }); it("should return the string as it is if there are no hyperlinks", () => { @@ -183,4 +183,4 @@ describe("MessagePreviewService", () => { sandbox.restore(); }); }); -}); \ No newline at end of file +}); diff --git a/test/services/MineSweeperServiceTest.ts b/test/services/MineSweeperServiceTest.ts deleted file mode 100644 index c5ca0891..00000000 --- a/test/services/MineSweeperServiceTest.ts +++ /dev/null @@ -1,49 +0,0 @@ -import {createSandbox, SinonSandbox} from "sinon"; -import {expect} from "chai"; - -import MineSweeperService from "../../src/services/MineSweeperService"; - -describe("MineSweeperService", () => { - describe("::getInstance()", () => { - it("returns an instance of MineSweeperService", () => { - const service = MineSweeperService.getInstance(); - - expect(service).to.be.instanceOf(MineSweeperService); - }); - }); - - describe("generateGame()", () => { - let sandbox: SinonSandbox; - let mineSweeperService: MineSweeperService; - - beforeEach(() => { - sandbox = createSandbox(); - mineSweeperService = MineSweeperService.getInstance(); - }); - - it("has the correct amount of cells", async () => { - const result = mineSweeperService.generateGame(1); - const count = (result.split("||")!.length - 1) / 2; - - expect(count).to.equal(121); - }); - - it("has the correct amount of bombs", async () => { - const result = mineSweeperService.generateGame(1); - const count = result.split(":boom:")!.length - 1; - - expect(count).to.equal(121); - }); - - it("returned string has no numbers", async () => { - const result = mineSweeperService.generateGame(1); - const hasNumber = (/\d/).test(result); - - expect(hasNumber).to.equal(false); - }); - - afterEach(() => { - sandbox.restore(); - }); - }); -}); \ No newline at end of file diff --git a/test/services/WebsiteUserServiceTest.ts b/test/services/WebsiteUserServiceTest.ts deleted file mode 100644 index 7e9eff4c..00000000 --- a/test/services/WebsiteUserServiceTest.ts +++ /dev/null @@ -1,40 +0,0 @@ -import {createSandbox, SinonSandbox} from "sinon"; -import {expect} from "chai"; - -import WebsiteUserService from "../../src/services/WebsiteUserService"; - -describe("WebsiteUserService", () => { - describe("::getInstance()", () => { - it("returns an instance of WebsiteUserService", () => { - const service = WebsiteUserService.getInstance(); - - expect(service).to.be.instanceOf(WebsiteUserService); - }); - }); - - describe("buildProfileURL()", () => { - let sandbox: SinonSandbox; - let websiteUserService: WebsiteUserService; - - beforeEach(() => { - sandbox = createSandbox(); - websiteUserService = WebsiteUserService.getInstance(); - }); - - it("Generates url containing profile name", async () => { - const mockArticle = { - createdBy: { - alias: "userAwesome" - } - }; - - const result = await websiteUserService.buildProfileURL(mockArticle.createdBy.alias); - - expect(result).to.equal("https://codesupport.dev/profile/userawesome"); - }); - - afterEach(() => { - sandbox.restore(); - }); - }); -}); \ No newline at end of file diff --git a/test/test-setup.ts b/test/test-setup.ts new file mode 100644 index 00000000..fded23ac --- /dev/null +++ b/test/test-setup.ts @@ -0,0 +1 @@ +import "reflect-metadata";