meocord 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -361,12 +361,70 @@ function findPrototypeMethod(instance, name) {
|
|
|
361
361
|
// Convenience wrappers for common discord.js classes
|
|
362
362
|
// ---------------------------------------------------------------------------
|
|
363
363
|
/** Creates a mock {@link User}. All methods are auto-stubbed as `jest.fn()`. */ const createMockUser = ()=>createMockInteraction(discord_js.User);
|
|
364
|
-
/**
|
|
365
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Creates a mock {@link Client}.
|
|
366
|
+
*
|
|
367
|
+
* Manager methods that are constructor-assigned (not on the prototype) are
|
|
368
|
+
* pre-initialized as `jest.fn()` so they work out of the box without manual
|
|
369
|
+
* setup: `users.fetch`, `channels.fetch`, `guilds.fetch`, and
|
|
370
|
+
* `application.commands.fetch`.
|
|
371
|
+
*/ function createMockClient() {
|
|
372
|
+
const instance = Object.create(discord_js.Client.prototype);
|
|
373
|
+
// Manager properties are constructor-assigned — pre-initialize as prototype-based
|
|
374
|
+
// stubs so ALL manager methods (not just fetch) are auto-stubbed as jest.fn().
|
|
375
|
+
const appInstance = Object.create(null);
|
|
376
|
+
appInstance.commands = stubDeep(Object.create(discord_js.ApplicationCommandManager.prototype));
|
|
377
|
+
instance.users = stubDeep(Object.create(discord_js.UserManager.prototype));
|
|
378
|
+
instance.channels = stubDeep(Object.create(discord_js.ChannelManager.prototype));
|
|
379
|
+
instance.guilds = stubDeep(Object.create(discord_js.GuildManager.prototype));
|
|
380
|
+
instance.user = stubDeep(Object.create(discord_js.ClientUser.prototype));
|
|
381
|
+
instance.application = stubDeep(appInstance);
|
|
382
|
+
return stubDeep(instance);
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Creates a mock {@link Guild}.
|
|
386
|
+
*
|
|
387
|
+
* Manager properties are constructor-assigned in discord.js. This factory
|
|
388
|
+
* pre-initializes each as a prototype-based stub so all methods are
|
|
389
|
+
* auto-stubbed as `jest.fn()`.
|
|
390
|
+
*
|
|
391
|
+
* Pre-initialized: `members`, `channels`, `roles`, `bans`.
|
|
392
|
+
*/ function createMockGuild() {
|
|
393
|
+
const instance = Object.create(discord_js.Guild.prototype);
|
|
394
|
+
instance.members = stubDeep(Object.create(discord_js.GuildMemberManager.prototype));
|
|
395
|
+
instance.channels = stubDeep(Object.create(discord_js.GuildChannelManager.prototype));
|
|
396
|
+
instance.roles = stubDeep(Object.create(discord_js.RoleManager.prototype));
|
|
397
|
+
instance.bans = stubDeep(Object.create(discord_js.GuildBanManager.prototype));
|
|
398
|
+
return stubDeep(instance);
|
|
399
|
+
}
|
|
366
400
|
/**
|
|
367
401
|
* Creates a mock channel of the given class (e.g. `TextChannel`, `DMChannel`).
|
|
368
|
-
*
|
|
369
|
-
|
|
402
|
+
*
|
|
403
|
+
* Manager properties that are constructor-assigned in discord.js are
|
|
404
|
+
* pre-initialized as prototype-based stubs so all methods are auto-stubbed
|
|
405
|
+
* as `jest.fn()`:
|
|
406
|
+
* - Guild text channels (`TextChannel`, `NewsChannel`): `messages`, `threads`
|
|
407
|
+
* - `DMChannel`: `messages`
|
|
408
|
+
* - `ThreadChannel`: `messages`, `members`
|
|
409
|
+
*/ function createMockChannel(Class) {
|
|
410
|
+
const instance = Object.create(Class.prototype);
|
|
411
|
+
// Guild text channels (TextChannel, NewsChannel) — messages & threads
|
|
412
|
+
// assigned in BaseGuildTextChannel constructor
|
|
413
|
+
if ('messages' in Class.prototype || Class.name === 'TextChannel' || Class.name === 'NewsChannel') {
|
|
414
|
+
instance.messages = stubDeep(Object.create(discord_js.GuildMessageManager.prototype));
|
|
415
|
+
instance.threads = stubDeep(Object.create(discord_js.ThreadManager.prototype));
|
|
416
|
+
}
|
|
417
|
+
// DMChannel — messages assigned in DMChannel constructor
|
|
418
|
+
if (Class.name === 'DMChannel') {
|
|
419
|
+
instance.messages = stubDeep(Object.create(discord_js.DMMessageManager.prototype));
|
|
420
|
+
}
|
|
421
|
+
// ThreadChannel — messages & members assigned in ThreadChannel constructor
|
|
422
|
+
if (Class.name === 'ThreadChannel') {
|
|
423
|
+
instance.messages = stubDeep(Object.create(discord_js.MessageManager.prototype));
|
|
424
|
+
instance.members = stubDeep(Object.create(discord_js.ThreadMemberManager.prototype));
|
|
425
|
+
}
|
|
426
|
+
return stubDeep(instance);
|
|
427
|
+
}
|
|
370
428
|
/**
|
|
371
429
|
* Creates a smart mock {@link Message}.
|
|
372
430
|
*
|
|
@@ -374,14 +432,54 @@ function findPrototypeMethod(instance, name) {
|
|
|
374
432
|
* `pin()`, and `unpin()` throw if the message has already been deleted.
|
|
375
433
|
* `edit()` and `reply()` resolve to a new mock `Message` instance.
|
|
376
434
|
*
|
|
435
|
+
* Constructor-assigned and getter properties are pre-initialized as
|
|
436
|
+
* prototype-based stubs so `msg.author.send`, `msg.member.fetch`,
|
|
437
|
+
* `msg.channel.send`, `msg.guild.members.fetch`, `msg.thread.fetch`,
|
|
438
|
+
* and `msg.mentions.has` all work out of the box.
|
|
439
|
+
*
|
|
377
440
|
* All methods remain `jest.fn()` — overridable per test.
|
|
378
441
|
*
|
|
379
442
|
* Note: `createMockInteraction`'s `followUp()` and `editReply()` stubs return
|
|
380
443
|
* a `createMockMessage()` by default, matching the official return types.
|
|
381
|
-
*/
|
|
444
|
+
*/ /**
|
|
445
|
+
* Internal guild stub for use inside createMockMessage.
|
|
446
|
+
* Reuses the same manager stubs as createMockGuild but avoids
|
|
447
|
+
* a circular reference in the module.
|
|
448
|
+
*/ function createMockGuildForMessage() {
|
|
449
|
+
const guild = Object.create(discord_js.Guild.prototype);
|
|
450
|
+
guild.members = stubDeep(Object.create(discord_js.GuildMemberManager.prototype));
|
|
451
|
+
guild.channels = stubDeep(Object.create(discord_js.GuildChannelManager.prototype));
|
|
452
|
+
guild.roles = stubDeep(Object.create(discord_js.RoleManager.prototype));
|
|
453
|
+
guild.bans = stubDeep(Object.create(discord_js.GuildBanManager.prototype));
|
|
454
|
+
return stubDeep(guild);
|
|
455
|
+
}
|
|
456
|
+
function createMockMessage() {
|
|
382
457
|
const instance = Object.create(discord_js.Message.prototype);
|
|
383
458
|
const stubs = new Map();
|
|
384
459
|
instance.deleted = false;
|
|
460
|
+
// Constructor-assigned — set as prototype-based stubs
|
|
461
|
+
instance.author = stubDeep(Object.create(discord_js.User.prototype));
|
|
462
|
+
// Getters on the prototype — the proxy sees them as functions and returns
|
|
463
|
+
// jest.fn(), which is wrong. Pre-initialize as own properties to shadow
|
|
464
|
+
// the prototype getters.
|
|
465
|
+
Object.defineProperty(instance, 'member', {
|
|
466
|
+
value: stubDeep(Object.create(discord_js.GuildMember.prototype)),
|
|
467
|
+
writable: true
|
|
468
|
+
});
|
|
469
|
+
Object.defineProperty(instance, 'channel', {
|
|
470
|
+
value: stubDeep(Object.create(discord_js.TextChannel.prototype)),
|
|
471
|
+
writable: true
|
|
472
|
+
});
|
|
473
|
+
Object.defineProperty(instance, 'guild', {
|
|
474
|
+
value: createMockGuildForMessage(),
|
|
475
|
+
writable: true
|
|
476
|
+
});
|
|
477
|
+
Object.defineProperty(instance, 'thread', {
|
|
478
|
+
value: stubDeep(Object.create(discord_js.ThreadChannel.prototype)),
|
|
479
|
+
writable: true
|
|
480
|
+
});
|
|
481
|
+
// MessageMentions — constructor-assigned, has methods like .has(), .members
|
|
482
|
+
instance.mentions = stubDeep(Object.create(discord_js.MessageMentions.prototype));
|
|
385
483
|
const alreadyDeleted = ()=>new Error('This message has already been deleted.');
|
|
386
484
|
stubs.set('delete', globals.jest.fn(async ()=>{
|
|
387
485
|
if (instance.deleted) throw alreadyDeleted();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { jest } from '@jest/globals';
|
|
3
|
-
import { InteractionType, ComponentType, ApplicationCommandType, CommandInteractionOptionResolver,
|
|
3
|
+
import { InteractionType, ComponentType, ApplicationCommandType, CommandInteractionOptionResolver, GuildMessageManager, ThreadManager, DMMessageManager, MessageManager, ThreadMemberManager, Client, ApplicationCommandManager, UserManager, ChannelManager, GuildManager, ClientUser, Guild, GuildMemberManager, GuildChannelManager, RoleManager, GuildBanManager, Message, User, GuildMember, TextChannel, ThreadChannel, MessageMentions } from 'discord.js';
|
|
4
4
|
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
// stubDeep — Proxy that auto-creates jest.fn() on any property access
|
|
@@ -250,12 +250,70 @@ function findPrototypeMethod(instance, name) {
|
|
|
250
250
|
// Convenience wrappers for common discord.js classes
|
|
251
251
|
// ---------------------------------------------------------------------------
|
|
252
252
|
/** Creates a mock {@link User}. All methods are auto-stubbed as `jest.fn()`. */ const createMockUser = ()=>createMockInteraction(User);
|
|
253
|
-
/**
|
|
254
|
-
|
|
253
|
+
/**
|
|
254
|
+
* Creates a mock {@link Client}.
|
|
255
|
+
*
|
|
256
|
+
* Manager methods that are constructor-assigned (not on the prototype) are
|
|
257
|
+
* pre-initialized as `jest.fn()` so they work out of the box without manual
|
|
258
|
+
* setup: `users.fetch`, `channels.fetch`, `guilds.fetch`, and
|
|
259
|
+
* `application.commands.fetch`.
|
|
260
|
+
*/ function createMockClient() {
|
|
261
|
+
const instance = Object.create(Client.prototype);
|
|
262
|
+
// Manager properties are constructor-assigned — pre-initialize as prototype-based
|
|
263
|
+
// stubs so ALL manager methods (not just fetch) are auto-stubbed as jest.fn().
|
|
264
|
+
const appInstance = Object.create(null);
|
|
265
|
+
appInstance.commands = stubDeep(Object.create(ApplicationCommandManager.prototype));
|
|
266
|
+
instance.users = stubDeep(Object.create(UserManager.prototype));
|
|
267
|
+
instance.channels = stubDeep(Object.create(ChannelManager.prototype));
|
|
268
|
+
instance.guilds = stubDeep(Object.create(GuildManager.prototype));
|
|
269
|
+
instance.user = stubDeep(Object.create(ClientUser.prototype));
|
|
270
|
+
instance.application = stubDeep(appInstance);
|
|
271
|
+
return stubDeep(instance);
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Creates a mock {@link Guild}.
|
|
275
|
+
*
|
|
276
|
+
* Manager properties are constructor-assigned in discord.js. This factory
|
|
277
|
+
* pre-initializes each as a prototype-based stub so all methods are
|
|
278
|
+
* auto-stubbed as `jest.fn()`.
|
|
279
|
+
*
|
|
280
|
+
* Pre-initialized: `members`, `channels`, `roles`, `bans`.
|
|
281
|
+
*/ function createMockGuild() {
|
|
282
|
+
const instance = Object.create(Guild.prototype);
|
|
283
|
+
instance.members = stubDeep(Object.create(GuildMemberManager.prototype));
|
|
284
|
+
instance.channels = stubDeep(Object.create(GuildChannelManager.prototype));
|
|
285
|
+
instance.roles = stubDeep(Object.create(RoleManager.prototype));
|
|
286
|
+
instance.bans = stubDeep(Object.create(GuildBanManager.prototype));
|
|
287
|
+
return stubDeep(instance);
|
|
288
|
+
}
|
|
255
289
|
/**
|
|
256
290
|
* Creates a mock channel of the given class (e.g. `TextChannel`, `DMChannel`).
|
|
257
|
-
*
|
|
258
|
-
|
|
291
|
+
*
|
|
292
|
+
* Manager properties that are constructor-assigned in discord.js are
|
|
293
|
+
* pre-initialized as prototype-based stubs so all methods are auto-stubbed
|
|
294
|
+
* as `jest.fn()`:
|
|
295
|
+
* - Guild text channels (`TextChannel`, `NewsChannel`): `messages`, `threads`
|
|
296
|
+
* - `DMChannel`: `messages`
|
|
297
|
+
* - `ThreadChannel`: `messages`, `members`
|
|
298
|
+
*/ function createMockChannel(Class) {
|
|
299
|
+
const instance = Object.create(Class.prototype);
|
|
300
|
+
// Guild text channels (TextChannel, NewsChannel) — messages & threads
|
|
301
|
+
// assigned in BaseGuildTextChannel constructor
|
|
302
|
+
if ('messages' in Class.prototype || Class.name === 'TextChannel' || Class.name === 'NewsChannel') {
|
|
303
|
+
instance.messages = stubDeep(Object.create(GuildMessageManager.prototype));
|
|
304
|
+
instance.threads = stubDeep(Object.create(ThreadManager.prototype));
|
|
305
|
+
}
|
|
306
|
+
// DMChannel — messages assigned in DMChannel constructor
|
|
307
|
+
if (Class.name === 'DMChannel') {
|
|
308
|
+
instance.messages = stubDeep(Object.create(DMMessageManager.prototype));
|
|
309
|
+
}
|
|
310
|
+
// ThreadChannel — messages & members assigned in ThreadChannel constructor
|
|
311
|
+
if (Class.name === 'ThreadChannel') {
|
|
312
|
+
instance.messages = stubDeep(Object.create(MessageManager.prototype));
|
|
313
|
+
instance.members = stubDeep(Object.create(ThreadMemberManager.prototype));
|
|
314
|
+
}
|
|
315
|
+
return stubDeep(instance);
|
|
316
|
+
}
|
|
259
317
|
/**
|
|
260
318
|
* Creates a smart mock {@link Message}.
|
|
261
319
|
*
|
|
@@ -263,14 +321,54 @@ function findPrototypeMethod(instance, name) {
|
|
|
263
321
|
* `pin()`, and `unpin()` throw if the message has already been deleted.
|
|
264
322
|
* `edit()` and `reply()` resolve to a new mock `Message` instance.
|
|
265
323
|
*
|
|
324
|
+
* Constructor-assigned and getter properties are pre-initialized as
|
|
325
|
+
* prototype-based stubs so `msg.author.send`, `msg.member.fetch`,
|
|
326
|
+
* `msg.channel.send`, `msg.guild.members.fetch`, `msg.thread.fetch`,
|
|
327
|
+
* and `msg.mentions.has` all work out of the box.
|
|
328
|
+
*
|
|
266
329
|
* All methods remain `jest.fn()` — overridable per test.
|
|
267
330
|
*
|
|
268
331
|
* Note: `createMockInteraction`'s `followUp()` and `editReply()` stubs return
|
|
269
332
|
* a `createMockMessage()` by default, matching the official return types.
|
|
270
|
-
*/
|
|
333
|
+
*/ /**
|
|
334
|
+
* Internal guild stub for use inside createMockMessage.
|
|
335
|
+
* Reuses the same manager stubs as createMockGuild but avoids
|
|
336
|
+
* a circular reference in the module.
|
|
337
|
+
*/ function createMockGuildForMessage() {
|
|
338
|
+
const guild = Object.create(Guild.prototype);
|
|
339
|
+
guild.members = stubDeep(Object.create(GuildMemberManager.prototype));
|
|
340
|
+
guild.channels = stubDeep(Object.create(GuildChannelManager.prototype));
|
|
341
|
+
guild.roles = stubDeep(Object.create(RoleManager.prototype));
|
|
342
|
+
guild.bans = stubDeep(Object.create(GuildBanManager.prototype));
|
|
343
|
+
return stubDeep(guild);
|
|
344
|
+
}
|
|
345
|
+
function createMockMessage() {
|
|
271
346
|
const instance = Object.create(Message.prototype);
|
|
272
347
|
const stubs = new Map();
|
|
273
348
|
instance.deleted = false;
|
|
349
|
+
// Constructor-assigned — set as prototype-based stubs
|
|
350
|
+
instance.author = stubDeep(Object.create(User.prototype));
|
|
351
|
+
// Getters on the prototype — the proxy sees them as functions and returns
|
|
352
|
+
// jest.fn(), which is wrong. Pre-initialize as own properties to shadow
|
|
353
|
+
// the prototype getters.
|
|
354
|
+
Object.defineProperty(instance, 'member', {
|
|
355
|
+
value: stubDeep(Object.create(GuildMember.prototype)),
|
|
356
|
+
writable: true
|
|
357
|
+
});
|
|
358
|
+
Object.defineProperty(instance, 'channel', {
|
|
359
|
+
value: stubDeep(Object.create(TextChannel.prototype)),
|
|
360
|
+
writable: true
|
|
361
|
+
});
|
|
362
|
+
Object.defineProperty(instance, 'guild', {
|
|
363
|
+
value: createMockGuildForMessage(),
|
|
364
|
+
writable: true
|
|
365
|
+
});
|
|
366
|
+
Object.defineProperty(instance, 'thread', {
|
|
367
|
+
value: stubDeep(Object.create(ThreadChannel.prototype)),
|
|
368
|
+
writable: true
|
|
369
|
+
});
|
|
370
|
+
// MessageMentions — constructor-assigned, has methods like .has(), .members
|
|
371
|
+
instance.mentions = stubDeep(Object.create(MessageMentions.prototype));
|
|
274
372
|
const alreadyDeleted = ()=>new Error('This message has already been deleted.');
|
|
275
373
|
stubs.set('delete', jest.fn(async ()=>{
|
|
276
374
|
if (instance.deleted) throw alreadyDeleted();
|
|
@@ -117,27 +117,36 @@ interface InteractionClass<T> {
|
|
|
117
117
|
declare function createMockInteraction<T extends object>(Class: InteractionClass<T>): DeepMocked<T>;
|
|
118
118
|
/** Creates a mock {@link User}. All methods are auto-stubbed as `jest.fn()`. */
|
|
119
119
|
declare const createMockUser: () => DeepMocked<User>;
|
|
120
|
-
/** Creates a mock {@link Client}. Managers like `client.users` are nested stubs. */
|
|
121
|
-
declare const createMockClient: () => DeepMocked<Client>;
|
|
122
|
-
/** Creates a mock {@link Guild}. Managers like `guild.members` are nested stubs. */
|
|
123
|
-
declare const createMockGuild: () => DeepMocked<Guild>;
|
|
124
120
|
/**
|
|
125
|
-
* Creates a mock
|
|
126
|
-
*
|
|
121
|
+
* Creates a mock {@link Client}.
|
|
122
|
+
*
|
|
123
|
+
* Manager methods that are constructor-assigned (not on the prototype) are
|
|
124
|
+
* pre-initialized as `jest.fn()` so they work out of the box without manual
|
|
125
|
+
* setup: `users.fetch`, `channels.fetch`, `guilds.fetch`, and
|
|
126
|
+
* `application.commands.fetch`.
|
|
127
127
|
*/
|
|
128
|
-
declare
|
|
128
|
+
declare function createMockClient(): DeepMocked<Client>;
|
|
129
129
|
/**
|
|
130
|
-
* Creates a
|
|
130
|
+
* Creates a mock {@link Guild}.
|
|
131
131
|
*
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
132
|
+
* Manager properties are constructor-assigned in discord.js. This factory
|
|
133
|
+
* pre-initializes each as a prototype-based stub so all methods are
|
|
134
|
+
* auto-stubbed as `jest.fn()`.
|
|
135
135
|
*
|
|
136
|
-
*
|
|
136
|
+
* Pre-initialized: `members`, `channels`, `roles`, `bans`.
|
|
137
|
+
*/
|
|
138
|
+
declare function createMockGuild(): DeepMocked<Guild>;
|
|
139
|
+
/**
|
|
140
|
+
* Creates a mock channel of the given class (e.g. `TextChannel`, `DMChannel`).
|
|
137
141
|
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
142
|
+
* Manager properties that are constructor-assigned in discord.js are
|
|
143
|
+
* pre-initialized as prototype-based stubs so all methods are auto-stubbed
|
|
144
|
+
* as `jest.fn()`:
|
|
145
|
+
* - Guild text channels (`TextChannel`, `NewsChannel`): `messages`, `threads`
|
|
146
|
+
* - `DMChannel`: `messages`
|
|
147
|
+
* - `ThreadChannel`: `messages`, `members`
|
|
140
148
|
*/
|
|
149
|
+
declare function createMockChannel<T extends Channel>(Class: InteractionClass<T>): DeepMocked<T>;
|
|
141
150
|
declare function createMockMessage(): DeepMocked<Message>;
|
|
142
151
|
interface ChatInputOptions {
|
|
143
152
|
subcommandGroup?: string | null;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "meocord",
|
|
3
3
|
"description": "Decorator-based Discord bot framework built on discord.js. Brings NestJS-style controllers, dependency injection, guards, and testing utilities to bot development — with a full CLI and TypeScript-first design.",
|
|
4
|
-
"version": "1.8.
|
|
4
|
+
"version": "1.8.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"lint": "eslint --fix . && tsc --noEmit && tsc --noEmit --project tsconfig.test.json",
|