refactor: update command tests to use require syntax and improve error handling
Deploy to NAS / deploy (push) Successful in 1m4s

This commit is contained in:
2025-12-25 10:28:44 -05:00
parent b362a43886
commit 06ac40a6d7
7 changed files with 33 additions and 25 deletions
+8 -8
View File
@@ -1,14 +1,16 @@
import { jest } from '@jest/globals'; const { createMockInteraction } = require('../utils/testUtils');
// Mock axios // Mock axios
jest.unstable_mockModule('axios', () => ({ jest.mock('axios', () => ({
__esModule: true,
default: { default: {
post: jest.fn(), post: jest.fn(),
}, },
})); }));
const axios = require('axios').default;
// Mock the discord.js module // Mock discord.js
jest.unstable_mockModule('discord.js', () => ({ jest.mock('discord.js', () => ({
SlashCommandBuilder: jest.fn().mockReturnValue({ SlashCommandBuilder: jest.fn().mockReturnValue({
setName: jest.fn().mockReturnThis(), setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(), setDescription: jest.fn().mockReturnThis(),
@@ -38,9 +40,7 @@ jest.unstable_mockModule('discord.js', () => ({
}), }),
})); }));
const axios = (await import('axios')).default; const AskCommand = require('../../commands/ask').default;
const { createMockInteraction } = await import('../utils/testUtils.js');
const AskCommand = (await import('../../commands/ask.js')).default;
const askCommand = new AskCommand(); const askCommand = new AskCommand();
describe('Ask Command', () => { describe('Ask Command', () => {
@@ -258,4 +258,4 @@ describe('Ask Command', () => {
await expect(askCommand.run(interaction)).rejects.toThrow('Failed to follow up'); await expect(askCommand.run(interaction)).rejects.toThrow('Failed to follow up');
}); });
}); });
}); });
+2 -1
View File
@@ -19,7 +19,8 @@ jest.mock('discord.js', () => ({
}), }),
})); }));
const helpCommand = require('../../commands/help'); const HelpCommand = require('../../commands/help').default;
const helpCommand = new HelpCommand();
describe('Help Command', () => { describe('Help Command', () => {
describe('Command Structure', () => { describe('Command Structure', () => {
+10 -8
View File
@@ -13,12 +13,13 @@ jest.mock('discord.js', () => ({
}; };
callback(option); callback(option);
return { return {
addStringOption: jest.fn().mockImplementation(callback => { addStringOption: jest.fn().mockImplementation(cb => {
const option = { const strOption = {
setName: jest.fn().mockReturnThis(), setName: jest.fn().mockReturnThis(),
setDescription: jest.fn().mockReturnThis(), setDescription: jest.fn().mockReturnThis(),
setRequired: jest.fn().mockReturnThis(),
}; };
callback(option); cb(strOption);
return { return {
setDefaultMemberPermissions: jest.fn().mockReturnThis(), setDefaultMemberPermissions: jest.fn().mockReturnThis(),
toJSON: jest.fn().mockReturnValue({ toJSON: jest.fn().mockReturnValue({
@@ -46,11 +47,12 @@ jest.mock('discord.js', () => ({
toJSON: jest.fn(), toJSON: jest.fn(),
}), }),
PermissionFlagsBits: { PermissionFlagsBits: {
KickMembers: 0x2n, KickMembers: 0x0000000000000002n,
}, },
})); }));
const kickCommand = require('../../commands/kick'); const KickCommand = require('../../commands/kick').default;
const kickCommand = new KickCommand();
describe('Kick Command', () => { describe('Kick Command', () => {
describe('Command Structure', () => { describe('Command Structure', () => {
@@ -152,7 +154,7 @@ describe('Kick Command', () => {
expect(mockKick).not.toHaveBeenCalled(); expect(mockKick).not.toHaveBeenCalled();
expect(interaction.reply).toHaveBeenCalledWith({ expect(interaction.reply).toHaveBeenCalledWith({
content: 'That user is not in this server!', content: 'Command failed: That user is not in this server!',
ephemeral: true, ephemeral: true,
}); });
}); });
@@ -173,7 +175,7 @@ describe('Kick Command', () => {
expect(mockKick).not.toHaveBeenCalled(); expect(mockKick).not.toHaveBeenCalled();
expect(interaction.reply).toHaveBeenCalledWith({ expect(interaction.reply).toHaveBeenCalledWith({
content: content:
'I cannot kick this user! They may have higher permissions than me.', 'Command failed: I cannot kick this user! They may have higher permissions than me.',
ephemeral: true, ephemeral: true,
}); });
}); });
@@ -195,7 +197,7 @@ describe('Kick Command', () => {
await kickCommand.execute(interaction); await kickCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledWith({ expect(interaction.reply).toHaveBeenCalledWith({
content: 'There was an error trying to kick this user!', content: 'Command failed: Failed to kick user',
ephemeral: true, ephemeral: true,
}); });
}); });
+3 -2
View File
@@ -12,7 +12,8 @@ jest.mock('discord.js', () => ({
}), }),
})); }));
const pingCommand = require('../../commands/ping'); const PingCommand = require('../../commands/ping').default;
const pingCommand = new PingCommand();
describe('Ping Command', () => { describe('Ping Command', () => {
describe('Command Structure', () => { describe('Command Structure', () => {
@@ -47,7 +48,7 @@ describe('Ping Command', () => {
it('should handle interaction reply failure', async () => { it('should handle interaction reply failure', async () => {
// Mock a failed reply // Mock a failed reply
interaction.reply.mockRejectedValueOnce(new Error('Failed to reply')); interaction.reply.mockRejectedValue(new Error('Failed to reply'));
await expect(pingCommand.execute(interaction)).rejects.toThrow( await expect(pingCommand.execute(interaction)).rejects.toThrow(
'Failed to reply', 'Failed to reply',
+4 -3
View File
@@ -39,7 +39,8 @@ jest.mock('discord.js', () => ({
}, },
})); }));
const pruneCommand = require('../../commands/prune'); const PruneCommand = require('../../commands/prune').default;
const pruneCommand = new PruneCommand();
describe('Prune Command', () => { describe('Prune Command', () => {
describe('Command Structure', () => { describe('Command Structure', () => {
@@ -103,7 +104,7 @@ describe('Prune Command', () => {
await pruneCommand.execute(interaction); await pruneCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledWith({ expect(interaction.reply).toHaveBeenCalledWith({
content: 'There was an error trying to prune messages in this channel!', content: 'Command failed: Failed to delete messages',
ephemeral: true, ephemeral: true,
}); });
}); });
@@ -116,7 +117,7 @@ describe('Prune Command', () => {
await pruneCommand.execute(interaction); await pruneCommand.execute(interaction);
expect(interaction.reply).toHaveBeenCalledWith({ expect(interaction.reply).toHaveBeenCalledWith({
content: 'There was an error trying to prune messages in this channel!', content: 'Command failed: Messages older than 14 days cannot be deleted',
ephemeral: true, ephemeral: true,
}); });
}); });
+3
View File
@@ -23,6 +23,9 @@ export default {
'^.+\\.jsx?$': 'babel-jest', '^.+\\.jsx?$': 'babel-jest',
}, },
// Ignore node_modules except for chalk (which is ESM only)
transformIgnorePatterns: ['/node_modules/(?!(chalk)/)'],
// An array of regexp pattern strings that are matched against all test files // An array of regexp pattern strings that are matched against all test files
testPathIgnorePatterns: ['/node_modules/'], testPathIgnorePatterns: ['/node_modules/'],
}; };
+3 -3
View File
@@ -34,7 +34,7 @@ export default class Command {
this._logger.info(`Executing command: ${interaction.commandName}`); this._logger.info(`Executing command: ${interaction.commandName}`);
await this.run(interaction); await this.run(interaction);
} catch (error) { } catch (error) {
this.handleError(interaction, error); await this.handleError(interaction, error);
} }
} }
@@ -44,13 +44,13 @@ export default class Command {
} }
// Standardized error handling // Standardized error handling
handleError(interaction, error) { async handleError(interaction, error) {
this._logger.error(`Command ${interaction.commandName} failed:`, error); this._logger.error(`Command ${interaction.commandName} failed:`, error);
const response = interaction.deferred ? 'followUp' : 'reply'; const response = interaction.deferred ? 'followUp' : 'reply';
const errorMessage = this.getErrorMessage(error); const errorMessage = this.getErrorMessage(error);
interaction[response]({ await interaction[response]({
content: errorMessage, content: errorMessage,
ephemeral: true, ephemeral: true,
}); });