meocord 2.0.0-beta.1 → 2.0.0

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.
package/README.md CHANGED
@@ -41,7 +41,7 @@
41
41
 
42
42
  ### Prerequisites
43
43
 
44
- - **Runtime**: Node.js (latest LTS) or Bun 1.x+
44
+ - **Runtime**: Node.js 22 or newer, or Bun 1.x+
45
45
  - **TypeScript**: 5.0+
46
46
  - **Package manager**: npm, yarn, pnpm, or bun
47
47
 
@@ -410,6 +410,33 @@ interaction.guildId = 'guild-123'
410
410
 
411
411
  Works for any discord.js class — interactions, `Message`, `MessageReaction`, and anything else. No per-type maintenance.
412
412
 
413
+ **Assignable to the real class** — the returned mock can be passed straight to code that expects the discord.js type. No `as unknown as ButtonInteraction` at the call site.
414
+
415
+ ```typescript
416
+ const interaction = createMockInteraction(ButtonInteraction)
417
+
418
+ await controller.handleButton(interaction) // takes a real ButtonInteraction
419
+ ```
420
+
421
+ **Property overrides at construction** — pass a second argument to set properties as the mock is built. This is required for anything discord.js declares `readonly` (`ModalSubmitInteraction#customId` and `#fields`, `MessageComponentInteraction#message`, `client`, `guildId` on some classes), since those cannot be assigned afterwards. It is also how you set a property backed by a getter-only prototype accessor, such as `targetUser` or `targetMessage` on a context menu.
422
+
423
+ ```typescript
424
+ import { createMockInteraction, createMockUser } from 'meocord/testing'
425
+ import { ModalSubmitInteraction, UserContextMenuCommandInteraction } from 'discord.js'
426
+
427
+ const modal = createMockInteraction(ModalSubmitInteraction, {
428
+ customId: 'wish-import-800000000',
429
+ fields: { getTextInputValue: () => '{"pulls":[]}' } as unknown as ModalSubmitInteraction['fields'],
430
+ })
431
+
432
+ const contextMenu = createMockInteraction(UserContextMenuCommandInteraction, {
433
+ commandName: 'profile',
434
+ targetUser: createMockUser(),
435
+ })
436
+ ```
437
+
438
+ The override record is typed as `MockProps<T>`, exported from `meocord/testing`. Every key is optional, and a misspelled property name is a compile error.
439
+
413
440
  ### `createChatInputOptions`
414
441
 
415
442
  Builds a typed options resolver from a plain record. Type routing mirrors the real `CommandInteractionOptionResolver`: wrong-type access returns `null`, `required=true` throws if the option is absent.
@@ -502,6 +529,19 @@ const module = MeoCordTestingModule.create({
502
529
 
503
530
  `canActivate: () => true` allows the method to run. `() => false` blocks it. Multiple guards chain fluently.
504
531
 
532
+ ### `overrideProvider`
533
+
534
+ Replaces a provider already registered on the module. The value is typed as `Partial<T>`, so a double only has to cover the methods the test exercises — a class with a private member could never be satisfied by a full object literal anyway. A misspelled method name is still a compile error.
535
+
536
+ ```typescript
537
+ const module = MeoCordTestingModule.create({
538
+ controllers: [GreetingSlashController],
539
+ providers: [{ provide: GreetingService, useValue: realGreetingService }],
540
+ })
541
+ .overrideProvider(GreetingService).useValue({ buildGreeting: createMockFn() })
542
+ .compile()
543
+ ```
544
+
505
545
  ### Full example
506
546
 
507
547
  ```typescript
@@ -747,7 +747,14 @@ function createMockMessage() {
747
747
  * interaction.options.getSubcommand() // → 'notes'
748
748
  * interaction.options.getNumber('uid') // → 12345678
749
749
  * ```
750
- */ function createChatInputOptions(opts = {}) {
750
+ */ // `any` is the default rather than `CacheType` because TypeScript types a generic
751
+ // class's `prototype` with `any` for its parameters, and createMockInteraction infers
752
+ // T from exactly that — `createMockInteraction(ChatInputCommandInteraction)` produces
753
+ // an interaction whose `options` is `CommandInteractionOptionResolver<any>`. Defaulting
754
+ // to `CacheType` instead makes CacheTypeReducer widen getChannel's return with a `null`
755
+ // the target rejects, and the resolver stops being assignable to the property it exists
756
+ // to fill. Pass Cached explicitly when the interaction under test is pinned.
757
+ function createChatInputOptions(opts = {}) {
751
758
  const { subcommandGroup = null, subcommand = null, ...values } = opts;
752
759
  function resolveOrThrow(name, value, required) {
753
760
  if (value === null) {
@@ -434,7 +434,14 @@ function createMockMessage() {
434
434
  * interaction.options.getSubcommand() // → 'notes'
435
435
  * interaction.options.getNumber('uid') // → 12345678
436
436
  * ```
437
- */ function createChatInputOptions(opts = {}) {
437
+ */ // `any` is the default rather than `CacheType` because TypeScript types a generic
438
+ // class's `prototype` with `any` for its parameters, and createMockInteraction infers
439
+ // T from exactly that — `createMockInteraction(ChatInputCommandInteraction)` produces
440
+ // an interaction whose `options` is `CommandInteractionOptionResolver<any>`. Defaulting
441
+ // to `CacheType` instead makes CacheTypeReducer widen getChannel's return with a `null`
442
+ // the target rejects, and the resolver stops being assignable to the property it exists
443
+ // to fill. Pass Cached explicitly when the interaction under test is pinned.
444
+ function createChatInputOptions(opts = {}) {
438
445
  const { subcommandGroup = null, subcommand = null, ...values } = opts;
439
446
  function resolveOrThrow(name, value, required) {
440
447
  if (value === null) {
@@ -1,6 +1,6 @@
1
1
  import { ServiceIdentifier, Container } from 'inversify';
2
2
  import { GuardInterface } from '../interface/index.js';
3
- import { CommandInteractionOptionResolver, Channel, Client, Guild, Message, User } from 'discord.js';
3
+ import { CacheType, CommandInteractionOptionResolver, Channel, Client, Guild, Message, User } from 'discord.js';
4
4
  import 'webpack';
5
5
  import '../controller.enum-QA-IuReF.js';
6
6
 
@@ -272,7 +272,7 @@ interface ChatInputOptions {
272
272
  * interaction.options.getNumber('uid') // → 12345678
273
273
  * ```
274
274
  */
275
- declare function createChatInputOptions(opts?: ChatInputOptions): DeepMocked<CommandInteractionOptionResolver>;
275
+ declare function createChatInputOptions<Cached extends CacheType = any>(opts?: ChatInputOptions): DeepMocked<CommandInteractionOptionResolver<Cached>>;
276
276
 
277
277
  export { MeoCordTestingModule, TestingModule, TestingModuleBuilder, createChatInputOptions, createMockChannel, createMockClient, createMockFn, createMockGuild, createMockInteraction, createMockMessage, createMockUser, isMockFunction };
278
278
  export type { ChatInputOptions, ClassProvider, DeepMocked, Mock, MockInstance, MockProps, MockResult, MockState, MockedFunction, Provider, TestingModuleOptions, ValueProvider };
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": "2.0.0-beta.1",
4
+ "version": "2.0.0",
5
5
  "type": "module",
6
6
  "engines": {
7
7
  "node": ">=22"