seyfert/tests/mixer.test.mts
Marcos Susaña 7af24bf043
fix(mixer): rewrite mixins management (#333)
* fix: declared properties

* fix: idk mixer

* fix(mixer): tbh I really hate runtimes

* fix: test

* test: change constructor test
2025-02-26 20:36:56 -04:00

187 lines
3.9 KiB
TypeScript

// test written by claude 🩻
import { describe, expect, it } from 'vitest';
import { mix } from '../lib/deps/mixer';
describe('mix decorator', () => {
// Helper classes for testing
class BaseClass {
baseProperty = 'baseValue';
baseMethod() {
return 'base';
}
toString() {
return 'from base';
}
}
class MixinOne {
propertyOne = 1;
methodOne() {
return 'one';
}
toString() {
return 'from one';
}
}
class MixinTwo {
propertyTwo = 'two';
methodTwo() {
return 'two';
}
}
it('should correctly mix classes and preserve properties', () => {
@mix(MixinOne, MixinTwo)
class TestClass extends BaseClass {
testProperty = 'test';
testMethod() {
return 'test';
}
}
interface TestClass extends MixinOne, MixinTwo {}
const instance = new TestClass();
// Check properties
expect(instance.baseProperty).toBe('baseValue');
expect(instance.propertyOne).toBe(1);
expect(instance.propertyTwo).toBe('two');
expect(instance.testProperty).toBe('test');
// Check methods
expect(instance.baseMethod()).toBe('base');
expect(instance.methodOne()).toBe('one');
expect(instance.methodTwo()).toBe('two');
expect(instance.testMethod()).toBe('test');
expect(instance.toString()).toBe('from base');
});
it('should correctly mix classes and preserve the original class name', () => {
@mix(MixinOne, MixinTwo)
class TestClass extends BaseClass {
testMethod() {
return 'test';
}
}
interface TestClass extends MixinOne, MixinTwo {}
const instance = new TestClass();
// Check if the class name is preserved
expect(TestClass.name).toBe('TestClass');
// Check if methods from all classes are available
expect(instance.baseMethod()).toBe('base');
expect(instance.methodOne()).toBe('one');
expect(instance.methodTwo()).toBe('two');
expect(instance.testMethod()).toBe('test');
});
it('should handle mixing with no additional mixins', () => {
@mix()
class TestClass extends BaseClass {
testMethod() {
return 'test';
}
}
const instance = new TestClass();
expect(TestClass.name).toBe('TestClass');
expect(instance.baseMethod()).toBe('base');
expect(instance.testMethod()).toBe('test');
});
it('should handle mixing with getters and setters', () => {
class MixinWithAccessors {
private _value = '';
get value(): string {
return this._value;
}
set value(val: string) {
this._value = val;
}
}
interface TestClass extends MixinWithAccessors {}
@mix(MixinWithAccessors)
class TestClass extends BaseClass {}
const instance = new TestClass();
instance.value = 'test';
expect(instance.value).toBe('test');
});
it('should not override existing methods in the target class', () => {
class MixinWithConflict {
baseMethod() {
return 'mixin';
}
}
@mix(MixinWithConflict)
class TestClass extends BaseClass {
baseMethod() {
return 'override';
}
}
const instance = new TestClass();
expect(instance.baseMethod()).toBe('override');
});
it('should handle constructor arguments', () => {
class MixinWithConstructor {
name: string;
constructor(name: string) {
this.name = name.slice(0, 2);
}
getName() {
return this.name;
}
}
interface TestClass extends MixinWithConstructor {}
@mix(MixinWithConstructor)
class TestClass extends BaseClass {
constructor(name: string) {
super();
this.name = name;
}
name: string;
}
const instance = new TestClass('test');
expect(instance.getName()).toBe('test');
});
it('should handle multiple levels of inheritance', () => {
class Level1 {
level1() {
return 'level1';
}
}
class Level2 extends Level1 {
level2() {
return 'level2';
}
}
interface TestClass extends Level1, Level2 {}
@mix(Level2)
class TestClass extends BaseClass {}
const instance = new TestClass();
expect(instance.level1()).toBe('level1');
expect(instance.level2()).toBe('level2');
});
});