From 46d827460b052691f7ac91af74de3dd6452e5fdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Marie=20De=20Mey?= Date: Thu, 7 Nov 2024 12:46:07 +0200 Subject: [PATCH] edge-cases (beginning) --- CHANGELOG.md | 9 +- README.md | 3 +- package-lock.json | 264 ++++++++++++++++++++++++++++++++++++---- package.json | 2 +- src/diamond.ts | 59 ++++++--- src/seclude.ts | 3 +- src/utils.ts | 10 ++ test/edge-cases.test.ts | 44 +++++++ 8 files changed, 344 insertions(+), 50 deletions(-) create mode 100644 test/edge-cases.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 9b0d787..e5bec6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,13 @@ ## Bug-fix -- `KeySet` now rooted on `Object.create(null)` instead of `{}` so that `'constructor' in secludedProperties` is false -- Temporary object (modified as head with `this` diamond as proxy) now has good constructor and `instanceof` +- `KeySet` now rooted on `Object.create(null)` instead of `{}` so that `'constructor' in secludedProperties` is false +- Temporary object (modified as head with `this` diamond as proxy) now has good constructor and `instanceof` +- Construction strategy is now a stack that is `unshift`-ed and not a map anymore. Detect construction conflict more efficiently (detect all conflict, no error on non-conflictual cases) # 1.0.9 ## Change -- `.secluded(...)` is removed as `Secluded` class becomes the function -- `instanceOf` helper is removed as classes used in diamonds have their `hasInstance` modified and `instanceof` keyword is enough \ No newline at end of file +- `.secluded(...)` is removed as `Secluded` class becomes the function +- `instanceOf` helper is removed as classes used in diamonds have their `hasInstance` modified and `instanceof` keyword is enough diff --git a/README.md b/README.md index f5f73cd..c77b1d1 100644 --- a/README.md +++ b/README.md @@ -189,7 +189,8 @@ D2(0) ### Construction concern -Building another diamond in the constructor before calling `super(...)` will break the game. (Fields are initialized when `super` returns - no worries there) +If a diamond creates an instance of the same diamond (itself) in its constructor before calling `super(...)`, a `LateSuperError` will be thrown. +Fields are initialized when `super` returns - no worries there # Seclusion diff --git a/package-lock.json b/package-lock.json index 20722a4..6ebffb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,14 +1,15 @@ { "name": "flat-diamond", - "version": "1.0.7", + "version": "1.0.10", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "flat-diamond", - "version": "1.0.7", + "version": "1.0.10", "license": "ISC", "devDependencies": { + "@biomejs/biome": "1.9.4", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@types/jest": "^29.5.12", @@ -16,7 +17,6 @@ "@typescript-eslint/parser": "^8.12.2", "eslint": "^9.13.0", "jest": "^29.7.0", - "prettier": "^3.3.3", "rollup": "^4.18.0", "rollup-plugin-dts": "^6.1.0", "rollup-plugin-typescript2": "^0.36.0", @@ -525,6 +525,170 @@ "dev": true, "license": "MIT" }, + "node_modules/@biomejs/biome": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", + "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", + "dev": true, + "hasInstallScript": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "1.9.4", + "@biomejs/cli-darwin-x64": "1.9.4", + "@biomejs/cli-linux-arm64": "1.9.4", + "@biomejs/cli-linux-arm64-musl": "1.9.4", + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", + "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", + "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", + "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", + "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", + "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", + "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", + "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", + "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -4744,22 +4908,6 @@ "node": ">= 0.8.0" } }, - "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -6000,6 +6148,78 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@biomejs/biome": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.9.4.tgz", + "integrity": "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==", + "dev": true, + "requires": { + "@biomejs/cli-darwin-arm64": "1.9.4", + "@biomejs/cli-darwin-x64": "1.9.4", + "@biomejs/cli-linux-arm64": "1.9.4", + "@biomejs/cli-linux-arm64-musl": "1.9.4", + "@biomejs/cli-linux-x64": "1.9.4", + "@biomejs/cli-linux-x64-musl": "1.9.4", + "@biomejs/cli-win32-arm64": "1.9.4", + "@biomejs/cli-win32-x64": "1.9.4" + } + }, + "@biomejs/cli-darwin-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.9.4.tgz", + "integrity": "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw==", + "dev": true, + "optional": true + }, + "@biomejs/cli-darwin-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.9.4.tgz", + "integrity": "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.9.4.tgz", + "integrity": "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-arm64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-1.9.4.tgz", + "integrity": "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-1.9.4.tgz", + "integrity": "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==", + "dev": true, + "optional": true + }, + "@biomejs/cli-linux-x64-musl": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-1.9.4.tgz", + "integrity": "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-arm64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.9.4.tgz", + "integrity": "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==", + "dev": true, + "optional": true + }, + "@biomejs/cli-win32-x64": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-1.9.4.tgz", + "integrity": "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA==", + "dev": true, + "optional": true + }, "@eslint-community/eslint-utils": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", @@ -8851,12 +9071,6 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, - "prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true - }, "pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", diff --git a/package.json b/package.json index eca9e7a..8c964ae 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "author": "François Marie ", "license": "ISC", "devDependencies": { + "@biomejs/biome": "1.9.4", "@rollup/plugin-commonjs": "^25.0.7", "@rollup/plugin-node-resolve": "^15.2.3", "@types/jest": "^29.5.12", @@ -44,7 +45,6 @@ "@typescript-eslint/parser": "^8.12.2", "eslint": "^9.13.0", "jest": "^29.7.0", - "prettier": "^3.3.3", "rollup": "^4.18.0", "rollup-plugin-dts": "^6.1.0", "rollup-plugin-typescript2": "^0.36.0", diff --git a/src/diamond.ts b/src/diamond.ts index 608060f..72403e0 100644 --- a/src/diamond.ts +++ b/src/diamond.ts @@ -1,33 +1,44 @@ import { Ctor, HasBases, Newable } from './types' import { allFLegs, + bottomLeg, emptySecludedProxyHandler, fLegs, hasInstanceManager, hasInstanceManagers, + LateSuperError, linearLeg, manageHasInstance, nextInFLeg } from './utils' -type BuildingStrategy = Map +type BuildingStrategy = { + target: Ctor + subs: Ctor[] +}[] let buildingDiamond: { built: object // TODO: Not a map, should be a stack where each elements are popped one by one, we know the order // It could allow 'Inconsistent diamond hierarchy' to become a warning? // (the error would be checked when exiting the stack) + // Also, we might have undetected conflict (2 objects ending up in the same `this`) strategy: BuildingStrategy } | null = null const diamondHandler: { - getPrototypeOf(target: Ctor): Ctor get(target: Ctor, p: PropertyKey, receiver: Ctor): any set(target: Ctor, p: PropertyKey, v: any, receiver: Ctor): boolean } & ProxyHandler = { get(target, p, receiver) { if (p === 'constructor') return Object const pd = nextInFLeg(receiver.constructor, p, target) - return pd && ('value' in pd ? pd.value : 'get' in pd ? pd.get!.call(receiver) : undefined) + return pd + ? 'value' in pd + ? pd.value + : 'get' in pd + ? pd.get!.call(receiver) + : undefined + : ({} as any)[p] }, set(target, p, v, receiver) { const pd = nextInFLeg(receiver.constructor, p, target) @@ -41,9 +52,6 @@ const diamondHandler: { else if (pd && pd.set) pd.set.call(receiver, v) else return false return true - }, - getPrototypeOf(target) { - return Object } } @@ -74,22 +82,28 @@ export default function Diamond( } while (iFLeg >= 0) bases.push(...fLeg) } - const buildingStrategy = new Map() + const buildingStrategy: BuildingStrategy = [] const myResponsibility: Ctor[] = [] + class Diamond { constructor(...args: any[]) { lastDiamondProperties = null - const responsibility = buildingDiamond - ? buildingDiamond!.strategy.get(this.constructor as Ctor)! - : myResponsibility - if (!responsibility) { - throw new Error(`Inconsistent diamond hierarchy. -This might happen if a diamond is created from another constructor before its 'super(...)' is called.`) - } - if (!buildingDiamond) + const { responsibility, bdRestore } = !buildingDiamond + ? { + responsibility: myResponsibility + } + : buildingDiamond.strategy[0].target === Diamond + ? { + responsibility: buildingDiamond.strategy.shift()!.subs + } + : { + bdRestore: buildingDiamond, + responsibility: myResponsibility + } + if (!buildingDiamond || bdRestore) buildingDiamond = { built: this, - strategy: buildingStrategy + strategy: [...buildingStrategy] } // It will be set to `null` on purpose in the process and needs to be restored const locallyStoredDiamond = buildingDiamond! try { @@ -100,6 +114,9 @@ This might happen if a diamond is created from another constructor before its 's const temp = new subs(...args) // Even if `Diamond` managed: property initializers do not go through proxy if (locallyStoredDiamond.built !== temp) { + if (fLegs(subs)) + throw new LateSuperError(`Inconsistent diamond hierarchy under [Diamond<${baseClasses.map((b) => b.name).join(',')}>], for ${this.constructor.name}, +This happens if a diamond creates another instance of the same diamond in the constructor before calling \`super(...)\`.`) // import properties from temp object Object.defineProperties( locallyStoredDiamond.built, @@ -118,13 +135,18 @@ This might happen if a diamond is created from another constructor before its 's } } finally { //In the constructor method and in the field initializers, we can build diamonds, but not *this* diamond - buildingDiamond = null + buildingDiamond = bdRestore ?? null } lastDiamondProperties = Object.getOwnPropertyDescriptors(locallyStoredDiamond.built) // Value used by `this` on `super(...)` return + // @ts-expect-error `Symbol.toStringTag` return locallyStoredDiamond.built } static [Symbol.hasInstance] = hasInstanceManager(Diamond) + + get [Symbol.toStringTag]() { + return 'Diamond<' + bases.map((base) => base.name).join(',') + '>' + } } hasInstanceManagers.add(Diamond) allFLegs.set(Diamond, bases) @@ -136,7 +158,8 @@ This might happen if a diamond is created from another constructor before its 's let nextResponsibility = myResponsibility for (const base of bases) { nextResponsibility.unshift(base) - if (fLegs(base)) buildingStrategy.set(base, (nextResponsibility = [])) + if (fLegs(base)) + buildingStrategy.push({ target: bottomLeg(base), subs: (nextResponsibility = []) }) } Object.setPrototypeOf(Diamond.prototype, new Proxy(Diamond, diamondHandler)) diff --git a/src/seclude.ts b/src/seclude.ts index 97d3685..b9af7e1 100644 --- a/src/seclude.ts +++ b/src/seclude.ts @@ -163,11 +163,12 @@ export function Seclude]` + return `Secluded<${target.name}>` } const actor = whoAmI(receiver) if (p in target.prototype && (!(p in secludedProperties) || actor.domain === 'private')) { const pd = nextInLine(target, p)! + if (!pd) return Reflect.get(target.prototype, p, receiver) if ('get' in pd) return pd.get!.call(actor.private) if ('value' in pd) { const rv = pd.value! diff --git a/src/utils.ts b/src/utils.ts index 95bf8bd..14455a0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,12 @@ import { Ctor, KeySet } from './types' +export class LateSuperError extends Error { + constructor(message: string) { + super(message) + this.name = 'LateSuperError' + } +} + /** * Gives all the classes from the base up to just before Object * Note: In "uni-legacy", the parent of Diamond is Object @@ -63,10 +70,12 @@ export function nextInFLeg( diamond: Ctor ): PropertyDescriptor | undefined { const fLeg = fLegs(ctor) + /* istanbul ignore next: internal bug guard */ if (!fLeg) throw new Error('Inconsistent diamond hierarchy') let ndx = bottomLeg(ctor) === diamond ? 0 : -1 if (ndx < 0) { ndx = fLeg.findIndex((base) => bottomLeg(base) === diamond) + 1 + /* istanbul ignore next: internal bug guard */ if (ndx <= 0) throw new Error('Inconsistent diamond hierarchy') } let rv: PropertyDescriptor | undefined @@ -92,6 +101,7 @@ export function secludedProxyHandler( }, set(target, p, value, receiver) { if (p in secludedProperties) { + // Occurs when a private field had no value in the private part yet Object.defineProperty(receiver, p, { value, writable: true, diff --git a/test/edge-cases.test.ts b/test/edge-cases.test.ts new file mode 100644 index 0000000..8f49bef --- /dev/null +++ b/test/edge-cases.test.ts @@ -0,0 +1,44 @@ +import Diamond, { Seclude } from '../src' + +test('toStringTags', () => { + class X {} + const S = Seclude(X), + s = new S() + expect(s.toString()).toBe('[object Secluded]') + const D = Diamond(X), + d = new D() + expect(d.toString()).toBe('[object Diamond]') +}) + +describe('before super', () => { + test('working', () => { + class A extends Diamond() {} + const X = Diamond(A) + class B extends Diamond() { + a: InstanceType + constructor() { + const a = new X() + super() + this.a = a + } + } + class C extends Diamond(B) {} + const c = new C() + expect(c.a).toBeInstanceOf(A) + expect(c.a === c).toBe(false) + }) + test('failing', () => { + class A extends Diamond() {} + const X = Diamond(A) + class B extends X { + a: InstanceType + constructor() { + const a = new X() + super() + this.a = a + } + } + class C extends Diamond(B) {} + expect(() => new C()).toThrow() + }) +})