Skip to content

Commit

Permalink
refactor: builders
Browse files Browse the repository at this point in the history
fix: main typings

chore: bump @types/node

refactor: embed builder

refactor: app command builders

feat: complete slash commands

refactor: use zod in embeds

chore: remove clone

feat: modals and context menus

chore: context menu tests

feat: remaining builders

chore: cleanup deps

chore: nits

chore: typo

fix: modal assertion

chore: re-order

refactor: perms validation

chore: comment explicit false validation override

refactor: getters

chore: update tests

chore: update confusing comment

fix: remove proxy url

refactor: re-introduce generic to choice mixin

refactor: some builder types

Co-authored-by: Vlad Frangu <kingdgrizzle@gmail.com>
  • Loading branch information
didinele and vladfrangu committed Sep 6, 2024
1 parent e2e71b4 commit 91e1a5d
Show file tree
Hide file tree
Showing 59 changed files with 2,302 additions and 2,609 deletions.
28 changes: 7 additions & 21 deletions packages/builders/__tests__/components/actionRow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ const rowWithSelectMenuData: APIActionRowComponent<APIMessageActionRowComponent>
describe('Action Row Components', () => {
describe('Assertion Tests', () => {
test('GIVEN valid components THEN do not throw', () => {
expect(() => new ActionRowBuilder().addComponents(new ButtonBuilder())).not.toThrowError();
expect(() => new ActionRowBuilder().setComponents(new ButtonBuilder())).not.toThrowError();
expect(() => new ActionRowBuilder().addComponents([new ButtonBuilder()])).not.toThrowError();
expect(() => new ActionRowBuilder().setComponents([new ButtonBuilder()])).not.toThrowError();
expect(() =>
new ActionRowBuilder().addButtonComponents(new ButtonBuilder(), new ButtonBuilder()),
).not.toThrowError();
expect(() => new ActionRowBuilder().addButtonComponents([new ButtonBuilder()])).not.toThrowError();
});

test('GIVEN valid JSON input THEN valid JSON output is given', () => {
Expand All @@ -72,22 +72,10 @@ describe('Action Row Components', () => {
style: ButtonStyle.Link,
url: 'https://google.com',
},
{
type: ComponentType.StringSelect,
placeholder: 'test',
custom_id: 'test',
options: [
{
label: 'option',
value: 'option',
},
],
},
],
};

expect(new ActionRowBuilder(actionRowData).toJSON()).toEqual(actionRowData);
expect(new ActionRowBuilder().toJSON()).toEqual({ type: ComponentType.ActionRow, components: [] });
expect(() => createComponentBuilder({ type: ComponentType.ActionRow, components: [] })).not.toThrowError();
});

Expand Down Expand Up @@ -128,7 +116,6 @@ describe('Action Row Components', () => {

expect(new ActionRowBuilder(rowWithButtonData).toJSON()).toEqual(rowWithButtonData);
expect(new ActionRowBuilder(rowWithSelectMenuData).toJSON()).toEqual(rowWithSelectMenuData);
expect(new ActionRowBuilder().toJSON()).toEqual({ type: ComponentType.ActionRow, components: [] });
expect(() => createComponentBuilder({ type: ComponentType.ActionRow, components: [] })).not.toThrowError();
});

Expand All @@ -147,10 +134,9 @@ describe('Action Row Components', () => {
new StringSelectMenuOptionBuilder().setLabel('two').setValue('two'),
]);

expect(new ActionRowBuilder().addComponents(button).toJSON()).toEqual(rowWithButtonData);
expect(new ActionRowBuilder().addComponents(selectMenu).toJSON()).toEqual(rowWithSelectMenuData);
expect(new ActionRowBuilder().addComponents([button]).toJSON()).toEqual(rowWithButtonData);
expect(new ActionRowBuilder().addComponents([selectMenu]).toJSON()).toEqual(rowWithSelectMenuData);
expect(new ActionRowBuilder().addButtonComponents(button).toJSON()).toEqual(rowWithButtonData);
expect(new ActionRowBuilder().addStringSelectMenuComponent(selectMenu).toJSON()).toEqual(rowWithSelectMenuData);
expect(new ActionRowBuilder().addButtonComponents([button]).toJSON()).toEqual(rowWithButtonData);
});
});
});
34 changes: 7 additions & 27 deletions packages/builders/__tests__/components/button.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
type APIButtonComponentWithURL,
} from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import { buttonLabelValidator, buttonStyleValidator } from '../../src/components/Assertions.js';
import { ButtonBuilder } from '../../src/components/button/Button.js';

const buttonComponent = () => new ButtonBuilder();
Expand All @@ -15,26 +14,6 @@ const longStr =

describe('Button Components', () => {
describe('Assertion Tests', () => {
test('GIVEN valid label THEN validator does not throw', () => {
expect(() => buttonLabelValidator.parse('foobar')).not.toThrowError();
});

test('GIVEN invalid label THEN validator does throw', () => {
expect(() => buttonLabelValidator.parse(null)).toThrowError();
expect(() => buttonLabelValidator.parse('')).toThrowError();

expect(() => buttonLabelValidator.parse(longStr)).toThrowError();
});

test('GIVEN valid style THEN validator does not throw', () => {
expect(() => buttonStyleValidator.parse(3)).not.toThrowError();
expect(() => buttonStyleValidator.parse(ButtonStyle.Secondary)).not.toThrowError();
});

test('GIVEN invalid style THEN validator does throw', () => {
expect(() => buttonStyleValidator.parse(7)).toThrowError();
});

test('GIVEN valid fields THEN builder does not throw', () => {
expect(() =>
buttonComponent().setCustomId('custom').setStyle(ButtonStyle.Primary).setLabel('test'),
Expand All @@ -43,6 +22,7 @@ describe('Button Components', () => {
expect(() => {
const button = buttonComponent()
.setCustomId('custom')
.setLabel('test')
.setStyle(ButtonStyle.Primary)
.setDisabled(true)
.setEmoji({ name: 'test' });
Expand All @@ -60,7 +40,7 @@ describe('Button Components', () => {

test('GIVEN invalid fields THEN build does throw', () => {
expect(() => {
buttonComponent().setCustomId(longStr);
buttonComponent().setCustomId(longStr).toJSON();
}).toThrowError();

expect(() => {
Expand Down Expand Up @@ -148,14 +128,14 @@ describe('Button Components', () => {
}).toThrowError();

// @ts-expect-error: Invalid style
expect(() => buttonComponent().setStyle(24)).toThrowError();
expect(() => buttonComponent().setLabel(longStr)).toThrowError();
expect(() => buttonComponent().setCustomId('hi').setStyle(24).toJSON()).toThrowError();
expect(() => buttonComponent().setCustomId('hi').setLabel(longStr).toJSON()).toThrowError();
// @ts-expect-error: Invalid parameter for disabled
expect(() => buttonComponent().setDisabled(0)).toThrowError();
expect(() => buttonComponent().setCustomId('hi').setDisabled(0).toJSON()).toThrowError();
// @ts-expect-error: Invalid emoji
expect(() => buttonComponent().setEmoji('foo')).toThrowError();
expect(() => buttonComponent().setCustomId('hi').setEmoji('foo').toJSON()).toThrowError();

expect(() => buttonComponent().setURL('foobar')).toThrowError();
expect(() => buttonComponent().setCustomId('hi').setURL('foobar').toJSON()).toThrowError();
});

test('GiVEN valid input THEN valid JSON outputs are given', () => {
Expand Down
111 changes: 75 additions & 36 deletions packages/builders/__tests__/components/selectMenu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { describe, test, expect } from 'vitest';
import { StringSelectMenuBuilder, StringSelectMenuOptionBuilder } from '../../src/index.js';

const selectMenu = () => new StringSelectMenuBuilder();
const selectMenuWithId = () => new StringSelectMenuBuilder({ custom_id: 'hi' });
const selectMenuOption = () => new StringSelectMenuOptionBuilder();

const longStr = 'a'.repeat(256);
Expand All @@ -16,7 +17,7 @@ const selectMenuOptionData: APISelectMenuOption = {
};

const selectMenuDataWithoutOptions = {
type: ComponentType.SelectMenu,
type: ComponentType.StringSelect,
custom_id: 'test',
max_values: 10,
min_values: 3,
Expand Down Expand Up @@ -109,49 +110,87 @@ describe('Select Menu Components', () => {
});

test('GIVEN invalid inputs THEN Select Menu does throw', () => {
expect(() => selectMenu().setCustomId(longStr)).toThrowError();
expect(() => selectMenu().setMaxValues(30)).toThrowError();
expect(() => selectMenu().setMinValues(-20)).toThrowError();
expect(() => selectMenu().setCustomId(longStr).toJSON()).toThrowError();
expect(() => selectMenuWithId().setMaxValues(30).toJSON()).toThrowError();
expect(() => selectMenuWithId().setMinValues(-20).toJSON()).toThrowError();
// @ts-expect-error: Invalid disabled value
expect(() => selectMenu().setDisabled(0)).toThrowError();
expect(() => selectMenu().setPlaceholder(longStr)).toThrowError();
expect(() => selectMenuWithId().setDisabled(0).toJSON()).toThrowError();
expect(() => selectMenuWithId().setPlaceholder(longStr).toJSON()).toThrowError();
// @ts-expect-error: Invalid option
expect(() => selectMenu().addOptions({ label: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ label: longStr, value: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ value: longStr, label: 'test' })).toThrowError();
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', description: longStr })).toThrowError();
// @ts-expect-error: Invalid option
expect(() => selectMenu().addOptions({ label: 'test', value: 'test', default: 100 })).toThrowError();
// @ts-expect-error: Invalid option
expect(() => selectMenu().addOptions({ value: 'test' })).toThrowError();
// @ts-expect-error: Invalid option
expect(() => selectMenu().addOptions({ default: true })).toThrowError();
// @ts-expect-error: Invalid option
expect(() => selectMenu().addOptions([{ label: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ label: longStr, value: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ value: longStr, label: 'test' }])).toThrowError();
expect(() => selectMenu().addOptions([{ label: 'test', value: 'test', description: longStr }])).toThrowError();
// @ts-expect-error: Invalid option
expect(() => selectMenu().addOptions([{ label: 'test', value: 'test', default: 100 }])).toThrowError();
expect(() => selectMenuWithId().addOptions({ label: 'test' }).toJSON()).toThrowError();
expect(() => selectMenuWithId().addOptions({ label: longStr, value: 'test' }).toJSON()).toThrowError();
expect(() => selectMenuWithId().addOptions({ value: longStr, label: 'test' }).toJSON()).toThrowError();
expect(() =>
selectMenuWithId().addOptions({ label: 'test', value: 'test', description: longStr }).toJSON(),
).toThrowError();
expect(() =>
// @ts-expect-error: Invalid option
selectMenuWithId().addOptions({ label: 'test', value: 'test', default: 100 }).toJSON(),
).toThrowError();
// @ts-expect-error: Invalid option
expect(() => selectMenu().addOptions([{ value: 'test' }])).toThrowError();
expect(() => selectMenuWithId().addOptions({ value: 'test' }).toJSON()).toThrowError();
// @ts-expect-error: Invalid option
expect(() => selectMenu().addOptions([{ default: true }])).toThrowError();
expect(() => selectMenuWithId().addOptions({ default: true }).toJSON()).toThrowError();
expect(() =>
selectMenuWithId()
// @ts-expect-error: Invalid option
.addOptions([{ label: 'test' }])
.toJSON(),
).toThrowError();
expect(() =>
selectMenuWithId()
.addOptions([{ label: longStr, value: 'test' }])
.toJSON(),
).toThrowError();
expect(() =>
selectMenuWithId()
.addOptions([{ value: longStr, label: 'test' }])
.toJSON(),
).toThrowError();
expect(() =>
selectMenuWithId()
.addOptions([{ label: 'test', value: 'test', description: longStr }])
.toJSON(),
).toThrowError();
expect(() =>
selectMenuWithId()
// @ts-expect-error: Invalid option
.addOptions([{ label: 'test', value: 'test', default: 100 }])
.toJSON(),
).toThrowError();
expect(() =>
selectMenuWithId()
// @ts-expect-error: Invalid option
.addOptions([{ value: 'test' }])
.toJSON(),
).toThrowError();
expect(() =>
selectMenuWithId()
// @ts-expect-error: Invalid option
.addOptions([{ default: true }])
.toJSON(),
).toThrowError();

const tooManyOptions = Array.from<APISelectMenuOption>({ length: 26 }).fill({ label: 'test', value: 'test' });

expect(() => selectMenu().setOptions(...tooManyOptions)).toThrowError();
expect(() => selectMenu().setOptions(tooManyOptions)).toThrowError();
expect(() =>
selectMenu()
.setOptions(...tooManyOptions)
.toJSON(),
).toThrowError();
expect(() => selectMenu().setOptions(tooManyOptions).toJSON()).toThrowError();

expect(() =>
selectMenu()
.addOptions({ label: 'test', value: 'test' })
.addOptions(...tooManyOptions),
.addOptions(...tooManyOptions)
.toJSON(),
).toThrowError();
expect(() =>
selectMenu()
.addOptions([{ label: 'test', value: 'test' }])
.addOptions(tooManyOptions),
.addOptions(tooManyOptions)
.toJSON(),
).toThrowError();

expect(() => {
Expand All @@ -162,7 +201,8 @@ describe('Select Menu Components', () => {
.setDefault(-1)
// @ts-expect-error: Invalid emoji
.setEmoji({ name: 1 })
.setDescription(longStr);
.setDescription(longStr)
.toJSON();
}).toThrowError();
});

Expand Down Expand Up @@ -212,17 +252,16 @@ describe('Select Menu Components', () => {
).toStrictEqual([selectMenuOptionData]);

expect(() =>
makeStringSelectMenuWithOptions().spliceOptions(
0,
0,
...Array.from({ length: 26 }, () => selectMenuOptionData),
),
makeStringSelectMenuWithOptions()
.spliceOptions(0, 0, ...Array.from({ length: 26 }, () => selectMenuOptionData))
.toJSON(),
).toThrowError();

expect(() =>
makeStringSelectMenuWithOptions()
.setOptions(Array.from({ length: 25 }, () => selectMenuOptionData))
.spliceOptions(-1, 2, selectMenuOptionData, selectMenuOptionData),
.spliceOptions(-1, 2, selectMenuOptionData, selectMenuOptionData)
.toJSON(),
).toThrowError();
});
});
Expand Down
62 changes: 1 addition & 61 deletions packages/builders/__tests__/components/textInput.test.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import { ComponentType, TextInputStyle, type APITextInputComponent } from 'discord-api-types/v10';
import { describe, test, expect } from 'vitest';
import {
labelValidator,
maxLengthValidator,
minLengthValidator,
placeholderValidator,
valueValidator,
textInputStyleValidator,
} from '../../src/components/textInput/Assertions.js';
import { TextInputBuilder } from '../../src/components/textInput/TextInput.js';

const superLongStr = 'a'.repeat(5_000);
Expand All @@ -16,56 +8,6 @@ const textInputComponent = () => new TextInputBuilder();

describe('Text Input Components', () => {
describe('Assertion Tests', () => {
test('GIVEN valid label THEN validator does not throw', () => {
expect(() => labelValidator.parse('foobar')).not.toThrowError();
});

test('GIVEN invalid label THEN validator does throw', () => {
expect(() => labelValidator.parse(24)).toThrowError();
expect(() => labelValidator.parse(undefined)).toThrowError();
});

test('GIVEN valid style THEN validator does not throw', () => {
expect(() => textInputStyleValidator.parse(TextInputStyle.Paragraph)).not.toThrowError();
expect(() => textInputStyleValidator.parse(TextInputStyle.Short)).not.toThrowError();
});

test('GIVEN invalid style THEN validator does throw', () => {
expect(() => textInputStyleValidator.parse(24)).toThrowError();
});

test('GIVEN valid min length THEN validator does not throw', () => {
expect(() => minLengthValidator.parse(10)).not.toThrowError();
});

test('GIVEN invalid min length THEN validator does throw', () => {
expect(() => minLengthValidator.parse(-1)).toThrowError();
});

test('GIVEN valid max length THEN validator does not throw', () => {
expect(() => maxLengthValidator.parse(10)).not.toThrowError();
});

test('GIVEN invalid min length THEN validator does throw 2', () => {
expect(() => maxLengthValidator.parse(4_001)).toThrowError();
});

test('GIVEN valid value THEN validator does not throw', () => {
expect(() => valueValidator.parse('foobar')).not.toThrowError();
});

test('GIVEN invalid value THEN validator does throw', () => {
expect(() => valueValidator.parse(superLongStr)).toThrowError();
});

test('GIVEN valid placeholder THEN validator does not throw', () => {
expect(() => placeholderValidator.parse('foobar')).not.toThrowError();
});

test('GIVEN invalid value THEN validator does throw 2', () => {
expect(() => placeholderValidator.parse(superLongStr)).toThrowError();
});

test('GIVEN valid fields THEN builder does not throw', () => {
expect(() => {
textInputComponent().setCustomId('foobar').setLabel('test').setStyle(TextInputStyle.Paragraph).toJSON();
Expand All @@ -84,9 +26,7 @@ describe('Text Input Components', () => {
}).not.toThrowError();

expect(() => {
// Issue #8107
// @ts-expect-error: Shapeshift maps the enum key to the value when parsing
textInputComponent().setCustomId('Custom').setLabel('Guess').setStyle('Short').toJSON();
textInputComponent().setCustomId('Custom').setLabel('Guess').setStyle(TextInputStyle.Short).toJSON();
}).not.toThrowError();
});
});
Expand Down
Loading

0 comments on commit 91e1a5d

Please sign in to comment.