mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-18 20:43:30 +01:00
chore: Merge builders/1.x into v14 (#11260)
* chore: merge builders (and formatters) * chore: match cliff.toml * chore: update README.mds * build: discord-api-types 0.38.32
This commit is contained in:
@@ -2,7 +2,7 @@ import {
|
||||
ButtonStyle,
|
||||
ComponentType,
|
||||
type APIActionRowComponent,
|
||||
type APIMessageActionRowComponent,
|
||||
type APIComponentInMessageActionRow,
|
||||
} from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import {
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
StringSelectMenuOptionBuilder,
|
||||
} from '../../src/index.js';
|
||||
|
||||
const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
const rowWithButtonData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
@@ -25,7 +25,7 @@ const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
],
|
||||
};
|
||||
|
||||
const rowWithSelectMenuData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
const rowWithSelectMenuData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
@@ -57,7 +57,7 @@ describe('Action Row Components', () => {
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||
const actionRowData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
const actionRowData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
@@ -92,7 +92,7 @@ describe('Action Row Components', () => {
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||
const rowWithButtonData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
const rowWithButtonData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
@@ -104,7 +104,7 @@ describe('Action Row Components', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const rowWithSelectMenuData: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
const rowWithSelectMenuData: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
type: ComponentType.ActionRow,
|
||||
components: [
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
ComponentType,
|
||||
TextInputStyle,
|
||||
type APIButtonComponent,
|
||||
type APIMessageActionRowComponent,
|
||||
type APIComponentInMessageActionRow,
|
||||
type APISelectMenuComponent,
|
||||
type APITextInputComponent,
|
||||
type APIActionRowComponent,
|
||||
@@ -27,7 +27,7 @@ describe('createComponentBuilder', () => {
|
||||
);
|
||||
|
||||
test('GIVEN an action row component THEN returns a ActionRowBuilder', () => {
|
||||
const actionRow: APIActionRowComponent<APIMessageActionRowComponent> = {
|
||||
const actionRow: APIActionRowComponent<APIComponentInMessageActionRow> = {
|
||||
components: [],
|
||||
type: ComponentType.ActionRow,
|
||||
};
|
||||
|
||||
46
packages/builders/__tests__/components/fileUpload.test.ts
Normal file
46
packages/builders/__tests__/components/fileUpload.test.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type { APIFileUploadComponent } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { FileUploadBuilder } from '../../src/components/fileUpload/FileUpload.js';
|
||||
|
||||
describe('File Upload Components', () => {
|
||||
test('Valid builder does not throw.', () => {
|
||||
expect(() => new FileUploadBuilder().setCustomId('file_upload').toJSON()).not.toThrowError();
|
||||
expect(() => new FileUploadBuilder().setCustomId('file_upload').setId(5).toJSON()).not.toThrowError();
|
||||
|
||||
expect(() =>
|
||||
new FileUploadBuilder().setCustomId('file_upload').setMaxValues(5).setMinValues(2).toJSON(),
|
||||
).not.toThrowError();
|
||||
|
||||
expect(() => new FileUploadBuilder().setCustomId('file_upload').setRequired(false).toJSON()).not.toThrowError();
|
||||
});
|
||||
|
||||
test('Invalid builder does throw.', () => {
|
||||
expect(() => new FileUploadBuilder().toJSON()).toThrowError();
|
||||
expect(() => new FileUploadBuilder().setCustomId('file_upload').setId(-3).toJSON()).toThrowError();
|
||||
expect(() => new FileUploadBuilder().setMaxValues(5).setMinValues(2).setId(10).toJSON()).toThrowError();
|
||||
expect(() => new FileUploadBuilder().setCustomId('file_upload').setMaxValues(500).toJSON()).toThrowError();
|
||||
|
||||
expect(() =>
|
||||
new FileUploadBuilder().setCustomId('file_upload').setMinValues(500).setMaxValues(501).toJSON(),
|
||||
).toThrowError();
|
||||
|
||||
expect(() => new FileUploadBuilder().setRequired(false).toJSON()).toThrowError();
|
||||
});
|
||||
|
||||
test('API data equals toJSON().', () => {
|
||||
const fileUploadData = {
|
||||
type: ComponentType.FileUpload,
|
||||
custom_id: 'file_upload',
|
||||
min_values: 4,
|
||||
max_values: 9,
|
||||
required: false,
|
||||
} satisfies APIFileUploadComponent;
|
||||
|
||||
expect(new FileUploadBuilder(fileUploadData).toJSON()).toEqual(fileUploadData);
|
||||
|
||||
expect(
|
||||
new FileUploadBuilder().setCustomId('file_upload').setMinValues(4).setMaxValues(9).setRequired(false).toJSON(),
|
||||
).toEqual(fileUploadData);
|
||||
});
|
||||
});
|
||||
@@ -100,11 +100,11 @@ describe('Text Input Components', () => {
|
||||
.setPlaceholder('hello')
|
||||
.setStyle(TextInputStyle.Paragraph)
|
||||
.toJSON();
|
||||
}).toThrowError();
|
||||
}).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid input THEN valid JSON outputs are given', () => {
|
||||
const textInputData: APITextInputComponent = {
|
||||
const textInputData = {
|
||||
type: ComponentType.TextInput,
|
||||
label: 'label',
|
||||
custom_id: 'custom id',
|
||||
@@ -114,7 +114,7 @@ describe('Text Input Components', () => {
|
||||
value: 'value',
|
||||
required: false,
|
||||
style: TextInputStyle.Paragraph,
|
||||
};
|
||||
} satisfies APITextInputComponent;
|
||||
|
||||
expect(new TextInputBuilder(textInputData).toJSON()).toEqual(textInputData);
|
||||
expect(
|
||||
|
||||
248
packages/builders/__tests__/components/v2/container.test.ts
Normal file
248
packages/builders/__tests__/components/v2/container.test.ts
Normal file
@@ -0,0 +1,248 @@
|
||||
import { type APIContainerComponent, ComponentType, SeparatorSpacingSize } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { ActionRowBuilder } from '../../../src/components/ActionRow.js';
|
||||
import { createComponentBuilder } from '../../../src/components/Components.js';
|
||||
import { ButtonBuilder } from '../../../src/components/button/Button.js';
|
||||
import { ContainerBuilder } from '../../../src/components/v2/Container.js';
|
||||
import { FileBuilder } from '../../../src/components/v2/File.js';
|
||||
import { MediaGalleryBuilder } from '../../../src/components/v2/MediaGallery.js';
|
||||
import { SectionBuilder } from '../../../src/components/v2/Section.js';
|
||||
import { SeparatorBuilder } from '../../../src/components/v2/Separator.js';
|
||||
import { TextDisplayBuilder } from '../../../src/components/v2/TextDisplay.js';
|
||||
|
||||
const containerWithTextDisplay: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
id: 123,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const containerWithSeparatorData: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.Separator,
|
||||
id: 1_234,
|
||||
spacing: SeparatorSpacingSize.Small,
|
||||
divider: false,
|
||||
},
|
||||
],
|
||||
accent_color: 0x00ff00,
|
||||
};
|
||||
|
||||
const containerWithSeparatorDataNoColor: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.Separator,
|
||||
id: 1_234,
|
||||
spacing: SeparatorSpacingSize.Small,
|
||||
divider: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe('Container Components', () => {
|
||||
describe('Assertion Tests', () => {
|
||||
test('GIVEN valid components THEN do not throw', () => {
|
||||
expect(() =>
|
||||
new ContainerBuilder().addActionRowComponents(
|
||||
new ActionRowBuilder<ButtonBuilder>().addComponents(new ButtonBuilder()),
|
||||
),
|
||||
).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addFileComponents(new FileBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addMediaGalleryComponents(new MediaGalleryBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addSectionComponents(new SectionBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addSeparatorComponents(new SeparatorBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addTextDisplayComponents(new TextDisplayBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().spliceComponents(0, 0, new SeparatorBuilder())).not.toThrowError();
|
||||
expect(() => new ContainerBuilder().addSeparatorComponents([new SeparatorBuilder()])).not.toThrowError();
|
||||
expect(() =>
|
||||
new ContainerBuilder().spliceComponents(0, 0, [{ type: ComponentType.Separator }]),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||
const containerData: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
id: 3,
|
||||
},
|
||||
{
|
||||
type: ComponentType.Separator,
|
||||
spacing: SeparatorSpacingSize.Large,
|
||||
divider: true,
|
||||
id: 4,
|
||||
},
|
||||
{
|
||||
type: ComponentType.File,
|
||||
file: {
|
||||
url: 'attachment://file.png',
|
||||
},
|
||||
spoiler: false,
|
||||
},
|
||||
],
|
||||
accent_color: 0xff00ff,
|
||||
spoiler: true,
|
||||
};
|
||||
|
||||
expect(new ContainerBuilder(containerData).toJSON()).toEqual(containerData);
|
||||
expect(() => createComponentBuilder({ type: ComponentType.Container, components: [] })).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||
const containerWithTextDisplay: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
id: 123,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const containerWithSeparatorData: APIContainerComponent = {
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.Separator,
|
||||
id: 1_234,
|
||||
spacing: SeparatorSpacingSize.Small,
|
||||
divider: false,
|
||||
},
|
||||
],
|
||||
accent_color: 0x00ff00,
|
||||
};
|
||||
|
||||
expect(new ContainerBuilder(containerWithTextDisplay).toJSON()).toEqual(containerWithTextDisplay);
|
||||
expect(new ContainerBuilder(containerWithSeparatorData).toJSON()).toEqual(containerWithSeparatorData);
|
||||
expect(() => createComponentBuilder({ type: ComponentType.Container, components: [] })).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given 2', () => {
|
||||
const textDisplay = new TextDisplayBuilder().setContent('test').setId(123);
|
||||
const separator = new SeparatorBuilder().setId(1_234).setSpacing(SeparatorSpacingSize.Small).setDivider(false);
|
||||
|
||||
expect(new ContainerBuilder().addTextDisplayComponents(textDisplay).toJSON()).toEqual(containerWithTextDisplay);
|
||||
expect(new ContainerBuilder().addSeparatorComponents(separator).toJSON()).toEqual(
|
||||
containerWithSeparatorDataNoColor,
|
||||
);
|
||||
expect(new ContainerBuilder().addTextDisplayComponents([textDisplay]).toJSON()).toEqual(containerWithTextDisplay);
|
||||
expect(new ContainerBuilder().addSeparatorComponents([separator]).toJSON()).toEqual(
|
||||
containerWithSeparatorDataNoColor,
|
||||
);
|
||||
});
|
||||
|
||||
test('GIVEN valid accent color THEN valid JSON output is given', () => {
|
||||
expect(
|
||||
new ContainerBuilder({
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
})
|
||||
.setAccentColor([255, 0, 255])
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accent_color: 0xff00ff,
|
||||
});
|
||||
expect(
|
||||
new ContainerBuilder({
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
})
|
||||
.setAccentColor(0xff00ff)
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accent_color: 0xff00ff,
|
||||
});
|
||||
expect(
|
||||
new ContainerBuilder({
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
})
|
||||
.setAccentColor([255, 0, 255])
|
||||
.clearAccentColor()
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(new ContainerBuilder(containerWithSeparatorData).clearAccentColor().toJSON()).toEqual(
|
||||
containerWithSeparatorDataNoColor,
|
||||
);
|
||||
});
|
||||
|
||||
test('GIVEN valid method parameters THEN valid JSON is given', () => {
|
||||
expect(
|
||||
new ContainerBuilder()
|
||||
.addTextDisplayComponents(new TextDisplayBuilder().setId(3).clearId().setContent('test'))
|
||||
.setSpoiler()
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
spoiler: true,
|
||||
});
|
||||
expect(
|
||||
new ContainerBuilder()
|
||||
.addTextDisplayComponents({ type: ComponentType.TextDisplay, content: 'test' })
|
||||
.setSpoiler(false)
|
||||
.setId(5)
|
||||
.toJSON(),
|
||||
).toEqual({
|
||||
type: ComponentType.Container,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
spoiler: false,
|
||||
id: 5,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
44
packages/builders/__tests__/components/v2/file.test.ts
Normal file
44
packages/builders/__tests__/components/v2/file.test.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { FileBuilder } from '../../../src/components/v2/File';
|
||||
|
||||
const dummy = {
|
||||
type: ComponentType.File as const,
|
||||
file: { url: 'attachment://owo.png' },
|
||||
};
|
||||
|
||||
describe('File', () => {
|
||||
describe('File url', () => {
|
||||
test('GIVEN a file with a pre-defined url THEN return valid toJSON data', () => {
|
||||
const file = new FileBuilder({ file: { url: 'attachment://owo.png' } });
|
||||
expect(file.toJSON()).toEqual({ ...dummy, file: { url: 'attachment://owo.png' } });
|
||||
});
|
||||
|
||||
test('GIVEN a file using File#setURL THEN return valid toJSON data', () => {
|
||||
const file = new FileBuilder();
|
||||
file.setURL('attachment://uwu.png');
|
||||
|
||||
expect(file.toJSON()).toEqual({ ...dummy, file: { url: 'attachment://uwu.png' } });
|
||||
});
|
||||
|
||||
test('GIVEN a file with an invalid url THEN throws error', () => {
|
||||
const file = new FileBuilder();
|
||||
|
||||
expect(() => file.setURL('https://google.com')).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('File spoiler', () => {
|
||||
test('GIVEN a file with a pre-defined spoiler status THEN return valid toJSON data', () => {
|
||||
const file = new FileBuilder({ ...dummy, spoiler: true });
|
||||
expect(file.toJSON()).toEqual({ ...dummy, spoiler: true });
|
||||
});
|
||||
|
||||
test('GIVEN a file using File#setSpoiler THEN return valid toJSON data', () => {
|
||||
const file = new FileBuilder({ ...dummy });
|
||||
file.setSpoiler(false);
|
||||
|
||||
expect(file.toJSON()).toEqual({ ...dummy, spoiler: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
150
packages/builders/__tests__/components/v2/mediagallery.test.ts
Normal file
150
packages/builders/__tests__/components/v2/mediagallery.test.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
import { type APIMediaGalleryItem, type APIMediaGalleryComponent, ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { createComponentBuilder } from '../../../src/components/Components.js';
|
||||
import { MediaGalleryBuilder } from '../../../src/components/v2/MediaGallery.js';
|
||||
import { MediaGalleryItemBuilder } from '../../../src/components/v2/MediaGalleryItem.js';
|
||||
|
||||
const galleryHttpsDisplay: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
description: 'test',
|
||||
spoiler: false,
|
||||
media: { url: 'https://discord.com/logo.png' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const galleryAttachmentData: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
media: { url: 'attachment://file.png' },
|
||||
},
|
||||
],
|
||||
id: 123,
|
||||
};
|
||||
|
||||
describe('Media Gallery Components', () => {
|
||||
describe('Assertion Tests', () => {
|
||||
test('GIVEN an empty media gallery THEN throws error', () => {
|
||||
const gallery = new MediaGalleryBuilder();
|
||||
expect(() => gallery.toJSON()).toThrow();
|
||||
});
|
||||
|
||||
test('GIVEN valid items THEN do not throw', () => {
|
||||
expect(() => new MediaGalleryBuilder().addItems(new MediaGalleryItemBuilder())).not.toThrowError();
|
||||
expect(() => new MediaGalleryBuilder().spliceItems(0, 0, new MediaGalleryItemBuilder())).not.toThrowError();
|
||||
expect(() => new MediaGalleryBuilder().addItems([new MediaGalleryItemBuilder()])).not.toThrowError();
|
||||
expect(() => new MediaGalleryBuilder().spliceItems(0, 0, [new MediaGalleryItemBuilder()])).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||
const mediaGalleryData: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
media: { url: 'attachment://file.png' },
|
||||
description: 'test',
|
||||
spoiler: false,
|
||||
},
|
||||
{
|
||||
media: { url: 'https://discord.js.org/logo.jpg' },
|
||||
spoiler: true,
|
||||
},
|
||||
],
|
||||
id: 1_234,
|
||||
};
|
||||
|
||||
expect(new MediaGalleryBuilder(mediaGalleryData).toJSON()).toEqual(mediaGalleryData);
|
||||
expect(() => createComponentBuilder({ type: ComponentType.MediaGallery, items: [] })).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||
const galleryHttpsDisplay: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
description: 'test',
|
||||
spoiler: false,
|
||||
media: { url: 'https://discord.com/logo.png' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const galleryAttachmentData: APIMediaGalleryComponent = {
|
||||
type: ComponentType.MediaGallery,
|
||||
items: [
|
||||
{
|
||||
media: { url: 'attachment://file.png' },
|
||||
},
|
||||
],
|
||||
id: 123,
|
||||
};
|
||||
|
||||
expect(new MediaGalleryBuilder(galleryHttpsDisplay).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder(galleryAttachmentData).toJSON()).toEqual(galleryAttachmentData);
|
||||
expect(() => createComponentBuilder({ type: ComponentType.MediaGallery, items: [] })).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given 2', () => {
|
||||
const item1 = new MediaGalleryItemBuilder()
|
||||
.setDescription('test')
|
||||
.setSpoiler(false)
|
||||
.setURL('https://discord.com/logo.png');
|
||||
const item2 = new MediaGalleryItemBuilder().setURL('attachment://file.png');
|
||||
|
||||
expect(new MediaGalleryBuilder().addItems(item1).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder().addItems(item2).setId(123).toJSON()).toEqual(galleryAttachmentData);
|
||||
expect(new MediaGalleryBuilder().addItems([item1]).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder().addItems([item2]).setId(123).toJSON()).toEqual(galleryAttachmentData);
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON options THEN valid JSON output is given 2', () => {
|
||||
const item1: APIMediaGalleryItem = {
|
||||
description: 'test',
|
||||
spoiler: false,
|
||||
media: { url: 'https://discord.com/logo.png' },
|
||||
};
|
||||
const item2 = {
|
||||
media: { url: 'attachment://file.png' },
|
||||
};
|
||||
|
||||
expect(new MediaGalleryBuilder().addItems(item1).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder().addItems(item2).setId(123).toJSON()).toEqual(galleryAttachmentData);
|
||||
expect(new MediaGalleryBuilder().addItems([item1]).toJSON()).toEqual(galleryHttpsDisplay);
|
||||
expect(new MediaGalleryBuilder().addItems([item2]).setId(123).toJSON()).toEqual(galleryAttachmentData);
|
||||
});
|
||||
|
||||
test('GIVEN valid builder callback THEN valid JSON output is given', () => {
|
||||
const item1 = new MediaGalleryItemBuilder()
|
||||
.setDescription('test')
|
||||
.setSpoiler(false)
|
||||
.setURL('https://discord.com/logo.png');
|
||||
const item2 = new MediaGalleryItemBuilder().setURL('attachment://file.png');
|
||||
|
||||
expect(
|
||||
new MediaGalleryBuilder()
|
||||
.addItems((item) => item.setDescription('test').setSpoiler(false).setURL('https://discord.com/logo.png'))
|
||||
.toJSON(),
|
||||
).toEqual(galleryHttpsDisplay);
|
||||
expect(
|
||||
new MediaGalleryBuilder()
|
||||
.spliceItems(0, 0, (item) => item.setURL('attachment://file.png'))
|
||||
.setId(123)
|
||||
.toJSON(),
|
||||
).toEqual(galleryAttachmentData);
|
||||
expect(
|
||||
new MediaGalleryBuilder()
|
||||
.addItems([(item) => item.setDescription('test').setSpoiler(false).setURL('https://discord.com/logo.png')])
|
||||
.toJSON(),
|
||||
).toEqual(galleryHttpsDisplay);
|
||||
expect(
|
||||
new MediaGalleryBuilder()
|
||||
.spliceItems(0, 0, [(item) => item.setDescription('test').clearDescription().setURL('attachment://file.png')])
|
||||
.setId(123)
|
||||
.toJSON(),
|
||||
).toEqual(galleryAttachmentData);
|
||||
});
|
||||
});
|
||||
});
|
||||
191
packages/builders/__tests__/components/v2/section.test.ts
Normal file
191
packages/builders/__tests__/components/v2/section.test.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
import { type APISectionComponent, ButtonStyle, ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { createComponentBuilder } from '../../../src/components/Components.js';
|
||||
import { ButtonBuilder } from '../../../src/components/button/Button.js';
|
||||
import { SectionBuilder } from '../../../src/components/v2/Section.js';
|
||||
import { TextDisplayBuilder } from '../../../src/components/v2/TextDisplay.js';
|
||||
import { ThumbnailBuilder } from '../../../src/components/v2/Thumbnail.js';
|
||||
|
||||
const sectionWithButtonData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Button,
|
||||
label: 'test',
|
||||
custom_id: '123',
|
||||
style: ButtonStyle.Primary,
|
||||
},
|
||||
};
|
||||
|
||||
const sectionWithThumbnailData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Thumbnail,
|
||||
media: { url: 'attachment://file.png' },
|
||||
spoiler: true,
|
||||
description: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
describe('Section Components', () => {
|
||||
describe('Assertion Tests', () => {
|
||||
test('GIVEN valid components THEN do not throw', () => {
|
||||
expect(() => new SectionBuilder().addTextDisplayComponents(new TextDisplayBuilder())).not.toThrowError();
|
||||
expect(() => new SectionBuilder().spliceTextDisplayComponents(0, 0, new TextDisplayBuilder())).not.toThrowError();
|
||||
expect(() => new SectionBuilder().addTextDisplayComponents([new TextDisplayBuilder()])).not.toThrowError();
|
||||
expect(() =>
|
||||
new SectionBuilder().spliceTextDisplayComponents(0, 0, [new TextDisplayBuilder()]),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid JSON input THEN valid JSON output is given', () => {
|
||||
const sectionData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
id: 123,
|
||||
},
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Thumbnail,
|
||||
media: { url: 'attachment://file.png' },
|
||||
},
|
||||
};
|
||||
|
||||
expect(new SectionBuilder(sectionData).toJSON()).toEqual(sectionData);
|
||||
expect(() =>
|
||||
createComponentBuilder({
|
||||
type: ComponentType.Section,
|
||||
components: [],
|
||||
accessory: { type: ComponentType.Thumbnail, media: { url: 'https://discord.com/logo.png' } },
|
||||
}),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given', () => {
|
||||
const sectionWithButtonData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Button,
|
||||
label: 'test',
|
||||
custom_id: '123',
|
||||
style: ButtonStyle.Primary,
|
||||
},
|
||||
};
|
||||
|
||||
const sectionWithThumbnailData: APISectionComponent = {
|
||||
type: ComponentType.Section,
|
||||
components: [
|
||||
{
|
||||
type: ComponentType.TextDisplay,
|
||||
content: 'test',
|
||||
},
|
||||
],
|
||||
accessory: {
|
||||
type: ComponentType.Thumbnail,
|
||||
media: { url: 'attachment://file.png' },
|
||||
spoiler: true,
|
||||
description: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
expect(new SectionBuilder(sectionWithButtonData).toJSON()).toEqual(sectionWithButtonData);
|
||||
expect(new SectionBuilder(sectionWithThumbnailData).toJSON()).toEqual(sectionWithThumbnailData);
|
||||
expect(() =>
|
||||
createComponentBuilder({
|
||||
type: ComponentType.Section,
|
||||
components: [],
|
||||
accessory: {
|
||||
type: ComponentType.Button,
|
||||
label: 'test',
|
||||
custom_id: '123',
|
||||
style: ButtonStyle.Primary,
|
||||
},
|
||||
}),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('GIVEN valid builder options THEN valid JSON output is given 2', () => {
|
||||
const button = new ButtonBuilder().setLabel('test').setStyle(ButtonStyle.Primary).setCustomId('123');
|
||||
const thumbnail = new ThumbnailBuilder().setDescription('test').setSpoiler().setURL('attachment://file.png');
|
||||
const textDisplay = new TextDisplayBuilder().setContent('test');
|
||||
|
||||
expect(new SectionBuilder().addTextDisplayComponents(textDisplay).setButtonAccessory(button).toJSON()).toEqual(
|
||||
sectionWithButtonData,
|
||||
);
|
||||
expect(
|
||||
new SectionBuilder().addTextDisplayComponents(textDisplay).setThumbnailAccessory(thumbnail).toJSON(),
|
||||
).toEqual(sectionWithThumbnailData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents([textDisplay])
|
||||
.setButtonAccessory((button) => button.setLabel('test').setStyle(ButtonStyle.Primary).setCustomId('123'))
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents([textDisplay])
|
||||
.setThumbnailAccessory((thumbnail) =>
|
||||
thumbnail.setDescription('test').setSpoiler().setURL('attachment://file.png'),
|
||||
)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithThumbnailData);
|
||||
});
|
||||
|
||||
test('GIVEN valid builder callback THEN valid JSON output is given', () => {
|
||||
const button = new ButtonBuilder().setLabel('test').setStyle(ButtonStyle.Primary).setCustomId('123');
|
||||
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents((textDisplay) => textDisplay.setContent('test'))
|
||||
.setButtonAccessory(button)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.spliceTextDisplayComponents(0, 0, (textDisplay) => textDisplay.setContent('test'))
|
||||
.setButtonAccessory(button)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.addTextDisplayComponents([(textDisplay) => textDisplay.setContent('test')])
|
||||
.setButtonAccessory(button)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
expect(
|
||||
new SectionBuilder()
|
||||
.spliceTextDisplayComponents(0, 0, [(textDisplay) => textDisplay.setContent('test')])
|
||||
.setButtonAccessory(button)
|
||||
.toJSON(),
|
||||
).toEqual(sectionWithButtonData);
|
||||
});
|
||||
});
|
||||
});
|
||||
35
packages/builders/__tests__/components/v2/separator.test.ts
Normal file
35
packages/builders/__tests__/components/v2/separator.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { ComponentType, SeparatorSpacingSize } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { SeparatorBuilder } from '../../../src/components/v2/Separator';
|
||||
|
||||
describe('Separator', () => {
|
||||
describe('Divider', () => {
|
||||
test('GIVEN a separator with a pre-defined divider THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder({ divider: true });
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator, divider: true });
|
||||
});
|
||||
|
||||
test('GIVEN a separator with a set divider THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder().setDivider(false);
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator, divider: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('Spacing', () => {
|
||||
test('GIVEN a separator with a pre-defined spacing THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder({ spacing: SeparatorSpacingSize.Small });
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator, spacing: SeparatorSpacingSize.Small });
|
||||
});
|
||||
|
||||
test('GIVEN a separator with a set spacing THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder().setSpacing(SeparatorSpacingSize.Large);
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator, spacing: SeparatorSpacingSize.Large });
|
||||
});
|
||||
|
||||
test('GIVEN a separator with a set spacing THEN clear spacing THEN return valid toJSON data', () => {
|
||||
const separator = new SeparatorBuilder({ spacing: SeparatorSpacingSize.Small });
|
||||
separator.clearSpacing();
|
||||
expect(separator.toJSON()).toEqual({ type: ComponentType.Separator });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { TextDisplayBuilder } from '../../../src/components/v2/TextDisplay';
|
||||
|
||||
describe('TextDisplay', () => {
|
||||
describe('TextDisplay content', () => {
|
||||
test('GIVEN a text display with a pre-defined content THEN return valid toJSON data', () => {
|
||||
const textDisplay = new TextDisplayBuilder({ content: 'foo' });
|
||||
expect(textDisplay.toJSON()).toEqual({ type: ComponentType.TextDisplay, content: 'foo' });
|
||||
});
|
||||
|
||||
test('GIVEN a text display with a set content THEN return valid toJSON data', () => {
|
||||
const textDisplay = new TextDisplayBuilder().setContent('foo');
|
||||
expect(textDisplay.toJSON()).toEqual({ type: ComponentType.TextDisplay, content: 'foo' });
|
||||
});
|
||||
|
||||
test('GIVEN a text display with a pre-defined content THEN overwritten content THEN return valid toJSON data', () => {
|
||||
const textDisplay = new TextDisplayBuilder({ content: 'foo' });
|
||||
textDisplay.setContent('bar');
|
||||
expect(textDisplay.toJSON()).toEqual({ type: ComponentType.TextDisplay, content: 'bar' });
|
||||
});
|
||||
});
|
||||
});
|
||||
69
packages/builders/__tests__/components/v2/thumbnail.test.ts
Normal file
69
packages/builders/__tests__/components/v2/thumbnail.test.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { ThumbnailBuilder } from '../../../src/components/v2/Thumbnail';
|
||||
|
||||
const dummy = {
|
||||
type: ComponentType.Thumbnail as const,
|
||||
media: { url: 'https://google.com' },
|
||||
};
|
||||
|
||||
describe('Thumbnail', () => {
|
||||
describe('Thumbnail url', () => {
|
||||
test('GIVEN a thumbnail with a pre-defined url THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ media: { url: 'https://google.com' } });
|
||||
expect(thumbnail.toJSON()).toEqual({ type: ComponentType.Thumbnail, media: { url: 'https://google.com' } });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with a set url THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder().setURL('https://google.com');
|
||||
expect(thumbnail.toJSON()).toEqual({ type: ComponentType.Thumbnail, media: { url: 'https://google.com' } });
|
||||
});
|
||||
|
||||
test.each(['owo', 'discord://user'])('GIVEN a thumbnail with an invalid URL (%s) THEN throws error', (input) => {
|
||||
const thumbnail = new ThumbnailBuilder();
|
||||
|
||||
expect(() => thumbnail.setURL(input)).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Thumbnail description', () => {
|
||||
test('GIVEN a thumbnail with a pre-defined description THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ ...dummy, description: 'foo' });
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy, description: 'foo' });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with a set description THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ ...dummy });
|
||||
thumbnail.setDescription('foo');
|
||||
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy, description: 'foo' });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with a pre-defined description THEN unset description THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ description: 'foo', ...dummy });
|
||||
thumbnail.clearDescription();
|
||||
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with an invalid description THEN throws error', () => {
|
||||
const thumbnail = new ThumbnailBuilder();
|
||||
|
||||
expect(() => thumbnail.setDescription('a'.repeat(1_025))).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Thumbnail spoiler', () => {
|
||||
test('GIVEN a thumbnail with a pre-defined spoiler status THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ ...dummy, spoiler: true });
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy, spoiler: true });
|
||||
});
|
||||
|
||||
test('GIVEN a thumbnail with a set spoiler status THEN return valid toJSON data', () => {
|
||||
const thumbnail = new ThumbnailBuilder({ ...dummy });
|
||||
thumbnail.setSpoiler(false);
|
||||
|
||||
expect(thumbnail.toJSON()).toEqual({ ...dummy, spoiler: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -16,8 +16,8 @@ describe('Context Menu Commands', () => {
|
||||
// Too short of a name
|
||||
expect(() => ContextMenuCommandAssertions.validateName('')).toThrowError();
|
||||
|
||||
// Invalid characters used
|
||||
expect(() => ContextMenuCommandAssertions.validateName('ABC123$%^&')).toThrowError();
|
||||
// This should be fine, even with trailing and leading spaces (API trims it).
|
||||
expect(() => ContextMenuCommandAssertions.validateName(' 🩵 ABC 123 $%^& ')).not.toThrowError();
|
||||
|
||||
// Too long of a name
|
||||
expect(() =>
|
||||
@@ -60,8 +60,6 @@ describe('Context Menu Commands', () => {
|
||||
});
|
||||
|
||||
test('GIVEN invalid name THEN throw error', () => {
|
||||
expect(() => getBuilder().setName('$$$')).toThrowError();
|
||||
|
||||
expect(() => getBuilder().setName(' ')).toThrowError();
|
||||
});
|
||||
|
||||
@@ -166,7 +164,7 @@ describe('Context Menu Commands', () => {
|
||||
});
|
||||
|
||||
describe('integration types', () => {
|
||||
test('GIVEN a builder with valid integration types THEN does not throw an error', () => {
|
||||
test('GIVEN a builder with valid integraton types THEN does not throw an error', () => {
|
||||
expect(() =>
|
||||
getBuilder().setIntegrationTypes([
|
||||
ApplicationIntegrationType.GuildInstall,
|
||||
|
||||
@@ -565,7 +565,7 @@ describe('Slash Commands', () => {
|
||||
});
|
||||
|
||||
describe('integration types', () => {
|
||||
test('GIVEN a builder with valid integration types THEN does not throw an error', () => {
|
||||
test('GIVEN a builder with valid integraton types THEN does not throw an error', () => {
|
||||
expect(() =>
|
||||
getBuilder().setIntegrationTypes([
|
||||
ApplicationIntegrationType.GuildInstall,
|
||||
|
||||
@@ -324,12 +324,16 @@ describe('Embed', () => {
|
||||
test('GIVEN an embed using Embed#addFields THEN returns valid toJSON data', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
embed.addFields({ name: 'foo', value: 'bar' });
|
||||
embed.addFields([{ name: 'foo', value: 'bar' }]);
|
||||
embed.addFields([
|
||||
{ name: 'foo', value: 'bar' },
|
||||
{ name: '', value: '' },
|
||||
]);
|
||||
|
||||
expect(embed.toJSON()).toStrictEqual({
|
||||
fields: [
|
||||
{ name: 'foo', value: 'bar' },
|
||||
{ name: 'foo', value: 'bar' },
|
||||
{ name: '', value: '' },
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -381,38 +385,24 @@ describe('Embed', () => {
|
||||
expect(() => embed.setFields(Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' })))).toThrowError();
|
||||
});
|
||||
|
||||
describe('GIVEN invalid field amount THEN throws error', () => {
|
||||
test('1', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
test('GIVEN invalid field amount THEN throws error', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
|
||||
expect(() =>
|
||||
embed.addFields(...Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||
).toThrowError();
|
||||
});
|
||||
expect(() =>
|
||||
embed.addFields(...Array.from({ length: 26 }, () => ({ name: 'foo', value: 'bar' }))),
|
||||
).toThrowError();
|
||||
});
|
||||
|
||||
describe('GIVEN invalid field name THEN throws error', () => {
|
||||
test('2', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
test('GIVEN invalid field name length THEN throws error', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
|
||||
expect(() => embed.addFields({ name: '', value: 'bar' })).toThrowError();
|
||||
});
|
||||
expect(() => embed.addFields({ name: 'a'.repeat(257), value: 'bar' })).toThrowError();
|
||||
});
|
||||
|
||||
describe('GIVEN invalid field name length THEN throws error', () => {
|
||||
test('3', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
test('GIVEN invalid field value length THEN throws error', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
|
||||
expect(() => embed.addFields({ name: 'a'.repeat(257), value: 'bar' })).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('GIVEN invalid field value length THEN throws error', () => {
|
||||
test('4', () => {
|
||||
const embed = new EmbedBuilder();
|
||||
|
||||
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1_025) })).toThrowError();
|
||||
});
|
||||
expect(() => embed.addFields({ name: '', value: 'a'.repeat(1_025) })).toThrowError();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user