From d9bbb28159504f53f5b0d262da3f66bbfb3d0bd6 Mon Sep 17 00:00:00 2001 From: Jeroen Claassens Date: Mon, 23 Oct 2023 13:42:30 +0200 Subject: [PATCH] refactor: move preconditions resolvers to dedicated files (#679) --- src/index.ts | 1 + .../clientPermissions.ts | 19 ++++ src/lib/precondition-resolvers/cooldown.ts | 42 ++++++++ src/lib/precondition-resolvers/index.ts | 5 + src/lib/precondition-resolvers/nsfw.ts | 11 +++ src/lib/precondition-resolvers/runIn.ts | 46 +++++++++ .../precondition-resolvers/userPermissions.ts | 19 ++++ src/lib/structures/Command.ts | 82 ++++----------- .../clientPermissions.test.ts | 26 +++++ tests/precondition-resolvers/cooldown.test.ts | 76 ++++++++++++++ tests/precondition-resolvers/nsfw.test.ts | 24 +++++ tests/precondition-resolvers/runIn.test.ts | 99 +++++++++++++++++++ .../userPermissions.test.ts | 26 +++++ tests/resolvers/boolean.test.ts | 36 ++++--- tests/resolvers/date.test.ts | 16 +-- tests/resolvers/emoji.test.ts | 16 +-- tests/resolvers/enum.test.ts | 20 ++-- tests/resolvers/float.test.ts | 16 +-- tests/resolvers/hyperlink.test.ts | 8 +- tests/resolvers/integer.test.ts | 16 +-- tests/resolvers/number.test.ts | 26 ++--- tests/resolvers/string.test.ts | 24 ++--- 22 files changed, 515 insertions(+), 139 deletions(-) create mode 100644 src/lib/precondition-resolvers/clientPermissions.ts create mode 100644 src/lib/precondition-resolvers/cooldown.ts create mode 100644 src/lib/precondition-resolvers/index.ts create mode 100644 src/lib/precondition-resolvers/nsfw.ts create mode 100644 src/lib/precondition-resolvers/runIn.ts create mode 100644 src/lib/precondition-resolvers/userPermissions.ts create mode 100644 tests/precondition-resolvers/clientPermissions.test.ts create mode 100644 tests/precondition-resolvers/cooldown.test.ts create mode 100644 tests/precondition-resolvers/nsfw.test.ts create mode 100644 tests/precondition-resolvers/runIn.test.ts create mode 100644 tests/precondition-resolvers/userPermissions.test.ts diff --git a/src/index.ts b/src/index.ts index 7d00f458d..2c580e5df 100644 --- a/src/index.ts +++ b/src/index.ts @@ -60,6 +60,7 @@ export * from './lib/parsers/Args'; export * from './lib/plugins/Plugin'; export * from './lib/plugins/PluginManager'; export * from './lib/plugins/symbols'; +export * as PreconditionResolvers from './lib/precondition-resolvers/index'; export type { EmojiObject } from './lib/resolvers/emoji'; export * as Resolvers from './lib/resolvers/index'; export type { MessageResolverOptions } from './lib/resolvers/message'; diff --git a/src/lib/precondition-resolvers/clientPermissions.ts b/src/lib/precondition-resolvers/clientPermissions.ts new file mode 100644 index 000000000..1a4642f8e --- /dev/null +++ b/src/lib/precondition-resolvers/clientPermissions.ts @@ -0,0 +1,19 @@ +import { PermissionsBitField, type PermissionResolvable } from 'discord.js'; +import { CommandPreConditions } from '../types/Enums'; +import type { PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; + +/** + * Appends the `ClientPermissions` precondition when {@link Command.Options.requiredClientPermissions} resolves to a + * non-zero bitfield. + * @param requiredClientPermissions The required client permissions. + * @param preconditionContainerArray The precondition container array to append the precondition to. + */ +export function parseConstructorPreConditionsRequiredClientPermissions( + requiredClientPermissions: PermissionResolvable | undefined, + preconditionContainerArray: PreconditionContainerArray +) { + const permissions = new PermissionsBitField(requiredClientPermissions); + if (permissions.bitfield !== 0n) { + preconditionContainerArray.append({ name: CommandPreConditions.ClientPermissions, context: { permissions } }); + } +} diff --git a/src/lib/precondition-resolvers/cooldown.ts b/src/lib/precondition-resolvers/cooldown.ts new file mode 100644 index 000000000..5475b3873 --- /dev/null +++ b/src/lib/precondition-resolvers/cooldown.ts @@ -0,0 +1,42 @@ +import { container } from '@sapphire/pieces'; +import type { Command } from '../structures/Command'; +import { BucketScope, CommandPreConditions } from '../types/Enums'; +import { type PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; + +/** + * Appends the `Cooldown` precondition when {@link Command.Options.cooldownLimit} and + * {@link Command.Options.cooldownDelay} are both non-zero. + * + * @param command The command to parse cooldowns for. + * @param cooldownLimit The cooldown limit to use. + * @param cooldownDelay The cooldown delay to use. + * @param cooldownScope The cooldown scope to use. + * @param cooldownFilteredUsers The cooldown filtered users to use. + * @param preconditionContainerArray The precondition container array to append the precondition to. + */ +export function parseConstructorPreConditionsCooldown( + command: Command, + cooldownLimit: number | undefined, + cooldownDelay: number | undefined, + cooldownScope: BucketScope | undefined, + cooldownFilteredUsers: string[] | undefined, + preconditionContainerArray: PreconditionContainerArray +) { + const { defaultCooldown } = container.client.options; + + // We will check for whether the command is filtered from the defaults, but we will allow overridden values to + // be set. If an overridden value is passed, it will have priority. Otherwise, it will default to 0 if filtered + // (causing the precondition to not be registered) or the default value with a fallback to a single-use cooldown. + const filtered = defaultCooldown?.filteredCommands?.includes(command.name) ?? false; + const limit = cooldownLimit ?? (filtered ? 0 : defaultCooldown?.limit ?? 1); + const delay = cooldownDelay ?? (filtered ? 0 : defaultCooldown?.delay ?? 0); + + if (limit && delay) { + const scope = cooldownScope ?? defaultCooldown?.scope ?? BucketScope.User; + const filteredUsers = cooldownFilteredUsers ?? defaultCooldown?.filteredUsers; + preconditionContainerArray.append({ + name: CommandPreConditions.Cooldown, + context: { scope, limit, delay, filteredUsers } + }); + } +} diff --git a/src/lib/precondition-resolvers/index.ts b/src/lib/precondition-resolvers/index.ts new file mode 100644 index 000000000..bbd307de6 --- /dev/null +++ b/src/lib/precondition-resolvers/index.ts @@ -0,0 +1,5 @@ +export * from './clientPermissions'; +export * from './cooldown'; +export * from './nsfw'; +export * from './runIn'; +export * from './userPermissions'; diff --git a/src/lib/precondition-resolvers/nsfw.ts b/src/lib/precondition-resolvers/nsfw.ts new file mode 100644 index 000000000..dc35b2efd --- /dev/null +++ b/src/lib/precondition-resolvers/nsfw.ts @@ -0,0 +1,11 @@ +import { CommandPreConditions } from '../types/Enums'; +import type { PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; + +/** + * Appends the `NSFW` precondition if {@link SubcommandMappingMethod.nsfw} is set to true. + * @param nsfw Whether this command is NSFW or not. + * @param preconditionContainerArray The precondition container array to append the precondition to. + */ +export function parseConstructorPreConditionsNsfw(nsfw: boolean | undefined, preconditionContainerArray: PreconditionContainerArray) { + if (nsfw) preconditionContainerArray.append(CommandPreConditions.NotSafeForWork); +} diff --git a/src/lib/precondition-resolvers/runIn.ts b/src/lib/precondition-resolvers/runIn.ts new file mode 100644 index 000000000..eaaee36a4 --- /dev/null +++ b/src/lib/precondition-resolvers/runIn.ts @@ -0,0 +1,46 @@ +import { isNullish } from '@sapphire/utilities'; +import type { ChannelType } from 'discord.js'; +import { Command } from '../structures/Command'; +import type { CommandRunInUnion, CommandSpecificRunIn } from '../types/CommandTypes'; +import { CommandPreConditions } from '../types/Enums'; +import type { PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; + +/** + * Appends the `RunIn` precondition based on the values passed, defaulting to `null`, which doesn't add a + * precondition. + * @param runIn The command's `runIn` option field from the constructor. + * @param resolveConstructorPreConditionsRunType The function to resolve the run type from the constructor. + * @param preconditionContainerArray The precondition container array to append the precondition to. + */ +export function parseConstructorPreConditionsRunIn( + runIn: CommandRunInUnion | CommandSpecificRunIn, + resolveConstructorPreConditionsRunType: (types: CommandRunInUnion) => readonly ChannelType[] | null, + preconditionContainerArray: PreconditionContainerArray +) { + // Early return if there's no runIn option: + if (isNullish(runIn)) return; + + if (Command.runInTypeIsSpecificsObject(runIn)) { + const messageRunTypes = resolveConstructorPreConditionsRunType(runIn.messageRun); + const chatInputRunTypes = resolveConstructorPreConditionsRunType(runIn.chatInputRun); + const contextMenuRunTypes = resolveConstructorPreConditionsRunType(runIn.contextMenuRun); + + if (messageRunTypes !== null || chatInputRunTypes !== null || contextMenuRunTypes !== null) { + preconditionContainerArray.append({ + name: CommandPreConditions.RunIn, + context: { + types: { + messageRun: messageRunTypes ?? [], + chatInputRun: chatInputRunTypes ?? [], + contextMenuRun: contextMenuRunTypes ?? [] + } + } + }); + } + } else { + const types = resolveConstructorPreConditionsRunType(runIn); + if (types !== null) { + preconditionContainerArray.append({ name: CommandPreConditions.RunIn, context: { types } }); + } + } +} diff --git a/src/lib/precondition-resolvers/userPermissions.ts b/src/lib/precondition-resolvers/userPermissions.ts new file mode 100644 index 000000000..6abbdfe3f --- /dev/null +++ b/src/lib/precondition-resolvers/userPermissions.ts @@ -0,0 +1,19 @@ +import { PermissionsBitField, type PermissionResolvable } from 'discord.js'; +import { CommandPreConditions } from '../types/Enums'; +import type { PreconditionContainerArray } from '../utils/preconditions/PreconditionContainerArray'; + +/** + * Appends the `UserPermissions` precondition when {@link Command.Options.requiredUserPermissions} resolves to a + * non-zero bitfield. + * @param requiredUserPermissions The required user permissions. + * @param preconditionContainerArray The precondition container array to append the precondition to. + */ +export function parseConstructorPreConditionsRequiredUserPermissions( + requiredUserPermissions: PermissionResolvable | undefined, + preconditionContainerArray: PreconditionContainerArray +) { + const permissions = new PermissionsBitField(requiredUserPermissions); + if (permissions.bitfield !== 0n) { + preconditionContainerArray.append({ name: CommandPreConditions.UserPermissions, context: { permissions } }); + } +} diff --git a/src/lib/structures/Command.ts b/src/lib/structures/Command.ts index ba03a46ea..0f51a500c 100644 --- a/src/lib/structures/Command.ts +++ b/src/lib/structures/Command.ts @@ -1,15 +1,15 @@ import { ArgumentStream, Lexer, Parser, type IUnorderedStrategy } from '@sapphire/lexure'; import { AliasPiece } from '@sapphire/pieces'; import { isNullish, isObject, type Awaitable } from '@sapphire/utilities'; -import { - ChannelType, - ChatInputCommandInteraction, - ContextMenuCommandInteraction, - PermissionsBitField, - type AutocompleteInteraction, - type Message -} from 'discord.js'; +import { ChannelType, ChatInputCommandInteraction, ContextMenuCommandInteraction, type AutocompleteInteraction, type Message } from 'discord.js'; import { Args } from '../parsers/Args'; +import { + parseConstructorPreConditionsCooldown, + parseConstructorPreConditionsNsfw, + parseConstructorPreConditionsRequiredClientPermissions, + parseConstructorPreConditionsRequiredUserPermissions, + parseConstructorPreConditionsRunIn +} from '../precondition-resolvers/index'; import type { AutocompleteCommand, ChatInputCommand, @@ -22,7 +22,7 @@ import type { DetailedDescriptionCommand, MessageCommand } from '../types/CommandTypes'; -import { BucketScope, CommandPreConditions, RegisterBehavior } from '../types/Enums'; +import { RegisterBehavior } from '../types/Enums'; import { acquire, getDefaultBehaviorWhenNotIdentical, handleBulkOverwrite } from '../utils/application-commands/ApplicationCommandRegistries'; import type { ApplicationCommandRegistry } from '../utils/application-commands/ApplicationCommandRegistry'; import { getNeededRegistryParameters } from '../utils/application-commands/getNeededParameters'; @@ -362,7 +362,7 @@ export class Command { + test('GIVEN valid permissions THEN appends to preconditionContainerArray', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsRequiredClientPermissions(PermissionFlagsBits.Administrator, preconditionContainerArray); + expect(preconditionContainerArray.entries.length).toBe(1); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe(CommandPreConditions.ClientPermissions); + expect( + ((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context as PermissionPreconditionContext).permissions?.has( + PermissionFlagsBits.Administrator + ) + ).toBe(true); + }); + + test('GIVEN no permissions THEN does not append to preconditionContainerArray', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsRequiredClientPermissions(undefined, preconditionContainerArray); + expect(preconditionContainerArray.entries.length).toBe(0); + }); +}); diff --git a/tests/precondition-resolvers/cooldown.test.ts b/tests/precondition-resolvers/cooldown.test.ts new file mode 100644 index 000000000..56245f31c --- /dev/null +++ b/tests/precondition-resolvers/cooldown.test.ts @@ -0,0 +1,76 @@ +import type { CooldownOptions } from '../../src/lib/SapphireClient'; +import { parseConstructorPreConditionsCooldown } from '../../src/lib/precondition-resolvers/cooldown'; +import { BucketScope } from '../../src/lib/types/Enums'; +import { PreconditionContainerArray } from '../../src/lib/utils/preconditions/PreconditionContainerArray'; +import type { PreconditionContainerSingle } from '../../src/lib/utils/preconditions/PreconditionContainerSingle'; + +describe('parseConstructorPreConditionsCooldown', () => { + vi.mock('@sapphire/pieces', async () => { + const mod = await vi.importActual('@sapphire/pieces'); + const { BucketScope } = await import('../../src/lib/types/Enums'); + + return { + ...mod, + container: { + client: { + options: { + defaultCooldown: { + limit: 1, + delay: 2, + scope: BucketScope.User, + filteredCommands: undefined, + filteredUsers: undefined + } as CooldownOptions + } + } + } + }; + }); + + afterAll(() => { + vi.restoreAllMocks(); + }); + + test('when limit and delay are undefined, sets limit to default limit and delay to default delay', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + + parseConstructorPreConditionsCooldown({ name: 'test' } as any, undefined, undefined, undefined, undefined, preconditionContainerArray); + + expect(preconditionContainerArray.entries.length).toBe(1); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe('Cooldown'); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context).toMatchObject({ + scope: BucketScope.User, + limit: 1, + delay: 2, + filteredUsers: undefined + }); + }); + + test('when limit and delay are defined, sets limit to passed limit and delay to passed delay', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsCooldown({ name: 'test' } as any, 5, 10, undefined, undefined, preconditionContainerArray); + + expect(preconditionContainerArray.entries.length).toBe(1); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe('Cooldown'); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context).toMatchObject({ + scope: BucketScope.User, + limit: 5, + delay: 10, + filteredUsers: undefined + }); + }); + + test('when scope, filteredUsers, limit, and delay are defined, sets all values to passed values', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsCooldown({ name: 'test' } as any, 5, 10, BucketScope.Guild, ['user1', 'user2'], preconditionContainerArray); + + expect(preconditionContainerArray.entries.length).toBe(1); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe('Cooldown'); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context).toMatchObject({ + scope: BucketScope.Guild, + limit: 5, + delay: 10, + filteredUsers: ['user1', 'user2'] + }); + }); +}); diff --git a/tests/precondition-resolvers/nsfw.test.ts b/tests/precondition-resolvers/nsfw.test.ts new file mode 100644 index 000000000..b75a135e3 --- /dev/null +++ b/tests/precondition-resolvers/nsfw.test.ts @@ -0,0 +1,24 @@ +import { parseConstructorPreConditionsNsfw } from '../../src/lib/precondition-resolvers/nsfw'; +import { CommandPreConditions } from '../../src/lib/types/Enums'; +import { PreconditionContainerArray } from '../../src/lib/utils/preconditions/PreconditionContainerArray'; + +describe('parseConstructorPreConditionsNsfw', () => { + test('GIVEN nsfw true THEN appends to preconditionContainerArray', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsNsfw(true, preconditionContainerArray); + expect(preconditionContainerArray.entries.length).toBe(1); + expect((preconditionContainerArray.entries[0] as any).name).toBe(CommandPreConditions.NotSafeForWork); + }); + + test('GIVEN nsfw false THEN does not append to preconditionContainerArray', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsNsfw(false, preconditionContainerArray); + expect(preconditionContainerArray.entries.length).toBe(0); + }); + + test('GIVEN nsfw undefined THEN does not append to preconditionContainerArray', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsNsfw(undefined, preconditionContainerArray); + expect(preconditionContainerArray.entries.length).toBe(0); + }); +}); diff --git a/tests/precondition-resolvers/runIn.test.ts b/tests/precondition-resolvers/runIn.test.ts new file mode 100644 index 000000000..842b9c60c --- /dev/null +++ b/tests/precondition-resolvers/runIn.test.ts @@ -0,0 +1,99 @@ +/* eslint-disable @typescript-eslint/dot-notation */ +import { isNullish } from '@sapphire/utilities'; +import { ChannelType } from 'discord.js'; +import { parseConstructorPreConditionsRunIn } from '../../src/lib/precondition-resolvers/runIn'; +import type { CommandRunInUnion } from '../../src/lib/types/CommandTypes'; +import { CommandPreConditions } from '../../src/lib/types/Enums'; +import { PreconditionContainerArray } from '../../src/lib/utils/preconditions/PreconditionContainerArray'; +import type { PreconditionContainerSingle } from '../../src/lib/utils/preconditions/PreconditionContainerSingle'; + +describe('parseConstructorPreConditionsRunIn', () => { + test('GIVEN runIn is null THEN returns undefined', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsRunIn(null, resolveConstructorPreConditionsRunType, preconditionContainerArray); + + expect(preconditionContainerArray.entries.length).toBe(0); + }); + + test('GIVEN runIn is a specific object THEN returns the correct types', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsRunIn( + { + messageRun: ChannelType.GuildText, + chatInputRun: ChannelType.DM, + contextMenuRun: ChannelType.GuildForum + }, + resolveConstructorPreConditionsRunType, + preconditionContainerArray + ); + expect(preconditionContainerArray.entries.length).toBe(1); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe(CommandPreConditions.RunIn); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context).toEqual({ + types: { + messageRun: [ChannelType.GuildText], + chatInputRun: [ChannelType.DM], + contextMenuRun: [ChannelType.GuildForum] + } + }); + }); + + test('GIVEN runIn is not a specific object THEN returns the correct types', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsRunIn(ChannelType.GuildVoice, resolveConstructorPreConditionsRunType, preconditionContainerArray); + expect(preconditionContainerArray.entries.length).toBe(1); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe(CommandPreConditions.RunIn); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context).toEqual({ + types: [ChannelType.GuildVoice] + }); + }); +}); + +// Copies of the same code in the Command class, extracted here for accessing them in tests without difficult mocking. +const ChannelTypes = Object.values(ChannelType).filter((type) => typeof type === 'number') as readonly ChannelType[]; +const GuildChannelTypes = ChannelTypes.filter((type) => type !== ChannelType.DM && type !== ChannelType.GroupDM) as readonly ChannelType[]; +function resolveConstructorPreConditionsRunType(types: CommandRunInUnion): readonly ChannelType[] | null { + if (isNullish(types)) return null; + if (typeof types === 'number') return [types]; + if (typeof types === 'string') { + switch (types) { + case 'DM': + return [ChannelType.DM]; + case 'GUILD_TEXT': + return [ChannelType.GuildText]; + case 'GUILD_VOICE': + return [ChannelType.GuildVoice]; + case 'GUILD_NEWS': + return [ChannelType.GuildAnnouncement]; + case 'GUILD_NEWS_THREAD': + return [ChannelType.AnnouncementThread]; + case 'GUILD_PUBLIC_THREAD': + return [ChannelType.PublicThread]; + case 'GUILD_PRIVATE_THREAD': + return [ChannelType.PrivateThread]; + case 'GUILD_ANY': + return GuildChannelTypes; + default: + return null; + } + } + + // If there's no channel it can run on, throw an error: + if (types.length === 0) { + throw new Error(`"runIn" was specified as an empty array.`); + } + + if (types.length === 1) { + return resolveConstructorPreConditionsRunType(types[0]); + } + + const resolved = new Set(); + for (const typeResolvable of types) { + for (const type of resolveConstructorPreConditionsRunType(typeResolvable) ?? []) resolved.add(type); + } + + // If all types were resolved, optimize to null: + if (resolved.size === ChannelTypes.length) return null; + + // Return the resolved types in ascending order: + return [...resolved].sort((a, b) => a - b); +} diff --git a/tests/precondition-resolvers/userPermissions.test.ts b/tests/precondition-resolvers/userPermissions.test.ts new file mode 100644 index 000000000..8c2207b9a --- /dev/null +++ b/tests/precondition-resolvers/userPermissions.test.ts @@ -0,0 +1,26 @@ +import { PermissionFlagsBits } from 'discord.js'; +import { parseConstructorPreConditionsRequiredUserPermissions } from '../../src/lib/precondition-resolvers/userPermissions'; +import { CommandPreConditions } from '../../src/lib/types/Enums'; +import { PreconditionContainerArray } from '../../src/lib/utils/preconditions/PreconditionContainerArray'; +import type { PreconditionContainerSingle } from '../../src/lib/utils/preconditions/PreconditionContainerSingle'; +import type { PermissionPreconditionContext } from '../../src/preconditions/ClientPermissions'; + +describe('parseConstructorPreConditionsRequiredUserPermissions', () => { + test('GIVEN valid permissions THEN appends to preconditionContainerArray', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsRequiredUserPermissions(PermissionFlagsBits.Administrator, preconditionContainerArray); + expect(preconditionContainerArray.entries.length).toBe(1); + expect((preconditionContainerArray.entries[0] as PreconditionContainerSingle).name).toBe(CommandPreConditions.UserPermissions); + expect( + ((preconditionContainerArray.entries[0] as PreconditionContainerSingle).context as PermissionPreconditionContext).permissions?.has( + PermissionFlagsBits.Administrator + ) + ).toBe(true); + }); + + test('GIVEN no permissions THEN does not append to preconditionContainerArray', () => { + const preconditionContainerArray = new PreconditionContainerArray(); + parseConstructorPreConditionsRequiredUserPermissions(undefined, preconditionContainerArray); + expect(preconditionContainerArray.entries.length).toBe(0); + }); +}); diff --git a/tests/resolvers/boolean.test.ts b/tests/resolvers/boolean.test.ts index a94c2d9c3..831eed704 100644 --- a/tests/resolvers/boolean.test.ts +++ b/tests/resolvers/boolean.test.ts @@ -1,28 +1,34 @@ -import { Identifiers, Resolvers, Result } from '../../src'; +import { Result } from '@sapphire/result'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveBoolean } from '../../src/lib/resolvers/boolean'; describe('Boolean resolver tests', () => { test('GIVEN a truthy value THEN returns true', () => { - expect(Resolvers.resolveBoolean('true')).toEqual(Result.ok(true)); - expect(Resolvers.resolveBoolean('1')).toEqual(Result.ok(true)); - expect(Resolvers.resolveBoolean('+')).toEqual(Result.ok(true)); - expect(Resolvers.resolveBoolean('yes')).toEqual(Result.ok(true)); + expect(resolveBoolean('true')).toEqual(Result.ok(true)); + expect(resolveBoolean('1')).toEqual(Result.ok(true)); + expect(resolveBoolean('+')).toEqual(Result.ok(true)); + expect(resolveBoolean('yes')).toEqual(Result.ok(true)); }); + test('GIVEN a falsy value THEN returns false', () => { - expect(Resolvers.resolveBoolean('false')).toEqual(Result.ok(false)); - expect(Resolvers.resolveBoolean('0')).toEqual(Result.ok(false)); - expect(Resolvers.resolveBoolean('-')).toEqual(Result.ok(false)); - expect(Resolvers.resolveBoolean('no')).toEqual(Result.ok(false)); + expect(resolveBoolean('false')).toEqual(Result.ok(false)); + expect(resolveBoolean('0')).toEqual(Result.ok(false)); + expect(resolveBoolean('-')).toEqual(Result.ok(false)); + expect(resolveBoolean('no')).toEqual(Result.ok(false)); }); + test('GIVEN a truthy value with custom ones THEN returns true', () => { - expect(Resolvers.resolveBoolean('yay', { truths: ['yay'] })).toEqual(Result.ok(true)); - expect(Resolvers.resolveBoolean('yup', { truths: ['yay', 'yup', 'yop'] })).toEqual(Result.ok(true)); + expect(resolveBoolean('yay', { truths: ['yay'] })).toEqual(Result.ok(true)); + expect(resolveBoolean('yup', { truths: ['yay', 'yup', 'yop'] })).toEqual(Result.ok(true)); }); + test('GIVEN a falsy value with custom ones THEN returns false', () => { - expect(Resolvers.resolveBoolean('nah', { falses: ['nah'] })).toEqual(Result.ok(false)); - expect(Resolvers.resolveBoolean('nope', { falses: ['nah', 'nope', 'noooo'] })).toEqual(Result.ok(false)); + expect(resolveBoolean('nah', { falses: ['nah'] })).toEqual(Result.ok(false)); + expect(resolveBoolean('nope', { falses: ['nah', 'nope', 'noooo'] })).toEqual(Result.ok(false)); }); + test('GIVEN an invalid values THEN returns error', () => { - expect(Resolvers.resolveBoolean('hello')).toEqual(Result.err(Identifiers.ArgumentBooleanError)); - expect(Resolvers.resolveBoolean('world', { truths: ['nah', 'nope', 'noooo'] })).toEqual(Result.err(Identifiers.ArgumentBooleanError)); + expect(resolveBoolean('hello')).toEqual(Result.err(Identifiers.ArgumentBooleanError)); + expect(resolveBoolean('world', { truths: ['nah', 'nope', 'noooo'] })).toEqual(Result.err(Identifiers.ArgumentBooleanError)); }); }); diff --git a/tests/resolvers/date.test.ts b/tests/resolvers/date.test.ts index 623a29dae..ecadadc58 100644 --- a/tests/resolvers/date.test.ts +++ b/tests/resolvers/date.test.ts @@ -1,4 +1,6 @@ -import { Identifiers, Resolvers, Result } from '../../src'; +import { Result } from '@sapphire/result'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveDate } from '../../src/lib/resolvers/date'; const DATE_2018_PLAIN_STRING = 'August 11, 2018 00:00:00'; const DATE_2018 = new Date(DATE_2018_PLAIN_STRING); @@ -14,21 +16,21 @@ const MAXIMUM = { maximum: new Date('August 11, 2021 00:00:00').getTime() }; describe('Date resolver tests', () => { test('GIVEN a valid date-time THEN returns the associated timestamp', () => { - expect(Resolvers.resolveDate(DATE_2020_PLAIN_STRING)).toEqual(Result.ok(DATE_2020)); + expect(resolveDate(DATE_2020_PLAIN_STRING)).toEqual(Result.ok(DATE_2020)); }); test('GIVEN a valid date-time with minimum THEN returns the associated timestamp', () => { - expect(Resolvers.resolveDate(DATE_2022_PLAIN_STRING, MINIMUM)).toEqual(Result.ok(DATE_2022)); + expect(resolveDate(DATE_2022_PLAIN_STRING, MINIMUM)).toEqual(Result.ok(DATE_2022)); }); test('GIVEN a valid date-time with maximum THEN returns the associated timestamp', () => { - expect(Resolvers.resolveDate(DATE_2018_PLAIN_STRING, MAXIMUM)).toEqual(Result.ok(DATE_2018)); + expect(resolveDate(DATE_2018_PLAIN_STRING, MAXIMUM)).toEqual(Result.ok(DATE_2018)); }); test('GIVEN a date-time before minimum THEN returns error', () => { - expect(Resolvers.resolveDate(DATE_2018_PLAIN_STRING, MINIMUM)).toEqual(Result.err(Identifiers.ArgumentDateTooEarly)); + expect(resolveDate(DATE_2018_PLAIN_STRING, MINIMUM)).toEqual(Result.err(Identifiers.ArgumentDateTooEarly)); }); test('GIVEN a date-time beyond maximum THEN returns error', () => { - expect(Resolvers.resolveDate(DATE_2022_PLAIN_STRING, MAXIMUM)).toEqual(Result.err(Identifiers.ArgumentDateTooFar)); + expect(resolveDate(DATE_2022_PLAIN_STRING, MAXIMUM)).toEqual(Result.err(Identifiers.ArgumentDateTooFar)); }); test('GIVEN an invalid date THEN returns error', () => { - expect(Resolvers.resolveDate('hello')).toEqual(Result.err(Identifiers.ArgumentDateError)); + expect(resolveDate('hello')).toEqual(Result.err(Identifiers.ArgumentDateError)); }); }); diff --git a/tests/resolvers/emoji.test.ts b/tests/resolvers/emoji.test.ts index a85d6eedc..c42de61d5 100644 --- a/tests/resolvers/emoji.test.ts +++ b/tests/resolvers/emoji.test.ts @@ -1,32 +1,34 @@ -import { Identifiers, Resolvers, Result } from '../../src'; +import { Result } from '@sapphire/result'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveEmoji } from '../../src/lib/resolvers/emoji'; describe('Emoji resolver tests', () => { test('GIVEN an unicode emoji THEN returns emojiObject', () => { - const resolvedEmoji = Resolvers.resolveEmoji('😄'); + const resolvedEmoji = resolveEmoji('😄'); expect(resolvedEmoji.isOk()).toBe(true); expect(resolvedEmoji.unwrapErr).toThrowError(); expect(resolvedEmoji.unwrap()).toMatchObject({ id: null, name: '😄' }); }); test('GIVEN a string emoji THEN returns ArgumentEmojiError', () => { - const resolvedEmoji = Resolvers.resolveEmoji(':smile:'); + const resolvedEmoji = resolveEmoji(':smile:'); expect(resolvedEmoji).toEqual(Result.err(Identifiers.ArgumentEmojiError)); }); test('GIVEN a string THEN returns ArgumentEmojiError', () => { - const resolvedEmoji = Resolvers.resolveEmoji('foo'); + const resolvedEmoji = resolveEmoji('foo'); expect(resolvedEmoji).toEqual(Result.err(Identifiers.ArgumentEmojiError)); }); test('GIVEN a wrongly formatted string custom emoji THEN returns ArgumentEmojiError', () => { - const resolvedEmoji = Resolvers.resolveEmoji(''); + const resolvedEmoji = resolveEmoji(''); expect(resolvedEmoji).toEqual(Result.err(Identifiers.ArgumentEmojiError)); }); test('GIVEN a string custom emoji THEN returns emojiObject', () => { - const resolvedEmoji = Resolvers.resolveEmoji('<:custom:737141877803057244>'); + const resolvedEmoji = resolveEmoji('<:custom:737141877803057244>'); expect(resolvedEmoji.isOk()).toBe(true); expect(resolvedEmoji.unwrapErr).toThrowError(); expect(resolvedEmoji.unwrap()).toMatchObject({ id: '737141877803057244', name: 'custom' }); }); test('GIVEN a string custom animated emoji THEN returns emojiObject', () => { - const resolvedEmoji = Resolvers.resolveEmoji(''); + const resolvedEmoji = resolveEmoji(''); expect(resolvedEmoji.isOk()).toBe(true); expect(resolvedEmoji.unwrapErr).toThrowError(); expect(resolvedEmoji.unwrap()).toMatchObject({ animated: true, id: '737141877803057244', name: 'custom' }); diff --git a/tests/resolvers/enum.test.ts b/tests/resolvers/enum.test.ts index f2ae78d02..cd599f7b3 100644 --- a/tests/resolvers/enum.test.ts +++ b/tests/resolvers/enum.test.ts @@ -1,36 +1,38 @@ -import { Identifiers, Resolvers, Result } from '../../src'; +import { Result } from '@sapphire/result'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveEnum } from '../../src/lib/resolvers/enum'; describe('Enum resolver tests', () => { test('GIVEN good lowercase enum from one option THEN returns string', () => { - const resolvedEnum = Resolvers.resolveEnum('foo', { enum: ['foo'] }); + const resolvedEnum = resolveEnum('foo', { enum: ['foo'] }); expect(resolvedEnum).toEqual(Result.ok('foo')); }); test('GIVEN good mixedcase enum from one option THEN returns string', () => { - const resolvedEnum = Resolvers.resolveEnum('FoO', { enum: ['FoO'] }); + const resolvedEnum = resolveEnum('FoO', { enum: ['FoO'] }); expect(resolvedEnum).toEqual(Result.ok('FoO')); }); test('GIVEN good enum from more options THEN returns string', () => { - const resolvedEnum = Resolvers.resolveEnum('foo', { enum: ['foo', 'bar', 'baz'] }); + const resolvedEnum = resolveEnum('foo', { enum: ['foo', 'bar', 'baz'] }); expect(resolvedEnum).toEqual(Result.ok('foo')); }); test('GIVEN good case insensitive enum from more options THEN returns string', () => { - const resolvedEnum = Resolvers.resolveEnum('FoO', { enum: ['FoO', 'foo', 'bar', 'baz'], caseInsensitive: false }); + const resolvedEnum = resolveEnum('FoO', { enum: ['FoO', 'foo', 'bar', 'baz'], caseInsensitive: false }); expect(resolvedEnum).toEqual(Result.ok('FoO')); }); test('GIVEN good enum from one option THEN returns ArgumentEnumError', () => { - const resolvedEnum = Resolvers.resolveEnum('foo', { enum: ['foo'] }); + const resolvedEnum = resolveEnum('foo', { enum: ['foo'] }); expect(resolvedEnum.isOk()).toBe(true); }); test('GIVEN an empty enum array THEN returns ArgumentEnumEmptyError', () => { - const resolvedEnum = Resolvers.resolveEnum('foo'); + const resolvedEnum = resolveEnum('foo'); expect(resolvedEnum).toEqual(Result.err(Identifiers.ArgumentEnumEmptyError)); }); test('GIVEN an enum not listed in the array THEN returns ArgumentEnumError', () => { - const resolvedEnum = Resolvers.resolveEnum('foo', { enum: ['bar', 'baz'] }); + const resolvedEnum = resolveEnum('foo', { enum: ['bar', 'baz'] }); expect(resolvedEnum).toEqual(Result.err(Identifiers.ArgumentEnumError)); }); test('GIVEN an enum with wrong case THEN returns ArgumentEnumError', () => { - const resolvedEnum = Resolvers.resolveEnum('FOO', { enum: ['bar', 'baz'], caseInsensitive: false }); + const resolvedEnum = resolveEnum('FOO', { enum: ['bar', 'baz'], caseInsensitive: false }); expect(resolvedEnum).toEqual(Result.err(Identifiers.ArgumentEnumError)); }); }); diff --git a/tests/resolvers/float.test.ts b/tests/resolvers/float.test.ts index 3127c7b24..7c3b571cc 100644 --- a/tests/resolvers/float.test.ts +++ b/tests/resolvers/float.test.ts @@ -1,22 +1,24 @@ -import { Identifiers, Resolvers, Result } from '../../src'; +import { Result } from '@sapphire/result'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveFloat } from '../../src/lib/resolvers/float'; describe('Float resolver tests', () => { test('GIVEN a valid float THEN returns its parsed value', () => { - expect(Resolvers.resolveFloat('1.23')).toEqual(Result.ok(1.23)); + expect(resolveFloat('1.23')).toEqual(Result.ok(1.23)); }); test('GIVEN a valid float with minimum THEN returns its parsed value', () => { - expect(Resolvers.resolveFloat('2.34', { minimum: 2 })).toEqual(Result.ok(2.34)); + expect(resolveFloat('2.34', { minimum: 2 })).toEqual(Result.ok(2.34)); }); test('GIVEN a valid float with maximum THEN returns its parsed value', () => { - expect(Resolvers.resolveFloat('3.45', { maximum: 4 })).toEqual(Result.ok(3.45)); + expect(resolveFloat('3.45', { maximum: 4 })).toEqual(Result.ok(3.45)); }); test('GIVEN a float before minimum THEN returns error', () => { - expect(Resolvers.resolveFloat('1.23', { minimum: 2 })).toEqual(Result.err(Identifiers.ArgumentFloatTooSmall)); + expect(resolveFloat('1.23', { minimum: 2 })).toEqual(Result.err(Identifiers.ArgumentFloatTooSmall)); }); test('GIVEN a float beyond maximum THEN returns error', () => { - expect(Resolvers.resolveFloat('4.56', { maximum: 4 })).toEqual(Result.err(Identifiers.ArgumentFloatTooLarge)); + expect(resolveFloat('4.56', { maximum: 4 })).toEqual(Result.err(Identifiers.ArgumentFloatTooLarge)); }); test('GIVEN an invalid float THEN returns error', () => { - expect(Resolvers.resolveFloat('hello')).toEqual(Result.err(Identifiers.ArgumentFloatError)); + expect(resolveFloat('hello')).toEqual(Result.err(Identifiers.ArgumentFloatError)); }); }); diff --git a/tests/resolvers/hyperlink.test.ts b/tests/resolvers/hyperlink.test.ts index 2c40ea4ff..3b4a82a2d 100644 --- a/tests/resolvers/hyperlink.test.ts +++ b/tests/resolvers/hyperlink.test.ts @@ -1,14 +1,16 @@ +import { Result } from '@sapphire/result'; import { URL } from 'node:url'; -import { Identifiers, Resolvers, Result } from '../../src'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveHyperlink } from '../../src/lib/resolvers/hyperlink'; const STRING_URL = 'https://github.com/sapphiredev'; const PARSED_URL = new URL(STRING_URL); describe('Hyperlink resolver tests', () => { test('GIVEN a valid hyperlink THEN returns its parsed value', () => { - expect(Resolvers.resolveHyperlink(STRING_URL)).toEqual(Result.ok(PARSED_URL)); + expect(resolveHyperlink(STRING_URL)).toEqual(Result.ok(PARSED_URL)); }); test('GIVEN an invalid hyperlink THEN returns error', () => { - expect(Resolvers.resolveHyperlink('hello')).toEqual(Result.err(Identifiers.ArgumentHyperlinkError)); + expect(resolveHyperlink('hello')).toEqual(Result.err(Identifiers.ArgumentHyperlinkError)); }); }); diff --git a/tests/resolvers/integer.test.ts b/tests/resolvers/integer.test.ts index e7f82cc80..f2ea8bb05 100644 --- a/tests/resolvers/integer.test.ts +++ b/tests/resolvers/integer.test.ts @@ -1,22 +1,24 @@ -import { Identifiers, Resolvers, Result } from '../../src'; +import { Result } from '@sapphire/result'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveInteger } from '../../src/lib/resolvers/integer'; describe('Integer resolver tests', () => { test('GIVEN a valid integer THEN returns its parsed value', () => { - expect(Resolvers.resolveInteger('1')).toEqual(Result.ok(1)); + expect(resolveInteger('1')).toEqual(Result.ok(1)); }); test('GIVEN a valid integer with minimum THEN returns its parsed value', () => { - expect(Resolvers.resolveInteger('2', { minimum: 2 })).toEqual(Result.ok(2)); + expect(resolveInteger('2', { minimum: 2 })).toEqual(Result.ok(2)); }); test('GIVEN a valid integer with maximum THEN returns its parsed value', () => { - expect(Resolvers.resolveInteger('3', { maximum: 4 })).toEqual(Result.ok(3)); + expect(resolveInteger('3', { maximum: 4 })).toEqual(Result.ok(3)); }); test('GIVEN a integer before minimum THEN returns error', () => { - expect(Resolvers.resolveInteger('1', { minimum: 2 })).toEqual(Result.err(Identifiers.ArgumentIntegerTooSmall)); + expect(resolveInteger('1', { minimum: 2 })).toEqual(Result.err(Identifiers.ArgumentIntegerTooSmall)); }); test('GIVEN a integer beyond maximum THEN returns error', () => { - expect(Resolvers.resolveInteger('5', { maximum: 4 })).toEqual(Result.err(Identifiers.ArgumentIntegerTooLarge)); + expect(resolveInteger('5', { maximum: 4 })).toEqual(Result.err(Identifiers.ArgumentIntegerTooLarge)); }); test('GIVEN an invalid integer THEN returns error', () => { - expect(Resolvers.resolveInteger('hello')).toEqual(Result.err(Identifiers.ArgumentIntegerError)); + expect(resolveInteger('hello')).toEqual(Result.err(Identifiers.ArgumentIntegerError)); }); }); diff --git a/tests/resolvers/number.test.ts b/tests/resolvers/number.test.ts index 7404424d5..8e601f9df 100644 --- a/tests/resolvers/number.test.ts +++ b/tests/resolvers/number.test.ts @@ -1,32 +1,34 @@ -import { Identifiers, Resolvers, Result } from '../../src'; +import { Result } from '@sapphire/result'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveNumber } from '../../src/lib/resolvers/number'; describe('Number resolver tests', () => { test('GIVEN a valid number THEN returns its parsed value', () => { - expect(Resolvers.resolveNumber('1.23')).toEqual(Result.ok(1.23)); + expect(resolveNumber('1.23')).toEqual(Result.ok(1.23)); - expect(Resolvers.resolveNumber('1')).toEqual(Result.ok(1)); + expect(resolveNumber('1')).toEqual(Result.ok(1)); }); test('GIVEN a valid number with minimum THEN returns its parsed value', () => { - expect(Resolvers.resolveNumber('2.34', { minimum: 2 })).toEqual(Result.ok(2.34)); + expect(resolveNumber('2.34', { minimum: 2 })).toEqual(Result.ok(2.34)); - expect(Resolvers.resolveNumber('2', { minimum: 2 })).toEqual(Result.ok(2)); + expect(resolveNumber('2', { minimum: 2 })).toEqual(Result.ok(2)); }); test('GIVEN a valid number with maximum THEN returns its parsed value', () => { - expect(Resolvers.resolveNumber('3.45', { maximum: 4 })).toEqual(Result.ok(3.45)); + expect(resolveNumber('3.45', { maximum: 4 })).toEqual(Result.ok(3.45)); - expect(Resolvers.resolveNumber('3', { maximum: 4 })).toEqual(Result.ok(3)); + expect(resolveNumber('3', { maximum: 4 })).toEqual(Result.ok(3)); }); test('GIVEN a number smaller than minimum THEN returns error', () => { - expect(Resolvers.resolveNumber('1.23', { minimum: 2 })).toEqual(Result.err(Identifiers.ArgumentNumberTooSmall)); + expect(resolveNumber('1.23', { minimum: 2 })).toEqual(Result.err(Identifiers.ArgumentNumberTooSmall)); - expect(Resolvers.resolveNumber('1', { minimum: 2 })).toEqual(Result.err(Identifiers.ArgumentNumberTooSmall)); + expect(resolveNumber('1', { minimum: 2 })).toEqual(Result.err(Identifiers.ArgumentNumberTooSmall)); }); test('GIVEN a number larger than maximum THEN returns error', () => { - expect(Resolvers.resolveNumber('4.56', { maximum: 4 })).toEqual(Result.err(Identifiers.ArgumentNumberTooLarge)); + expect(resolveNumber('4.56', { maximum: 4 })).toEqual(Result.err(Identifiers.ArgumentNumberTooLarge)); - expect(Resolvers.resolveNumber('5', { maximum: 4 })).toEqual(Result.err(Identifiers.ArgumentNumberTooLarge)); + expect(resolveNumber('5', { maximum: 4 })).toEqual(Result.err(Identifiers.ArgumentNumberTooLarge)); }); test('GIVEN an invalid number THEN returns error', () => { - expect(Resolvers.resolveNumber('hello')).toEqual(Result.err(Identifiers.ArgumentNumberError)); + expect(resolveNumber('hello')).toEqual(Result.err(Identifiers.ArgumentNumberError)); }); }); diff --git a/tests/resolvers/string.test.ts b/tests/resolvers/string.test.ts index 767d439c9..bf2e3082e 100644 --- a/tests/resolvers/string.test.ts +++ b/tests/resolvers/string.test.ts @@ -1,29 +1,31 @@ -import { Identifiers, Resolvers, Result } from '../../src'; +import { Result } from '@sapphire/result'; +import { Identifiers } from '../../src/lib/errors/Identifiers'; +import { resolveString } from '../../src/lib/resolvers/string'; describe('String resolver tests', () => { test('GIVEN a valid string THEN returns it', () => { - expect(Resolvers.resolveString('hello')).toEqual(Result.ok('hello')); + expect(resolveString('hello')).toEqual(Result.ok('hello')); - expect(Resolvers.resolveString('100')).toEqual(Result.ok('100')); + expect(resolveString('100')).toEqual(Result.ok('100')); }); test('GIVEN a valid string with minimum THEN returns it', () => { - expect(Resolvers.resolveString('hello', { minimum: 2 })).toEqual(Result.ok('hello')); + expect(resolveString('hello', { minimum: 2 })).toEqual(Result.ok('hello')); - expect(Resolvers.resolveString('100', { minimum: 2 })).toEqual(Result.ok('100')); + expect(resolveString('100', { minimum: 2 })).toEqual(Result.ok('100')); }); test('GIVEN a valid string with maximum THEN returns its parsed value', () => { - expect(Resolvers.resolveString('hello', { maximum: 10 })).toEqual(Result.ok('hello')); + expect(resolveString('hello', { maximum: 10 })).toEqual(Result.ok('hello')); - expect(Resolvers.resolveString('100', { maximum: 100 })).toEqual(Result.ok('100')); + expect(resolveString('100', { maximum: 100 })).toEqual(Result.ok('100')); }); test('GIVEN a string shorter than minimum THEN returns error', () => { - expect(Resolvers.resolveString('hello', { minimum: 10 })).toEqual(Result.err(Identifiers.ArgumentStringTooShort)); + expect(resolveString('hello', { minimum: 10 })).toEqual(Result.err(Identifiers.ArgumentStringTooShort)); - expect(Resolvers.resolveString('100', { minimum: 10 })).toEqual(Result.err(Identifiers.ArgumentStringTooShort)); + expect(resolveString('100', { minimum: 10 })).toEqual(Result.err(Identifiers.ArgumentStringTooShort)); }); test('GIVEN a string longer than maximum THEN returns error', () => { - expect(Resolvers.resolveString('hello', { maximum: 2 })).toEqual(Result.err(Identifiers.ArgumentStringTooLong)); + expect(resolveString('hello', { maximum: 2 })).toEqual(Result.err(Identifiers.ArgumentStringTooLong)); - expect(Resolvers.resolveString('100', { maximum: 2 })).toEqual(Result.err(Identifiers.ArgumentStringTooLong)); + expect(resolveString('100', { maximum: 2 })).toEqual(Result.err(Identifiers.ArgumentStringTooLong)); }); });