meocord 4.1.2 → 4.2.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/CHANGELOG.md +98 -0
- package/dist/cjs/_shared/{controller.decorator-BywwkU9O.cjs → controller.decorator-CRn9kWob.cjs} +295 -87
- package/dist/cjs/_shared/{providers-BMMLp2SN.cjs → providers-ChvMqnLd.cjs} +74 -40
- package/dist/cjs/_shared/{sharded-cooldown-store-B65iaiAy.cjs → sharded-cooldown-store-BdAUTdYs.cjs} +1 -1
- package/dist/cjs/_shared/{startup-roots-lFc69AxC.cjs → startup-roots-CQo8mSP8.cjs} +158 -72
- package/dist/cjs/_shared/{translate-error-BnM8FbSH.cjs → translate-error-DhDVbT5M.cjs} +12 -3
- package/dist/cjs/common/index.cjs +4 -4
- package/dist/cjs/core/index.cjs +195 -157
- package/dist/cjs/decorator/index.cjs +66 -57
- package/dist/cjs/testing/index.cjs +733 -301
- package/dist/cli.json +1 -1
- package/dist/esm/bin/app-template/README.md.template +3 -1
- package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.spec.ts.template +8 -4
- package/dist/esm/bin/app-template/src/controllers/button/sample.button.controller.ts.template +2 -2
- package/dist/esm/bin/app-template/src/guards/owner.guard.spec.ts.template +8 -4
- package/dist/esm/bin/app-template/src/guards/owner.guard.ts.template +1 -1
- package/dist/esm/bin/app-template/vitest.config.ts.template +1 -1
- package/dist/esm/bin/app-template/vitest.setup.ts.template +7 -3
- package/dist/esm/build/load-config.pre-entry.js +4 -1
- package/dist/esm/common/decorator.js +6 -6
- package/dist/esm/common/deprecation.js +2 -2
- package/dist/esm/common/errors.js +2 -2
- package/dist/esm/common/execution-context.js +1 -1
- package/dist/esm/common/metadata.js +1 -1
- package/dist/esm/common/route.js +10 -1
- package/dist/esm/common/translate-error.js +1 -1
- package/dist/esm/core/command-conflicts.js +34 -34
- package/dist/esm/core/component-routes.js +74 -31
- package/dist/esm/core/dispatcher.js +51 -7
- package/dist/esm/core/filter-runner.js +2 -2
- package/dist/esm/core/handler-pipeline.js +27 -14
- package/dist/esm/core/handler-registry.js +1 -1
- package/dist/esm/core/interceptor-runner.js +2 -2
- package/dist/esm/core/meocord-factory.js +196 -157
- package/dist/esm/core/message-routes.js +4 -3
- package/dist/esm/core/observer-runner.js +4 -3
- package/dist/esm/core/providers.js +38 -21
- package/dist/esm/core/route-specificity.js +4 -4
- package/dist/esm/core/scalar-types.js +15 -3
- package/dist/esm/core/shard-context.js +1 -1
- package/dist/esm/core/startup-roots.js +2 -3
- package/dist/esm/core/theme-runtime.js +2 -2
- package/dist/esm/decorator/app.decorator.js +12 -7
- package/dist/esm/decorator/command-builder.decorator.js +4 -4
- package/dist/esm/decorator/controller-class.decorator.js +9 -4
- package/dist/esm/decorator/controller.decorator.js +110 -67
- package/dist/esm/decorator/cooldown.decorator.js +3 -3
- package/dist/esm/decorator/defer.decorator.js +4 -4
- package/dist/esm/decorator/event.decorator.js +5 -5
- package/dist/esm/decorator/filter.decorator.js +7 -7
- package/dist/esm/decorator/guard.decorator.js +8 -8
- package/dist/esm/decorator/inject.decorator.js +4 -4
- package/dist/esm/decorator/interceptor.decorator.js +7 -7
- package/dist/esm/decorator/observer.decorator.js +4 -4
- package/dist/esm/decorator/service.decorator.js +4 -4
- package/dist/esm/decorator/theme.decorator.js +3 -3
- package/dist/esm/decorator/validation.decorator.js +10 -10
- package/dist/esm/package.json.js +1 -1
- package/dist/esm/testing/catalog.js +1 -1
- package/dist/esm/testing/cooldown-store-suite.js +18 -10
- package/dist/esm/testing/discord-defaults.js +60 -7
- package/dist/esm/testing/index.js +4 -2
- package/dist/esm/testing/inspect-handler.js +20 -3
- package/dist/esm/testing/meocord-testing-module.js +229 -188
- package/dist/esm/testing/mock-fn.js +80 -10
- package/dist/esm/testing/mock-interaction.js +223 -72
- package/dist/esm/testing/response.js +1 -1
- package/dist/esm/testing/routing.js +39 -14
- package/dist/esm/testing/startup-errors.js +26 -0
- package/dist/esm/testing/strict-mocks.js +45 -0
- package/dist/esm/testing/theme.js +1 -1
- package/dist/esm/util/bundle-entry.util.js +2 -1
- package/dist/esm/util/interaction.util.js +1 -1
- package/dist/esm/util/meocord-config-validation.util.js +1 -0
- package/dist/esm/util/metadata-keys.js +1 -0
- package/dist/esm/util/refusal.util.js +148 -8
- package/dist/types/common/index.d.cts +8 -8
- package/dist/types/common/index.d.ts +8 -8
- package/dist/types/core/index.d.cts +8 -6
- package/dist/types/core/index.d.ts +8 -6
- package/dist/types/decorator/index.d.cts +30 -29
- package/dist/types/decorator/index.d.ts +30 -29
- package/dist/types/{index-D7n0FBMm.d.ts → index-CdXsFgjE.d.ts} +62 -15
- package/dist/types/{index-DEP0NYrU.d.cts → index-nn5p7OoX.d.cts} +62 -15
- package/dist/types/interface/index.d.cts +1 -1
- package/dist/types/interface/index.d.ts +1 -1
- package/dist/types/{route-C-aMI7Sl.d.cts → route-BZgFoNeB.d.cts} +5 -2
- package/dist/types/{route-C-aMI7Sl.d.ts → route-BZgFoNeB.d.ts} +5 -2
- package/dist/types/testing/index.d.cts +245 -31
- package/dist/types/testing/index.d.ts +245 -31
- package/dist/types/{theme-resolvers-DedxyscT.d.ts → theme-resolvers-BcOJVzR9.d.ts} +1 -1
- package/dist/types/{theme-resolvers-COHdX5g6.d.cts → theme-resolvers-UKr3-6Ys.d.cts} +1 -1
- package/dist/types/{translator-J6tIUfdp.d.ts → translator-BViWKtLJ.d.ts} +1 -1
- package/dist/types/{translator-B4mc1lzJ.d.cts → translator-DHMkkCpg.d.cts} +1 -1
- package/package.json +3 -2
|
@@ -9,7 +9,10 @@ type RouteParamSpecs<T extends string> = T extends `${string}{${infer Param}}${i
|
|
|
9
9
|
} | RouteParamSpecs<Rest> : never;
|
|
10
10
|
/** The words a type such as `open|closed` chooses from, as a union. */
|
|
11
11
|
type Choices<T extends string> = T extends `${infer Word}|${infer Rest}` ? Word | Choices<Rest> : T;
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* The value a typed customId segment gives its handler: a number, a boolean, one of the words to choose from, or text,
|
|
14
|
+
* as a snowflake or a uuid is.
|
|
15
|
+
*/
|
|
13
16
|
type SegmentValue<T extends string> = T extends 'int' | 'number' ? number : T extends 'bool' ? boolean : T extends `${string}|${string}` ? Choices<T> : string;
|
|
14
17
|
/**
|
|
15
18
|
* The names of a customId pattern's params, as a union, such as `'id' | 'page'` for `'list/{id}/{page:int}'`.
|
|
@@ -95,7 +98,7 @@ interface Route<T extends string = string> {
|
|
|
95
98
|
*
|
|
96
99
|
* @group Utilities
|
|
97
100
|
* @see {@link Route}
|
|
98
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
101
|
+
* @see {@link https://meocord.dev/docs/4.2/components | Buttons, selects and modals}
|
|
99
102
|
*/
|
|
100
103
|
declare function route<const T extends string>(pattern: T): Route<T>;
|
|
101
104
|
|
|
@@ -9,7 +9,10 @@ type RouteParamSpecs<T extends string> = T extends `${string}{${infer Param}}${i
|
|
|
9
9
|
} | RouteParamSpecs<Rest> : never;
|
|
10
10
|
/** The words a type such as `open|closed` chooses from, as a union. */
|
|
11
11
|
type Choices<T extends string> = T extends `${infer Word}|${infer Rest}` ? Word | Choices<Rest> : T;
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* The value a typed customId segment gives its handler: a number, a boolean, one of the words to choose from, or text,
|
|
14
|
+
* as a snowflake or a uuid is.
|
|
15
|
+
*/
|
|
13
16
|
type SegmentValue<T extends string> = T extends 'int' | 'number' ? number : T extends 'bool' ? boolean : T extends `${string}|${string}` ? Choices<T> : string;
|
|
14
17
|
/**
|
|
15
18
|
* The names of a customId pattern's params, as a union, such as `'id' | 'page'` for `'list/{id}/{page:int}'`.
|
|
@@ -95,7 +98,7 @@ interface Route<T extends string = string> {
|
|
|
95
98
|
*
|
|
96
99
|
* @group Utilities
|
|
97
100
|
* @see {@link Route}
|
|
98
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
101
|
+
* @see {@link https://meocord.dev/docs/4.2/components | Buttons, selects and modals}
|
|
99
102
|
*/
|
|
100
103
|
declare function route<const T extends string>(pattern: T): Route<T>;
|
|
101
104
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ServiceIdentifier, Container } from 'inversify';
|
|
2
|
-
import { Client, Interaction, Message, MessageReaction, PartialMessageReaction, User, PartialUser, ClientEvents, Base, Locale, GuildMember, Role, GuildBasedChannel, Guild, APIMessageTopLevelComponent, JSONEncodable, APIEmbed, MessageFlagsResolvable, TextBasedChannel, BaseChannel, APIAuthorizingIntegrationOwnersMap, CacheType, CommandInteractionOptionResolver, ThreadChannel, AnyThreadChannel, ChatInputCommandInteraction, AutocompleteInteraction, MessageContextMenuCommandInteraction, UserContextMenuCommandInteraction, PrimaryEntryPointCommandInteraction, ContextMenuCommandInteraction, CommandInteraction, ButtonInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, MentionableSelectMenuInteraction, ChannelSelectMenuInteraction, MessageComponentInteraction, ModalSubmitInteraction, BaseInteraction, DMChannel, PartialGroupDMChannel, OmitPartialGroupDMChannel, Attachment, ModalSubmitFields, MessagePayload, InteractionReplyOptions, InteractionUpdateOptions, InteractionEditReplyOptions, MessageEditOptions, APIModalInteractionResponseCallbackData, ModalComponentData, InteractionDeferReplyOptions, InteractionDeferUpdateOptions, MessageResolvable, DiscordAPIError } from 'discord.js';
|
|
2
|
+
import { Client, Interaction, Message, MessageReaction, PartialMessageReaction, User, PartialUser, ClientEvents, Base, Locale, GuildMember, Role, GuildBasedChannel, Guild, APIMessageTopLevelComponent, JSONEncodable, APIEmbed, MessageFlagsResolvable, TextBasedChannel, BaseChannel, APIAuthorizingIntegrationOwnersMap, APIInteractionGuildMember, PermissionResolvable, APIUser, CacheType, CommandInteractionOptionResolver, ThreadChannel, AnyThreadChannel, ChatInputCommandInteraction, AutocompleteInteraction, MessageContextMenuCommandInteraction, UserContextMenuCommandInteraction, PrimaryEntryPointCommandInteraction, ContextMenuCommandInteraction, CommandInteraction, ButtonInteraction, StringSelectMenuInteraction, UserSelectMenuInteraction, RoleSelectMenuInteraction, MentionableSelectMenuInteraction, ChannelSelectMenuInteraction, MessageComponentInteraction, ModalSubmitInteraction, BaseInteraction, DMChannel, PartialGroupDMChannel, OmitPartialGroupDMChannel, Attachment, ModalSubmitFields, MessagePayload, InteractionReplyOptions, InteractionUpdateOptions, InteractionEditReplyOptions, MessageEditOptions, APIModalInteractionResponseCallbackData, ModalComponentData, InteractionDeferReplyOptions, InteractionDeferUpdateOptions, MessageResolvable, DiscordAPIError } from 'discord.js';
|
|
3
3
|
import { ReactionHandlerAction, CommandType } from '../enum/index.cjs';
|
|
4
|
-
import { a8 as Provider, P as ProviderToken } from '../index-
|
|
4
|
+
import { a8 as Provider, P as ProviderToken } from '../index-nn5p7OoX.cjs';
|
|
5
5
|
import { CooldownStore, ExecutionContextType, ExecutionContext, CooldownScope, MetadataDecorator, ResponsePhase, ResponseCall } from '../common/index.cjs';
|
|
6
6
|
import { DispatchObserver, GuardInterface, InterceptorInterface, ExceptionFilter, ThemeOverride, ThemeResolvers, ThemeResolver, MessageCommandOptions, MessagePrefix, RootTheme, DeepReadonly, MeoCordTheme } from '../interface/index.cjs';
|
|
7
|
-
export { ao as ClassProvider, F as FactoryProvider, ap as ValueProvider } from '../index-
|
|
7
|
+
export { ao as ClassProvider, F as FactoryProvider, ap as ValueProvider } from '../index-nn5p7OoX.cjs';
|
|
8
8
|
import { ThemeCache } from '../common/index.cjs';
|
|
9
9
|
import { Translator } from '../common/index.cjs';
|
|
10
10
|
import '@rsbuild/core';
|
|
@@ -194,7 +194,7 @@ interface EmitResult {
|
|
|
194
194
|
* @category Module
|
|
195
195
|
* @see {@link MeoCordTestingModule}
|
|
196
196
|
* @see {@link getResponse}
|
|
197
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
197
|
+
* @see {@link https://meocord.dev/docs/4.2/invoke-and-dispatch | Invoke and dispatch}
|
|
198
198
|
*/
|
|
199
199
|
declare class TestingModule {
|
|
200
200
|
private readonly container;
|
|
@@ -470,7 +470,7 @@ declare class TestingModule {
|
|
|
470
470
|
* @group Testing
|
|
471
471
|
* @category Module
|
|
472
472
|
* @see {@link MeoCordTestingModule}
|
|
473
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
473
|
+
* @see {@link https://meocord.dev/docs/4.2/testing | The testing module}
|
|
474
474
|
*/
|
|
475
475
|
declare class TestingModuleBuilder {
|
|
476
476
|
private readonly options;
|
|
@@ -578,7 +578,12 @@ declare class TestingModuleBuilder {
|
|
|
578
578
|
* its command's builder does not register, a customId pattern given as a command name, a builder that registers
|
|
579
579
|
* another name, and component patterns that can match the same customId are named, as the bot names them.
|
|
580
580
|
*
|
|
581
|
+
* Each check reports every startup error it finds, and the first is thrown, unchanged; when there are several, each
|
|
582
|
+
* is logged first, with the file it comes from. After {@link reportAllStartupErrors}, the errors decorators kept on
|
|
583
|
+
* the classes the module runs are among them.
|
|
584
|
+
*
|
|
581
585
|
* @returns The compiled module.
|
|
586
|
+
* @throws Error for the first startup error the checks find.
|
|
582
587
|
*/
|
|
583
588
|
compile(): TestingModule;
|
|
584
589
|
}
|
|
@@ -609,7 +614,7 @@ declare class TestingModuleBuilder {
|
|
|
609
614
|
* @category Module
|
|
610
615
|
* @see {@link TestingModuleBuilder}
|
|
611
616
|
* @see {@link TestingModule}
|
|
612
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
617
|
+
* @see {@link https://meocord.dev/docs/4.2/testing | The testing module}
|
|
613
618
|
*/
|
|
614
619
|
declare class MeoCordTestingModule {
|
|
615
620
|
/**
|
|
@@ -805,12 +810,76 @@ type Mock<T extends (...args: any[]) => any = (...args: any[]) => any> = MockedF
|
|
|
805
810
|
* @see {@link createMockFn}
|
|
806
811
|
*/
|
|
807
812
|
declare function isMockFunction(fn: unknown): fn is MockInstance;
|
|
813
|
+
/**
|
|
814
|
+
* A mock function a test runner makes, as {@link useMockFn} takes it.
|
|
815
|
+
*
|
|
816
|
+
* It is callable, records its calls in `mock.calls`, and has `mockImplementation`, `mockClear` and `mockReset`.
|
|
817
|
+
* Vitest's `vi.fn`, jest's `jest.fn` and bun's `mock` make one.
|
|
818
|
+
*
|
|
819
|
+
* @group Testing
|
|
820
|
+
* @category Mocks
|
|
821
|
+
* @see {@link MockFnFactory}
|
|
822
|
+
*/
|
|
823
|
+
interface RunnerMock {
|
|
824
|
+
(...args: any[]): any;
|
|
825
|
+
/** The calls the mock has recorded, each an array of its arguments. */
|
|
826
|
+
mock: {
|
|
827
|
+
calls: readonly unknown[][];
|
|
828
|
+
};
|
|
829
|
+
/** Sets the implementation the mock runs. */
|
|
830
|
+
mockImplementation(fn: (...args: any[]) => any): unknown;
|
|
831
|
+
/** Forgets the calls the mock has recorded. */
|
|
832
|
+
mockClear(): unknown;
|
|
833
|
+
/** Forgets the calls and what the mock was told to do. */
|
|
834
|
+
mockReset(): unknown;
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* A test runner's mock function factory, such as `vi.fn`.
|
|
838
|
+
*
|
|
839
|
+
* Called with the implementation a mock starts with, it returns the runner's mock. See {@link useMockFn}.
|
|
840
|
+
*
|
|
841
|
+
* @group Testing
|
|
842
|
+
* @category Mocks
|
|
843
|
+
*/
|
|
844
|
+
type MockFnFactory = (impl?: (...args: any[]) => any) => RunnerMock;
|
|
845
|
+
/**
|
|
846
|
+
* Makes every mock `meocord/testing` creates with the test runner's own mock function, such as Vitest's `vi.fn`.
|
|
847
|
+
*
|
|
848
|
+
* Call it once in the runner's setup file. The runner then treats meocord's mocks as its own: its `clearMocks` and
|
|
849
|
+
* `mockReset` config and `vi.clearAllMocks()` reach them, its matchers read them, and its whole mock API is there.
|
|
850
|
+
* Without it, mocks are {@link createMockFn}'s own, which jest's and Vitest's `expect` read too.
|
|
851
|
+
*
|
|
852
|
+
* @remarks
|
|
853
|
+
* Each mock starts with the runner's mock holding meocord's behaviour, such as an interaction's replies, as its own
|
|
854
|
+
* implementation. {@link resetAllMocks} puts that behaviour back where the runner's `mockReset` drops it, as jest's and
|
|
855
|
+
* bun's do; a runner's own reset-all, such as jest's `resetMocks`, can't, so under jest and bun reset with
|
|
856
|
+
* {@link resetAllMocks}. node:test's `mock.fn` records calls in a shape of its own, so under node:test leave meocord's
|
|
857
|
+
* own function in place. Calling it again with the same factory does nothing; another, once a mock exists, throws.
|
|
858
|
+
*
|
|
859
|
+
* @param factory - The runner's mock function factory: `vi.fn`, `jest.fn` or bun's `mock`.
|
|
860
|
+
* @throws Error when a mock already exists from another factory, or when `factory` doesn't make a mock with jest's and
|
|
861
|
+
* Vitest's API.
|
|
862
|
+
*
|
|
863
|
+
* @example
|
|
864
|
+
* ```ts
|
|
865
|
+
* // vitest.setup.ts
|
|
866
|
+
* import { vi } from 'vitest'
|
|
867
|
+
*
|
|
868
|
+
* useMockFn(vi.fn)
|
|
869
|
+
* ```
|
|
870
|
+
*
|
|
871
|
+
* @group Testing
|
|
872
|
+
* @category Mocks
|
|
873
|
+
* @see {@link createMockFn}
|
|
874
|
+
*/
|
|
875
|
+
declare function useMockFn(factory: MockFnFactory): void;
|
|
808
876
|
/**
|
|
809
877
|
* Clears the calls every mock from `meocord/testing` has recorded, keeping what each was told to do.
|
|
810
878
|
*
|
|
811
|
-
* Vitest's `clearMocks` and jest's `clearAllMocks()` reach only their own `vi.fn()` and `jest.fn()` mocks
|
|
812
|
-
*
|
|
813
|
-
*
|
|
879
|
+
* Vitest's `clearMocks` and jest's `clearAllMocks()` reach only their own `vi.fn()` and `jest.fn()` mocks, and
|
|
880
|
+
* meocord's too once {@link useMockFn} makes them with the runner's function. This one covers `createMockFn` and
|
|
881
|
+
* everything built on it, such as `createMockInteraction` and `createMockClient`, either way. To also undo what a test
|
|
882
|
+
* set, use {@link resetAllMocks}.
|
|
814
883
|
*
|
|
815
884
|
* @example
|
|
816
885
|
* ```ts
|
|
@@ -850,7 +919,8 @@ declare function resetAllMocks(): void;
|
|
|
850
919
|
* Creates a mock function that jest's and Vitest's `expect` both read.
|
|
851
920
|
*
|
|
852
921
|
* Use it for a function double in code that must not depend on one test runner, such as a shared test helper; in a
|
|
853
|
-
* Vitest test, `vi.fn()` works as well. {@link clearAllMocks} and {@link resetAllMocks} reach it.
|
|
922
|
+
* Vitest test, `vi.fn()` works as well. {@link clearAllMocks} and {@link resetAllMocks} reach it. Once
|
|
923
|
+
* {@link useMockFn} registers a runner's function, it returns that runner's mock.
|
|
854
924
|
*
|
|
855
925
|
* @remarks
|
|
856
926
|
* `mockReturnValue`, `mockResolvedValue`, `mockRejectedValue` and `mockImplementation` share one implementation, so
|
|
@@ -973,9 +1043,35 @@ type OutsideServer = {
|
|
|
973
1043
|
* @category Mocks
|
|
974
1044
|
* @see {@link MockProps}
|
|
975
1045
|
* @see {@link createChatInputOptions}
|
|
976
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
1046
|
+
* @see {@link https://meocord.dev/docs/4.2/mocks | Mocks}
|
|
977
1047
|
*/
|
|
978
1048
|
declare function createMockInteraction<T extends object>(Class: InteractionClass<T>, props?: MockProps<T>): DeepMocked<T>;
|
|
1049
|
+
/**
|
|
1050
|
+
* Creates a mock interaction from a server the bot isn't in: one given a {@link createMockRawMember | raw member} and
|
|
1051
|
+
* that server's `guildId`, as a user-installed command run there arrives.
|
|
1052
|
+
*
|
|
1053
|
+
* It is typed as discord.js types an interaction from such a server, `'raw'`: its `guild` is `null` and its `member`
|
|
1054
|
+
* the raw member. Everything else is as {@link createMockInteraction} builds it.
|
|
1055
|
+
*
|
|
1056
|
+
* @param Class - The discord.js interaction class to mock.
|
|
1057
|
+
* @param props - The interaction's properties, with the raw `member` and the server's `guildId`; see {@link MockProps}.
|
|
1058
|
+
*
|
|
1059
|
+
* @example
|
|
1060
|
+
* ```ts
|
|
1061
|
+
* import { expect } from 'vitest'
|
|
1062
|
+
*
|
|
1063
|
+
* const command = createMockInteraction(ChatInputCommandInteraction, { guildId: '100000000000000001', member: createMockRawMember() })
|
|
1064
|
+
* expect(command.inRawGuild()).toBe(true)
|
|
1065
|
+
* ```
|
|
1066
|
+
*
|
|
1067
|
+
* @group Testing
|
|
1068
|
+
* @category Mocks
|
|
1069
|
+
*/
|
|
1070
|
+
declare function createMockInteraction<T extends object>(Class: InteractionClass<T>, props: Omit<MockProps<WithCache<T, 'raw'>>, 'options'> & {
|
|
1071
|
+
options?: object;
|
|
1072
|
+
guildId: string;
|
|
1073
|
+
member: APIInteractionGuildMember;
|
|
1074
|
+
}): DeepMocked<WithCache<T, 'raw'>>;
|
|
979
1075
|
/**
|
|
980
1076
|
* Creates a mock interaction from outside a server: one given `guild: null`, `member: null` or a DM channel, as from a
|
|
981
1077
|
* direct message or a server the bot isn't in.
|
|
@@ -1171,6 +1267,48 @@ type ChannelOf<T> = T extends ThreadChannel ? AnyThreadChannel : T;
|
|
|
1171
1267
|
* @see {@link createMockGuild}
|
|
1172
1268
|
*/
|
|
1173
1269
|
declare function createMockChannel<T extends BaseChannel>(Class: InteractionClass<T>, props?: MockProps<T>): DeepMocked<ChannelOf<T>>;
|
|
1270
|
+
/**
|
|
1271
|
+
* What {@link createMockRawMember} builds a member with: any field Discord sends, with `permissions` as a permission
|
|
1272
|
+
* set and `user` as the fields of the user given.
|
|
1273
|
+
*
|
|
1274
|
+
* @group Testing
|
|
1275
|
+
* @category Mocks
|
|
1276
|
+
*/
|
|
1277
|
+
type MockRawMemberOverrides = Partial<Omit<APIInteractionGuildMember, 'permissions' | 'user'>> & {
|
|
1278
|
+
/** The member's permissions in the channel; unless given, what @everyone has in a server Discord makes today. */
|
|
1279
|
+
permissions?: PermissionResolvable;
|
|
1280
|
+
/** The member's user; a new person, `username` `'user'`, unless given. */
|
|
1281
|
+
user?: Partial<APIUser>;
|
|
1282
|
+
};
|
|
1283
|
+
/**
|
|
1284
|
+
* Creates the member Discord sends with an interaction from a server the bot isn't in, as discord.js keeps it.
|
|
1285
|
+
*
|
|
1286
|
+
* Give it as an interaction's `member`, with the server's `guildId` and no `guild`, to test a user-installed command
|
|
1287
|
+
* run there: the interaction reads `inRawGuild()` true, `guild` and `channel` null, `user` the member's user and
|
|
1288
|
+
* `memberPermissions` the member's, and a user option's member is the plain member Discord resolves too.
|
|
1289
|
+
*
|
|
1290
|
+
* @remarks
|
|
1291
|
+
* It is plain data, not a `GuildMember`: `roles` are role ids, `permissions` is the permission bitfield as a string,
|
|
1292
|
+
* and the rest are as Discord sends them, `null` for what may be absent and `false` for a flag. Code that reads
|
|
1293
|
+
* `member.roles.cache` throws on it, as it does in Discord.
|
|
1294
|
+
*
|
|
1295
|
+
* @param overrides - Fields for the member; see {@link MockRawMemberOverrides}.
|
|
1296
|
+
* @returns The member, as an `APIInteractionGuildMember`.
|
|
1297
|
+
*
|
|
1298
|
+
* @example
|
|
1299
|
+
* ```ts
|
|
1300
|
+
* import { expect } from 'vitest'
|
|
1301
|
+
*
|
|
1302
|
+
* const member = createMockRawMember({ roles: ['300000000000000001'], permissions: [PermissionFlagsBits.SendMessages] })
|
|
1303
|
+
* const command = createMockInteraction(ChatInputCommandInteraction, { guildId: '100000000000000001', member })
|
|
1304
|
+
* expect(command.inRawGuild()).toBe(true)
|
|
1305
|
+
* ```
|
|
1306
|
+
*
|
|
1307
|
+
* @group Testing
|
|
1308
|
+
* @category Mocks
|
|
1309
|
+
* @see {@link createMockMember}
|
|
1310
|
+
*/
|
|
1311
|
+
declare function createMockRawMember(overrides?: MockRawMemberOverrides): APIInteractionGuildMember;
|
|
1174
1312
|
/**
|
|
1175
1313
|
* What {@link createMockMember} builds a member with.
|
|
1176
1314
|
*
|
|
@@ -1365,6 +1503,60 @@ interface ChatInputOptions {
|
|
|
1365
1503
|
*/
|
|
1366
1504
|
declare function createChatInputOptions<Cached extends CacheType = any>(opts?: ChatInputOptions): DeepMocked<CommandInteractionOptionResolver<Cached>>;
|
|
1367
1505
|
|
|
1506
|
+
/**
|
|
1507
|
+
* Has every mock from `meocord/testing` compute the values discord.js computes, where it reads a placeholder otherwise.
|
|
1508
|
+
*
|
|
1509
|
+
* Call it once in the test runner's setup file. A message's `editable`, `deletable`, `pinnable`, `crosspostable`,
|
|
1510
|
+
* `bulkDeletable`, `hasThread` and `partial`, a member's `manageable`, `kickable`, `bannable` and `moderatable`, a
|
|
1511
|
+
* role's `editable`, a channel's and a thread's `viewable`, `manageable`, `deletable`, `joinable` and the rest, and
|
|
1512
|
+
* `partial` on users, channels and reactions then run discord.js's own getters against the mock, and `message.thread`
|
|
1513
|
+
* is `null` when the channel caches no thread under the message's id. No placeholder warning is logged.
|
|
1514
|
+
*
|
|
1515
|
+
* @remarks
|
|
1516
|
+
* Those getters read what a server holds, so strict mocks also have it as Discord sends it: the bot's member is in
|
|
1517
|
+
* the server's member cache from the start, @everyone has the permissions Discord gives it in a new server, and a
|
|
1518
|
+
* channel or thread made without a server has one of its own, a thread with a text channel as its parent. A message
|
|
1519
|
+
* has empty `content`, a thread is neither archived nor locked, and a reaction's `count` is 1. Give the bot's member
|
|
1520
|
+
* a role to let it do more, such as kick a member its role ranks above. A value the test sets on a mock wins over the
|
|
1521
|
+
* computed one. The next major version (5.0) computes these values without the call.
|
|
1522
|
+
*
|
|
1523
|
+
* @throws Error when a mock already exists, which was made without it.
|
|
1524
|
+
*
|
|
1525
|
+
* @example
|
|
1526
|
+
* ```ts
|
|
1527
|
+
* // vitest.setup.ts
|
|
1528
|
+
* useStrictMocks()
|
|
1529
|
+
* ```
|
|
1530
|
+
*
|
|
1531
|
+
* @group Testing
|
|
1532
|
+
* @category Mocks
|
|
1533
|
+
* @see {@link useMockFn}
|
|
1534
|
+
* @see {@link https://meocord.dev/docs/4.2/mocks | Mocks}
|
|
1535
|
+
*/
|
|
1536
|
+
declare function useStrictMocks(): void;
|
|
1537
|
+
|
|
1538
|
+
/**
|
|
1539
|
+
* Has decorators keep the startup errors they find, as a bot built with `startupErrors: 'all'` does, so a testing
|
|
1540
|
+
* module reports every one at once.
|
|
1541
|
+
*
|
|
1542
|
+
* Call it once in the test runner's setup file, before any test imports a controller: a decorator runs when its class
|
|
1543
|
+
* is defined, as its file is imported. A decorator given what it cannot use then throws nothing as the file loads, and
|
|
1544
|
+
* `MeoCordTestingModule.create(...).compile()` logs every startup error of the classes it runs, each with its handler
|
|
1545
|
+
* and file, then throws the first. Without it, a decorator throws its error as its class is defined.
|
|
1546
|
+
*
|
|
1547
|
+
* @example
|
|
1548
|
+
* ```ts
|
|
1549
|
+
* // vitest.setup.ts
|
|
1550
|
+
* reportAllStartupErrors()
|
|
1551
|
+
* ```
|
|
1552
|
+
*
|
|
1553
|
+
* @group Testing
|
|
1554
|
+
* @category Module
|
|
1555
|
+
* @see {@link MeoCordTestingModule}
|
|
1556
|
+
* @see {@link https://meocord.dev/docs/4.2/configuration | Configuration}
|
|
1557
|
+
*/
|
|
1558
|
+
declare function reportAllStartupErrors(): void;
|
|
1559
|
+
|
|
1368
1560
|
/**
|
|
1369
1561
|
* The command types routed by a `customId` pattern: buttons, select menus and modals.
|
|
1370
1562
|
*
|
|
@@ -1423,9 +1615,16 @@ interface ResolvedRoute {
|
|
|
1423
1615
|
* handler receives `params`.
|
|
1424
1616
|
*/
|
|
1425
1617
|
values?: Record<string, string | number | boolean>;
|
|
1618
|
+
/**
|
|
1619
|
+
* For a component route, the other patterns of its type that match the `customId` too, in the order they rank
|
|
1620
|
+
* behind it: the ones that lost to it. Absent when no other pattern matches. A test reads it; `toEqual` doesn't
|
|
1621
|
+
* compare it, so an assertion on the other fields holds either way.
|
|
1622
|
+
*/
|
|
1623
|
+
alsoMatches?: string[];
|
|
1426
1624
|
}
|
|
1427
1625
|
/**
|
|
1428
|
-
* Two patterns of one component type that can both match a `customId`, as {@link findRouteConflicts}
|
|
1626
|
+
* Two patterns of one component type that rank equally and can both match a `customId`, as {@link findRouteConflicts}
|
|
1627
|
+
* reports them.
|
|
1429
1628
|
*
|
|
1430
1629
|
* @group Testing
|
|
1431
1630
|
* @category Inspection
|
|
@@ -1435,6 +1634,11 @@ interface RouteConflict {
|
|
|
1435
1634
|
type: ComponentCommandType;
|
|
1436
1635
|
/** The two patterns. */
|
|
1437
1636
|
patterns: [string, string];
|
|
1637
|
+
/**
|
|
1638
|
+
* The pattern that runs for the `customId`s both match: the one listed first. A test reads it; `toEqual` doesn't
|
|
1639
|
+
* compare it, so an assertion on `type` and `patterns` alone holds.
|
|
1640
|
+
*/
|
|
1641
|
+
runs: string;
|
|
1438
1642
|
}
|
|
1439
1643
|
/**
|
|
1440
1644
|
* Resolves which handler a component's `customId` or a message's content reaches, as dispatch routes it.
|
|
@@ -1471,33 +1675,36 @@ interface RouteConflict {
|
|
|
1471
1675
|
* @group Testing
|
|
1472
1676
|
* @category Inspection
|
|
1473
1677
|
* @see {@link findRouteConflicts}
|
|
1474
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
1678
|
+
* @see {@link https://meocord.dev/docs/4.2/components | Buttons, selects and modals}
|
|
1475
1679
|
*/
|
|
1476
1680
|
declare function resolveRoute(app: ControllerClass, input: {
|
|
1477
1681
|
type: ComponentCommandType;
|
|
1478
1682
|
customId: string;
|
|
1479
1683
|
} | MessageToResolve): ResolvedRoute | undefined;
|
|
1480
1684
|
/**
|
|
1481
|
-
* Finds component patterns that can match the same `customId`, which MeoCord otherwise only warns
|
|
1685
|
+
* Finds component patterns that rank equally and can match the same `customId`, which MeoCord otherwise only warns
|
|
1686
|
+
* about at startup.
|
|
1482
1687
|
*
|
|
1483
|
-
* Use it in a test to keep the warning from reaching production
|
|
1484
|
-
*
|
|
1688
|
+
* Use it in a test to keep the warning from reaching production. Patterns rank segment by segment, a literal before a
|
|
1689
|
+
* param and then the narrower type, so two collide only with the same literals and equally narrow params at each
|
|
1690
|
+
* position, such as `{action:close|reopen}` and `{step:close|confirm}`, which both take `close`; the one listed
|
|
1691
|
+
* first runs. Patterns are compared within a component type.
|
|
1485
1692
|
*
|
|
1486
1693
|
* @param app - The application class decorated with `@MeoCord`.
|
|
1487
|
-
* @returns Each pair of patterns that can match the same `customId`, with its component type.
|
|
1694
|
+
* @returns Each pair of patterns that rank equally and can match the same `customId`, with its component type.
|
|
1488
1695
|
*
|
|
1489
1696
|
* @example
|
|
1490
1697
|
* ```ts
|
|
1491
1698
|
* import { expect } from 'vitest'
|
|
1492
1699
|
*
|
|
1493
1700
|
* @Controller()
|
|
1494
|
-
* class
|
|
1495
|
-
* @Command('
|
|
1496
|
-
* async
|
|
1497
|
-
* @Command('
|
|
1498
|
-
* async
|
|
1701
|
+
* class TicketController {
|
|
1702
|
+
* @Command('ticket/{action:close|reopen}', CommandType.BUTTON)
|
|
1703
|
+
* async act(interaction: ButtonInteraction) { await respond(interaction).send('Done.') }
|
|
1704
|
+
* @Command('ticket/{step:close|confirm}', CommandType.BUTTON)
|
|
1705
|
+
* async step(interaction: ButtonInteraction) { await respond(interaction).send('Next.') }
|
|
1499
1706
|
* }
|
|
1500
|
-
* @MeoCord({ controllers: [
|
|
1707
|
+
* @MeoCord({ controllers: [TicketController], clientOptions: { intents: [] } })
|
|
1501
1708
|
* class App {}
|
|
1502
1709
|
* expect(findRouteConflicts(App)).toHaveLength(1)
|
|
1503
1710
|
* ```
|
|
@@ -1584,7 +1791,7 @@ declare function createModalFields(values: Record<string, string | string[] | At
|
|
|
1584
1791
|
* @group Testing
|
|
1585
1792
|
* @category Inspection
|
|
1586
1793
|
* @see {@link createTranslator}
|
|
1587
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
1794
|
+
* @see {@link https://meocord.dev/docs/4.2/localisation | Localisation}
|
|
1588
1795
|
*/
|
|
1589
1796
|
declare function expectCompleteCatalog(translator: Translator<any>, options?: {
|
|
1590
1797
|
meocord?: boolean;
|
|
@@ -1622,7 +1829,8 @@ interface CooldownStoreSuiteFramework {
|
|
|
1622
1829
|
* The cases cover what a shared store most often gets wrong: a sliding window rather than fixed buckets,
|
|
1623
1830
|
* `retryAfterMs` from the oldest call still in the window, calls in the same millisecond kept apart, and concurrent
|
|
1624
1831
|
* calls at the limit where exactly one may pass. They use real time, with windows short enough that the suite takes
|
|
1625
|
-
* a few seconds
|
|
1832
|
+
* a few seconds and margins wide enough that a correct store passes when timers fire late, as on a busy CI runner,
|
|
1833
|
+
* and each case counts under keys of its own. Each window is a whole number of milliseconds, as
|
|
1626
1834
|
* `@Cooldown` gives a store: it rounds its `seconds` to the millisecond.
|
|
1627
1835
|
*
|
|
1628
1836
|
* @param name - What the store is called in the report.
|
|
@@ -1639,7 +1847,7 @@ interface CooldownStoreSuiteFramework {
|
|
|
1639
1847
|
* @group Testing
|
|
1640
1848
|
* @category Inspection
|
|
1641
1849
|
* @see {@link CooldownStore}
|
|
1642
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
1850
|
+
* @see {@link https://meocord.dev/docs/4.2/recipes/cooldown-stores | Cooldown stores}
|
|
1643
1851
|
*/
|
|
1644
1852
|
declare function testCooldownStore(name: string, factory: () => CooldownStore | Promise<CooldownStore>, { describe, it, expect }: CooldownStoreSuiteFramework): void;
|
|
1645
1853
|
|
|
@@ -1751,6 +1959,12 @@ interface HandlerInspection {
|
|
|
1751
1959
|
readonly cooldowns: readonly InspectedCooldown[];
|
|
1752
1960
|
/** The `@MessageHandler` pattern, or `undefined` for a listener and for any other kind of handler. */
|
|
1753
1961
|
readonly pattern: string | undefined;
|
|
1962
|
+
/**
|
|
1963
|
+
* The routes the handler answers because a class its controller extends declares them, such as `slash "ping"`:
|
|
1964
|
+
* those its own class doesn't declare for it. Empty for a handler that declares all it answers. A test reads it;
|
|
1965
|
+
* `toEqual` doesn't compare it, so an assertion on the other fields holds either way.
|
|
1966
|
+
*/
|
|
1967
|
+
readonly inheritedRoutes: readonly string[];
|
|
1754
1968
|
/** The `app`'s observers, in the order they are told about the call; empty without an `app`. */
|
|
1755
1969
|
readonly observers: readonly (new (...args: any[]) => DispatchObserver)[];
|
|
1756
1970
|
/**
|
|
@@ -1847,7 +2061,7 @@ interface InspectHandlerOptions {
|
|
|
1847
2061
|
* @group Testing
|
|
1848
2062
|
* @category Inspection
|
|
1849
2063
|
* @see {@link createExecutionContext}
|
|
1850
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
2064
|
+
* @see {@link https://meocord.dev/docs/4.2/testing-recipes | Testing recipes}
|
|
1851
2065
|
*/
|
|
1852
2066
|
declare function inspectHandler<C extends new (...args: any[]) => unknown>(controller: C, methodName: keyof InstanceType<C> & string, options?: InspectHandlerOptions): HandlerInspection;
|
|
1853
2067
|
|
|
@@ -1925,7 +2139,7 @@ interface ResponseReport {
|
|
|
1925
2139
|
* @group Testing
|
|
1926
2140
|
* @category Inspection
|
|
1927
2141
|
* @see {@link TestingModule}
|
|
1928
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
2142
|
+
* @see {@link https://meocord.dev/docs/4.2/invoke-and-dispatch | Invoke and dispatch}
|
|
1929
2143
|
*/
|
|
1930
2144
|
declare function getResponse(interaction: Interaction): ResponseReport;
|
|
1931
2145
|
/**
|
|
@@ -1982,7 +2196,7 @@ type MockThemeArgs = Partial<RootTheme> extends RootTheme ? [overrides?: RootThe
|
|
|
1982
2196
|
* @group Testing
|
|
1983
2197
|
* @category Mocks
|
|
1984
2198
|
* @see {@link withTheme}
|
|
1985
|
-
* @see {@link https://meocord.dev/docs/4.
|
|
2199
|
+
* @see {@link https://meocord.dev/docs/4.2/theming | Theming}
|
|
1986
2200
|
*/
|
|
1987
2201
|
declare function createMockTheme(...[overrides]: MockThemeArgs): DeepReadonly<MeoCordTheme>;
|
|
1988
2202
|
/**
|
|
@@ -2011,5 +2225,5 @@ declare function createMockTheme(...[overrides]: MockThemeArgs): DeepReadonly<Me
|
|
|
2011
2225
|
*/
|
|
2012
2226
|
declare function withTheme<T>(theme: DeepReadonly<MeoCordTheme> | RootTheme, fn: () => T): T;
|
|
2013
2227
|
|
|
2014
|
-
export { MeoCordTestingModule, Provider, ProviderToken, TestingModule, TestingModuleBuilder, clearAllMocks, createChatInputOptions, createDiscordError, createExecutionContext, createMock, createMockChannel, createMockClient, createMockFn, createMockGuild, createMockInteraction, createMockMember, createMockMessage, createMockTheme, createMockUser, createModalFields, expectCompleteCatalog, findRouteConflicts, getResponse, inspectHandler, isMockFunction, resetAllMocks, resolveRoute, testCooldownStore, withTheme };
|
|
2015
|
-
export type { ChatInputOptions, ComponentCommandType, CooldownStoreSuiteFramework, DeepMocked, DispatchedCall, DispatchedHandler, EmitResult, ExecutionContextOptions, FromAppOptions, HandlerInspection, HandlerName, InspectHandlerOptions, InspectedCooldown, InspectedFilter, InspectedGuard, InspectedInterceptor, InvocationResult, MessageToResolve, Mock, MockGuildOverrides, MockInstance, MockMemberOverrides, MockMessageOverrides, MockProps, MockResult, MockState, MockedFunction, ResolvedRoute, ResponseReport, RouteConflict, TestingModuleInitOptions, TestingModuleOptions };
|
|
2228
|
+
export { MeoCordTestingModule, Provider, ProviderToken, TestingModule, TestingModuleBuilder, clearAllMocks, createChatInputOptions, createDiscordError, createExecutionContext, createMock, createMockChannel, createMockClient, createMockFn, createMockGuild, createMockInteraction, createMockMember, createMockMessage, createMockRawMember, createMockTheme, createMockUser, createModalFields, expectCompleteCatalog, findRouteConflicts, getResponse, inspectHandler, isMockFunction, reportAllStartupErrors, resetAllMocks, resolveRoute, testCooldownStore, useMockFn, useStrictMocks, withTheme };
|
|
2229
|
+
export type { ChatInputOptions, ComponentCommandType, CooldownStoreSuiteFramework, DeepMocked, DispatchedCall, DispatchedHandler, EmitResult, ExecutionContextOptions, FromAppOptions, HandlerInspection, HandlerName, InspectHandlerOptions, InspectedCooldown, InspectedFilter, InspectedGuard, InspectedInterceptor, InvocationResult, MessageToResolve, Mock, MockFnFactory, MockGuildOverrides, MockInstance, MockMemberOverrides, MockMessageOverrides, MockProps, MockRawMemberOverrides, MockResult, MockState, MockedFunction, ResolvedRoute, ResponseReport, RouteConflict, RunnerMock, TestingModuleInitOptions, TestingModuleOptions };
|