Files
discord.js/packages/structures/__tests__/Mixin.test.ts
Qjuh 3cff4d7412 feat: @discordjs/structures (#10900)
* chore: init /structures

* feat: base structure

* feat: initial structures design attempt

* refactor(Structure): use unknown to store in kData

* feat(Structure): add Invite

refactor(Structure): patch to _patch

* refactor: symbol names and override location

* fix: don't possibly return 0 if discord borks

Co-authored-by: Synbulat Biishev <signin@syjalo.dev>

* refactor: use getter value instead of api

Co-authored-by: Synbulat Biishev <signin@syjalo.dev>

* refactor: cache createdTimestamp value

Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com>

* docs: better docs for what's done so far

* feat: add Mixin

* refactor(User): remove bitfield getters and add displayName

* feat(structures): add Connection

* feat(structures): add Channel base

* refactor(Mixin): trace prototype chain, allow construction

* fix(structures): fix mixin behavior

* fix(structures): data optimization call behavior from perf testing

* feat: channel mixins

* chore: update deps

* feat: channels and mixins

* chore: more typeguard tests

* fix: tests and some other issues

* feat: add ChannelWebhookMixin

* fix: more tests

* chore: tests and docs

* chore: docs

* fix: remove unneccessary omitted

* chore: apply code suggestions

* refactor: change how extended invite works

* fix: type imports

* Apply suggestions from code review

Co-authored-by: Almeida <github@almeidx.dev>

* fix: tests

* chore: add jsdoc

* refactor: apply code suggestions

* fix: don't instantiate sub-structures

* fix: don't do null default twice

* chore: use formatters, add _cache

* chore: lockfile

* chore: move MixinTypes to declaratiion file

* fix: tests

* fix: don't include source d.ts files for docs

* feat: bitfields

* feat: more bitfields

* refactor: remove DirectoryChannel structure

* chore: apply suggestions from code review

* chore: remove unused import

* refactor: use symbol for mixin toJSON, remove _ prefix

* chore: apply suggestions from code review

* refactor: remove bitfield casts

* refactor: remove special case for threadchannel types

* fix: apply code review suggestions

* refactor: bitfields always store bigint

* fix: tests

* chore: apply suggestions from code review

* fix: lint

* refactor: conditional structuredClone

* Apply suggestions from code review

Co-authored-by: ckohen <chaikohen@gmail.com>

* fix: code review errors

* fix: lint

* chore: bump dtypes

* Update packages/structures/cliff.toml

Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>

* docs: link to VideoQualityMode

* chore: typo in comment

* chore: small nits in docs links

* chore: small nits

* docs: forgot one

* chore: update template

* chore: typos and things

* chore: apply suggestions from code review

* fix: tests and typeguards

* chore: don't clone appliedTags

* refactor: use a symbol for patch method

* fix: add missing readonly

* chore: remove todo comment

* refactor: use symbol for clone

* fix: add constraint to DataType

* chore: apply suggestions

* fix: dtypes bump

* chore: fix comment

* chore: add todo comment

* chore: mark bitfield as todo
chore: mark bit field as todo and edit readme

---------

Co-authored-by: ckohen <chaikohen@gmail.com>
Co-authored-by: Synbulat Biishev <signin@syjalo.dev>
Co-authored-by: Almeida <github@almeidx.dev>
Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
2025-07-12 18:24:30 +00:00

99 lines
4.2 KiB
TypeScript

import { describe, test, expect } from 'vitest';
import { kData, kPatch } from '../src/utils/symbols.js';
import type { APIData } from './mixinClasses.js';
import { Base, Mixed, MixedWithExtended } from './mixinClasses.js';
describe('Mixin function', () => {
const data: APIData = {
id: '1',
property1: 23,
};
test('Mixed class has all getters', () => {
const instance = new Mixed(data);
expect(instance.id).toBe(data.id);
expect(instance.property1).toBe(data.property1);
expect(instance.property2).toBe(data.property2);
});
test('Mixed class has all methods', () => {
const instance = new Mixed(data);
expect(instance.getId()).toBe(data.id);
expect(instance.getProperty1()).toBe(data.property1);
expect(instance.getProperty2()).toBe(data.property2);
expect(instance.getProperties()).toEqual({
property1: data.property1,
property2: data.property2,
});
});
test('Mixed with extended class has all getters', () => {
const instance = new MixedWithExtended(data);
expect(instance.id).toBe(data.id);
expect(instance.property1).toBe(data.property1);
expect(instance.property2).toBe(data.property2);
expect(instance.isExtended).toBe(true);
});
test('Mixed with extended class has all methods', () => {
const instance = new MixedWithExtended(data);
expect(instance.getId()).toBe(data.id);
expect(instance.getProperty1()).toBe(data.property1);
expect(instance.getProperty2()).toBe(data.property2);
expect(instance.getProperties()).toEqual({
property1: data.property1,
property2: data.property2,
});
});
test('Mixed class calls construct methods on construct', () => {
const instance1 = new Mixed(data);
const instance2 = new MixedWithExtended(data);
expect(instance1.constructCalled).toBe(true);
expect(instance2.constructCalled).toBe(true);
});
test('Mixed class respects mixin data optimizations', () => {
expect(typeof Object.getOwnPropertyDescriptor(Mixed.DataTemplate, 'mixinOptimize')?.set).toBe('function');
const missingOptimizedInstance = new Mixed(data);
const alreadyOptimizedInstance = new Mixed({ ...data, mixinOptimize: 'true', baseOptimize: 'true' });
const baseOptimizedInstance = new Base({ ...data, mixinOptimize: 'true', baseOptimize: 'true' });
expect(missingOptimizedInstance.baseOptimize).toBe(null);
expect(missingOptimizedInstance.mixinOptimize).toBe(null);
// Setters pass this
expect('baseOptimize' in missingOptimizedInstance[kData]).toBe(true);
expect('mixinOptimize' in missingOptimizedInstance[kData]).toBe(true);
expect(missingOptimizedInstance[kData].baseOptimize).toBeUndefined();
expect(missingOptimizedInstance[kData].mixinOptimize).toBeUndefined();
expect(alreadyOptimizedInstance.baseOptimize).toBe(true);
expect(alreadyOptimizedInstance.mixinOptimize).toBe(true);
// Setters pass this
expect('baseOptimize' in alreadyOptimizedInstance[kData]).toBe(true);
expect('mixinOptimize' in alreadyOptimizedInstance[kData]).toBe(true);
expect(alreadyOptimizedInstance[kData].baseOptimize).toBeUndefined();
expect(alreadyOptimizedInstance[kData].mixinOptimize).toBeUndefined();
expect(alreadyOptimizedInstance.toJSON()).toEqual({ ...data, mixinOptimize: 'true', baseOptimize: 'true' });
alreadyOptimizedInstance[kPatch]({ mixinOptimize: '', baseOptimize: '' });
expect(alreadyOptimizedInstance.baseOptimize).toBe(false);
expect(alreadyOptimizedInstance.mixinOptimize).toBe(false);
// Setters pass this
expect('baseOptimize' in alreadyOptimizedInstance[kData]).toBe(true);
expect('mixinOptimize' in alreadyOptimizedInstance[kData]).toBe(true);
expect(alreadyOptimizedInstance[kData].baseOptimize).toBeUndefined();
expect(alreadyOptimizedInstance[kData].mixinOptimize).toBeUndefined();
// Ensure mixin optimizations don't happen on base (ie overwritten DataTemplate)
expect(baseOptimizedInstance.baseOptimize).toBe(true);
expect('mixinOptimize' in baseOptimizedInstance).toBe(false);
// Setters pass this
expect('baseOptimize' in baseOptimizedInstance[kData]).toBe(true);
expect('mixinOptimize' in baseOptimizedInstance[kData]).toBe(true);
expect(baseOptimizedInstance[kData].baseOptimize).toBeUndefined();
expect(baseOptimizedInstance[kData].mixinOptimize).toBe('true');
});
});