Mixins.spec.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { describe, it, expect } from "vitest";
  2. import { Mixins } from "./Mixins.js";
  3. class TestMixins<
  4. TMixinMap extends Record<string, (arg: any, ctx?: any) => any>,
  5. TMixinKey extends Extract<keyof TMixinMap, string> = Extract<keyof TMixinMap, string>,
  6. > extends Mixins<TMixinMap, TMixinKey> {
  7. public removeAll(key: TMixinKey) {
  8. super.removeAll(key);
  9. }
  10. }
  11. describe("Mixins", () => {
  12. //#region base
  13. it("Base resolution", () => {
  14. const mixins = new TestMixins<{ foo: (v: number, ctx: { a: number }) => number; }>({
  15. autoIncrementPriority: true,
  16. });
  17. mixins.add("foo", (v) => v ^ 0b0001); // 1 (prio 0)
  18. mixins.add("foo", (v) => v ^ 0b1000); // 2 (prio 1)
  19. mixins.add("foo", (v, c) => v ^ c.a); // 3 (prio 2)
  20. // input: 0b1100
  21. // 1: 0b1100 ^ 0b0001 = 0b1101
  22. // 2: 0b1101 ^ 0b1000 = 0b0101
  23. // 3: 0b0101 ^ 0b0100 = 0b0001
  24. // result: 0b0001 = 1
  25. expect(mixins.resolve("foo", 0b1100, { a: 0b0100 })).toBe(1);
  26. expect(mixins.list()).toHaveLength(3);
  27. expect(mixins.list().every(m => m.key === "foo")).toBe(true);
  28. mixins.removeAll("foo");
  29. expect(mixins.list()).toHaveLength(0);
  30. });
  31. //#region priority
  32. it("Priority resolution", () => {
  33. const mixins = new Mixins<{ foo: (v: number) => number; }>();
  34. mixins.add("foo", (v) => v / 2, 1); // 2 (prio 1)
  35. mixins.add("foo", (v) => Math.round(Math.log(v) * 10), -1); // 4 (prio -1, index 0)
  36. mixins.add("foo", (v) => v + 2, -1); // 5 (prio -1, index 1)
  37. mixins.add("foo", (v) => v ** 2); // 3 (prio 0)
  38. mixins.add("foo", (v) => Math.sqrt(v), Number.MAX_SAFE_INTEGER); // 1 (prio max)
  39. // input: 100
  40. // 1: sqrt(100) = 10
  41. // 2: 10 / 2 = 5
  42. // 3: 5 ** 2 = 25
  43. // 4: round(log(25) * 10) = round(32.188758248682006) = 32
  44. // 5: 32 + 2 = 34
  45. expect(mixins.resolve("foo", 100)).toBe(34);
  46. });
  47. //#region sync/async & cleanup
  48. it("Sync/async resolution & cleanup", async () => {
  49. const acAll = new AbortController();
  50. const mixins = new Mixins<{ foo: (v: number) => Promise<number>; }>({
  51. defaultSignal: acAll.signal,
  52. });
  53. const ac1 = new AbortController();
  54. mixins.add("foo", (v) => Math.sqrt(v), { signal: ac1.signal }); // 1 (prio 0, index 0)
  55. mixins.add("foo", (v) => Math.pow(v, 4)); // 2 (prio 0, index 1)
  56. const rem3 = mixins.add("foo", async (v) => { // 3 (prio 0, index 2)
  57. await new Promise((r) => setTimeout(r, 50));
  58. return v + 2;
  59. });
  60. const rem4 = mixins.add("foo", async (v) => v); // 4 (prio 0, index 3)
  61. const res1 = mixins.resolve("foo", 100);
  62. expect(res1).toBeInstanceOf(Promise);
  63. expect(await res1).toBe(10002);
  64. rem3();
  65. rem4();
  66. const res2 = mixins.resolve("foo", 100);
  67. expect(res2).not.toBeInstanceOf(Promise);
  68. expect(res2).toBe(10000);
  69. ac1.abort();
  70. const res3 = mixins.resolve("foo", 100);
  71. expect(res3).not.toBeInstanceOf(Promise);
  72. expect(res3).toBe(100000000);
  73. acAll.abort();
  74. const res4 = mixins.resolve("foo", 100);
  75. expect(res4).not.toBeInstanceOf(Promise);
  76. expect(res4).toBe(100);
  77. });
  78. });