parent
2a0c2b0629
commit
3316818633
@ -1,126 +0,0 @@ |
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */ |
|
||||||
import type { |
|
||||||
InterfaceType, |
|
||||||
PrimitiveType, |
|
||||||
TraverserTypes, |
|
||||||
UnionType, |
|
||||||
} from "."; |
|
||||||
|
|
||||||
export type InterfaceVisitorFunction< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
Type extends InterfaceType<keyof Types>, |
|
||||||
Context, |
|
||||||
> = (originalData: Type["type"], context: Context) => Promise<void>; |
|
||||||
|
|
||||||
export type InterfaceVisitorPropertyFunction< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
Type extends InterfaceType<keyof Types>, |
|
||||||
PropertyName extends keyof Type["properties"], |
|
||||||
Context, |
|
||||||
> = ( |
|
||||||
originalData: Types[Type["properties"][PropertyName]]["type"], |
|
||||||
context: Context, |
|
||||||
) => Promise<void>; |
|
||||||
|
|
||||||
export type InterfaceVisitorDefinition< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
Type extends InterfaceType<keyof Types>, |
|
||||||
Context, |
|
||||||
> = { |
|
||||||
visitor: InterfaceVisitorFunction<Types, Type, Context>; |
|
||||||
properties: { |
|
||||||
[PropertyName in keyof Type["properties"]]: InterfaceVisitorPropertyFunction< |
|
||||||
Types, |
|
||||||
Type, |
|
||||||
PropertyName, |
|
||||||
Context |
|
||||||
>; |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
export type UnionVisitorFunction< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
Type extends UnionType<keyof Types>, |
|
||||||
Context, |
|
||||||
> = (originalData: Type["type"], context: Context) => Promise<void>; |
|
||||||
|
|
||||||
export type UnionVisitorDefinition< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
Type extends UnionType<keyof Types>, |
|
||||||
Context, |
|
||||||
> = UnionVisitorFunction<Types, Type, Context>; |
|
||||||
|
|
||||||
export type PrimitiveVisitorFunction<Type extends PrimitiveType, Context> = ( |
|
||||||
originalData: Type["type"], |
|
||||||
context: Context, |
|
||||||
) => Promise<void>; |
|
||||||
|
|
||||||
export type PrimitiveVisitorDefinition< |
|
||||||
Type extends PrimitiveType, |
|
||||||
Context, |
|
||||||
> = PrimitiveVisitorFunction<Type, Context>; |
|
||||||
|
|
||||||
export type VisitorDefinition< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
TypeName extends keyof Types, |
|
||||||
Context, |
|
||||||
> = Types[TypeName] extends InterfaceType<keyof Types> |
|
||||||
? InterfaceVisitorDefinition<Types, Types[TypeName], Context> |
|
||||||
: Types[TypeName] extends UnionType<keyof Types> |
|
||||||
? UnionVisitorDefinition<Types, Types[TypeName], Context> |
|
||||||
: Types[TypeName] extends PrimitiveType |
|
||||||
? PrimitiveVisitorDefinition<Types[TypeName], Context> |
|
||||||
: never; |
|
||||||
|
|
||||||
export type Visitors<Types extends TraverserTypes<any>, Context> = { |
|
||||||
[TypeName in keyof Types]: VisitorDefinition<Types, TypeName, Context>; |
|
||||||
}; |
|
||||||
|
|
||||||
/** |
|
||||||
* Input |
|
||||||
*/ |
|
||||||
export type InterfaceVisitorInputDefinition< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
Type extends InterfaceType<keyof Types>, |
|
||||||
Context, |
|
||||||
> = { |
|
||||||
visitor: InterfaceVisitorFunction<Types, Type, Context>; |
|
||||||
properties?: Partial<{ |
|
||||||
[PropertyName in keyof Type["properties"]]: InterfaceVisitorPropertyFunction< |
|
||||||
Types, |
|
||||||
Type, |
|
||||||
PropertyName, |
|
||||||
Context |
|
||||||
>; |
|
||||||
}>; |
|
||||||
}; |
|
||||||
|
|
||||||
export type UnionVisitorInputDefinition< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
Type extends UnionType<keyof Types>, |
|
||||||
Context, |
|
||||||
> = UnionVisitorFunction<Types, Type, Context>; |
|
||||||
|
|
||||||
export type PrimitiveVisitorInputDefinition< |
|
||||||
Type extends PrimitiveType, |
|
||||||
Context, |
|
||||||
> = PrimitiveVisitorFunction<Type, Context>; |
|
||||||
|
|
||||||
export type VisitorInputDefinition< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
TypeName extends keyof Types, |
|
||||||
Context, |
|
||||||
> = Types[TypeName] extends InterfaceType<keyof Types> |
|
||||||
? InterfaceVisitorInputDefinition<Types, Types[TypeName], Context> |
|
||||||
: Types[TypeName] extends UnionType<keyof Types> |
|
||||||
? UnionVisitorInputDefinition<Types, Types[TypeName], Context> |
|
||||||
: Types[TypeName] extends PrimitiveType |
|
||||||
? PrimitiveVisitorInputDefinition<Types[TypeName], Context> |
|
||||||
: never; |
|
||||||
|
|
||||||
export type VisitorsInput< |
|
||||||
Types extends TraverserTypes<any>, |
|
||||||
Context, |
|
||||||
> = Partial<{ |
|
||||||
[TypeName in keyof Types]: VisitorInputDefinition<Types, TypeName, Context>; |
|
||||||
}>; |
|
@ -1,5 +1,9 @@ |
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */ |
/* eslint-disable @typescript-eslint/no-explicit-any */ |
||||||
import type { BaseReturnType, BaseTraverserTypes, TraverserTypes } from ".."; |
import type { |
||||||
|
BaseReturnType, |
||||||
|
BaseTraverserTypes, |
||||||
|
TraverserTypes, |
||||||
|
} from "../../"; |
||||||
import type { TransformerReturnTypes } from "../TransformerReturnTypes"; |
import type { TransformerReturnTypes } from "../TransformerReturnTypes"; |
||||||
import { transformerInterfaceSubTraverser } from "./TransformerInterfaceSubTraverser"; |
import { transformerInterfaceSubTraverser } from "./TransformerInterfaceSubTraverser"; |
||||||
import { transformerPrimitiveSubTraverser } from "./TransformerPrimitiveSubTraverser"; |
import { transformerPrimitiveSubTraverser } from "./TransformerPrimitiveSubTraverser"; |
@ -1,4 +1,4 @@ |
|||||||
import type { KeyTypes } from "../.."; |
import type { KeyTypes } from "../../../"; |
||||||
import { MultiMap } from "./MultiMap"; |
import { MultiMap } from "./MultiMap"; |
||||||
import { MultiSet } from "./MultiSet"; |
import { MultiSet } from "./MultiSet"; |
||||||
import type { TransformerSubTraverserExecutingPromises } from "./transformerSubTraverserTypes"; |
import type { TransformerSubTraverserExecutingPromises } from "./transformerSubTraverserTypes"; |
@ -0,0 +1,157 @@ |
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */ |
||||||
|
import type { |
||||||
|
InterfaceType, |
||||||
|
PrimitiveType, |
||||||
|
TraverserTypes, |
||||||
|
UnionType, |
||||||
|
} from "../"; |
||||||
|
import type { InterfaceInstanceNode } from "../instanceGraph/nodes/InterfaceInstanceNode"; |
||||||
|
import type { PrimitiveInstanceNode } from "../instanceGraph/nodes/PrimitiveInstanceNode"; |
||||||
|
import type { UnionInstanceNode } from "../instanceGraph/nodes/UnionInstanceNode"; |
||||||
|
|
||||||
|
export type InterfaceVisitorFunction< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends InterfaceType<keyof Types> & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = ( |
||||||
|
originalData: Type["type"], |
||||||
|
node: InterfaceInstanceNode<Types, TypeName, Type>, |
||||||
|
context: Context, |
||||||
|
) => Promise<void>; |
||||||
|
|
||||||
|
export type InterfaceVisitorPropertyFunction< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends InterfaceType<keyof Types> & Types[TypeName], |
||||||
|
PropertyName extends keyof Type["properties"], |
||||||
|
Context, |
||||||
|
> = ( |
||||||
|
originalData: Types[Type["properties"][PropertyName]]["type"], |
||||||
|
node: InterfaceInstanceNode<Types, TypeName, Type>, |
||||||
|
context: Context, |
||||||
|
) => Promise<void>; |
||||||
|
|
||||||
|
export type InterfaceVisitorDefinition< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends InterfaceType<keyof Types> & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = { |
||||||
|
visitor: InterfaceVisitorFunction<Types, TypeName, Type, Context>; |
||||||
|
properties: { |
||||||
|
[PropertyName in keyof Type["properties"]]: InterfaceVisitorPropertyFunction< |
||||||
|
Types, |
||||||
|
TypeName, |
||||||
|
Type, |
||||||
|
PropertyName, |
||||||
|
Context |
||||||
|
>; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
export type UnionVisitorFunction< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends UnionType<keyof Types> & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = ( |
||||||
|
originalData: Type["type"], |
||||||
|
node: UnionInstanceNode<Types, TypeName, Type>, |
||||||
|
context: Context, |
||||||
|
) => Promise<void>; |
||||||
|
|
||||||
|
export type UnionVisitorDefinition< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends UnionType<keyof Types> & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = UnionVisitorFunction<Types, TypeName, Type, Context>; |
||||||
|
|
||||||
|
export type PrimitiveVisitorFunction< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends PrimitiveType & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = ( |
||||||
|
originalData: Type["type"], |
||||||
|
node: PrimitiveInstanceNode<Types, TypeName, Type>, |
||||||
|
context: Context, |
||||||
|
) => Promise<void>; |
||||||
|
|
||||||
|
export type PrimitiveVisitorDefinition< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends PrimitiveType & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = PrimitiveVisitorFunction<Types, TypeName, Type, Context>; |
||||||
|
|
||||||
|
export type VisitorDefinition< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Context, |
||||||
|
> = Types[TypeName] extends InterfaceType<keyof Types> |
||||||
|
? InterfaceVisitorDefinition<Types, TypeName, Types[TypeName], Context> |
||||||
|
: Types[TypeName] extends UnionType<keyof Types> |
||||||
|
? UnionVisitorDefinition<Types, TypeName, Types[TypeName], Context> |
||||||
|
: Types[TypeName] extends PrimitiveType |
||||||
|
? PrimitiveVisitorDefinition<Types, TypeName, Types[TypeName], Context> |
||||||
|
: never; |
||||||
|
|
||||||
|
export type Visitors<Types extends TraverserTypes<any>, Context> = { |
||||||
|
[TypeName in keyof Types]: VisitorDefinition<Types, TypeName, Context>; |
||||||
|
}; |
||||||
|
|
||||||
|
/** |
||||||
|
* Input |
||||||
|
*/ |
||||||
|
export type InterfaceVisitorInputDefinition< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends InterfaceType<keyof Types> & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = { |
||||||
|
visitor: InterfaceVisitorFunction<Types, TypeName, Type, Context>; |
||||||
|
properties?: Partial<{ |
||||||
|
[PropertyName in keyof Type["properties"]]: InterfaceVisitorPropertyFunction< |
||||||
|
Types, |
||||||
|
TypeName, |
||||||
|
Type, |
||||||
|
PropertyName, |
||||||
|
Context |
||||||
|
>; |
||||||
|
}>; |
||||||
|
}; |
||||||
|
|
||||||
|
export type UnionVisitorInputDefinition< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends UnionType<keyof Types> & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = UnionVisitorFunction<Types, TypeName, Type, Context>; |
||||||
|
|
||||||
|
export type PrimitiveVisitorInputDefinition< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Type extends PrimitiveType & Types[TypeName], |
||||||
|
Context, |
||||||
|
> = PrimitiveVisitorFunction<Types, TypeName, Type, Context>; |
||||||
|
|
||||||
|
export type VisitorInputDefinition< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
TypeName extends keyof Types, |
||||||
|
Context, |
||||||
|
> = Types[TypeName] extends InterfaceType<keyof Types> |
||||||
|
? InterfaceVisitorInputDefinition<Types, TypeName, Types[TypeName], Context> |
||||||
|
: Types[TypeName] extends UnionType<keyof Types> |
||||||
|
? UnionVisitorInputDefinition<Types, TypeName, Types[TypeName], Context> |
||||||
|
: Types[TypeName] extends PrimitiveType |
||||||
|
? PrimitiveVisitorInputDefinition<Types, TypeName, Types[TypeName], Context> |
||||||
|
: never; |
||||||
|
|
||||||
|
export type VisitorsInput< |
||||||
|
Types extends TraverserTypes<any>, |
||||||
|
Context, |
||||||
|
> = Partial<{ |
||||||
|
[TypeName in keyof Types]: VisitorInputDefinition<Types, TypeName, Context>; |
||||||
|
}>; |
@ -1,5 +1,5 @@ |
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */ |
/* eslint-disable @typescript-eslint/no-explicit-any */ |
||||||
import type { BaseTraverserTypes, TraverserTypes } from ".."; |
import type { BaseTraverserTypes, TraverserTypes } from "../../"; |
||||||
import type { |
import type { |
||||||
VisitorSubTraverser, |
VisitorSubTraverser, |
||||||
VisitorSubTraverserGlobals, |
VisitorSubTraverserGlobals, |
@ -0,0 +1,156 @@ |
|||||||
|
import type { TraverserDefinitions, ValidateTraverserTypes } from "../../src"; |
||||||
|
import { InstanceGraph } from "../../src/instanceGraph/instanceGraph"; |
||||||
|
|
||||||
|
describe("InstanceGraph", () => { |
||||||
|
/** |
||||||
|
* Types |
||||||
|
*/ |
||||||
|
type Element = "Water" | "Earth" | "Fire" | "Air"; |
||||||
|
interface Bender { |
||||||
|
name: string; |
||||||
|
element: Element; |
||||||
|
friends: Person[]; |
||||||
|
} |
||||||
|
interface NonBender { |
||||||
|
name: string; |
||||||
|
friends: Person[]; |
||||||
|
} |
||||||
|
type Person = Bender | NonBender; |
||||||
|
|
||||||
|
/** |
||||||
|
* Raw Data to Traverse |
||||||
|
*/ |
||||||
|
const aang: Bender = { |
||||||
|
name: "Aang", |
||||||
|
element: "Air", |
||||||
|
friends: [], |
||||||
|
}; |
||||||
|
const sokka: NonBender = { |
||||||
|
name: "Sokka", |
||||||
|
friends: [], |
||||||
|
}; |
||||||
|
const katara: Bender = { |
||||||
|
name: "Katara", |
||||||
|
element: "Water", |
||||||
|
friends: [], |
||||||
|
}; |
||||||
|
aang.friends.push(sokka, katara); |
||||||
|
sokka.friends.push(aang, katara); |
||||||
|
katara.friends.push(aang, sokka); |
||||||
|
|
||||||
|
/** |
||||||
|
* Traverser Types |
||||||
|
*/ |
||||||
|
type AvatarTraverserTypes = ValidateTraverserTypes<{ |
||||||
|
Element: { |
||||||
|
kind: "primitive"; |
||||||
|
type: Element; |
||||||
|
}; |
||||||
|
Bender: { |
||||||
|
kind: "interface"; |
||||||
|
type: Bender; |
||||||
|
properties: { |
||||||
|
element: "Element"; |
||||||
|
friends: "Person"; |
||||||
|
}; |
||||||
|
}; |
||||||
|
NonBender: { |
||||||
|
kind: "interface"; |
||||||
|
type: NonBender; |
||||||
|
properties: { |
||||||
|
friends: "Person"; |
||||||
|
}; |
||||||
|
}; |
||||||
|
Person: { |
||||||
|
kind: "union"; |
||||||
|
type: Person; |
||||||
|
typeNames: "Bender" | "NonBender"; |
||||||
|
}; |
||||||
|
}>; |
||||||
|
|
||||||
|
/** |
||||||
|
* Create the traverser definition |
||||||
|
*/ |
||||||
|
const avatarTraverserDefinition: TraverserDefinitions<AvatarTraverserTypes> = |
||||||
|
{ |
||||||
|
Element: { |
||||||
|
kind: "primitive", |
||||||
|
}, |
||||||
|
Bender: { |
||||||
|
kind: "interface", |
||||||
|
properties: { |
||||||
|
element: "Element", |
||||||
|
friends: "Person", |
||||||
|
}, |
||||||
|
}, |
||||||
|
NonBender: { |
||||||
|
kind: "interface", |
||||||
|
properties: { |
||||||
|
friends: "Person", |
||||||
|
}, |
||||||
|
}, |
||||||
|
Person: { |
||||||
|
kind: "union", |
||||||
|
selector: (item) => { |
||||||
|
return (item as Bender).element ? "Bender" : "NonBender"; |
||||||
|
}, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
describe("Build Instance Graph", () => { |
||||||
|
it("returns child nodes when child methods are called.", () => { |
||||||
|
const graph = new InstanceGraph(avatarTraverserDefinition); |
||||||
|
const aangBender = graph.getNodeFor(aang, "Bender"); |
||||||
|
expect(aangBender.typeName).toBe("Bender"); |
||||||
|
expect(aangBender.instance.name).toBe("Aang"); |
||||||
|
// child
|
||||||
|
const aangElement = aangBender.child("element"); |
||||||
|
expect(aangElement.instance).toBe("Air"); |
||||||
|
expect(aangElement.typeName).toBe("Element"); |
||||||
|
const aangFriends = aangBender.child("friends"); |
||||||
|
expect(aangFriends.length).toBe(2); |
||||||
|
const sokkaPerson = aangFriends[0]; |
||||||
|
const kataraPerson = aangFriends[1]; |
||||||
|
expect(sokkaPerson.instance.name).toBe("Sokka"); |
||||||
|
expect(kataraPerson.instance.name).toBe("Katara"); |
||||||
|
expect(sokkaPerson.typeName).toBe("Person"); |
||||||
|
expect(kataraPerson.typeName).toBe("Person"); |
||||||
|
const sokkaNonBender = sokkaPerson.child(); |
||||||
|
expect(sokkaNonBender.instance.name).toBe("Sokka"); |
||||||
|
expect(sokkaNonBender.typeName).toBe("NonBender"); |
||||||
|
if (sokkaNonBender.typeName === "NonBender") { |
||||||
|
const aangPerson = sokkaNonBender.child("friends")[0]; |
||||||
|
const aangBender2 = aangPerson.child(); |
||||||
|
expect(aangBender2).toBe(aangBender); |
||||||
|
} |
||||||
|
// allChildren
|
||||||
|
const [childElemement, childSokka, childKatara] = |
||||||
|
aangBender.allChildren(); |
||||||
|
expect(childElemement.instance).toBe("Air"); |
||||||
|
expect((childSokka.instance as NonBender).name).toBe("Sokka"); |
||||||
|
expect((childKatara.instance as Bender).name).toBe("Katara"); |
||||||
|
const childOfSokkaPerson = sokkaPerson.allChildren(); |
||||||
|
expect(childOfSokkaPerson.length).toBe(1); |
||||||
|
expect(childOfSokkaPerson[0].instance.name).toBe("Sokka"); |
||||||
|
}); |
||||||
|
|
||||||
|
it("returns parent nodes when parent methods are called.", () => { |
||||||
|
const graph = new InstanceGraph(avatarTraverserDefinition); |
||||||
|
const aangBender = graph.getNodeFor(aang, "Bender"); |
||||||
|
// parent
|
||||||
|
const [aangPerson] = aangBender.parent("Person"); |
||||||
|
expect(aangPerson.instance.name).toBe("Aang"); |
||||||
|
expect(aangPerson.typeName).toBe("Person"); |
||||||
|
const [sokkaNonBender] = aangPerson.parent("NonBender", "friends"); |
||||||
|
const [kataraBender] = aangPerson.parent("Bender", "friends"); |
||||||
|
expect(sokkaNonBender.typeName).toBe("NonBender"); |
||||||
|
expect(sokkaNonBender.instance.name).toBe("Sokka"); |
||||||
|
expect(kataraBender.typeName).toBe("Bender"); |
||||||
|
expect(kataraBender.instance.name).toBe("Katara"); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe("Transformer", () => { |
||||||
|
it("transforms", () => {}); |
||||||
|
}); |
||||||
|
}); |
@ -1,7 +1,7 @@ |
|||||||
import type { TraverserDefinition } from "../../../src"; |
import type { TraverserDefinitions } from "../../../src"; |
||||||
import type { AvatarTraverserTypes, Bender } from "./AvatarTraverserTypes"; |
import type { AvatarTraverserTypes, Bender } from "./AvatarTraverserTypes"; |
||||||
|
|
||||||
export const avatarTraverserDefinition: TraverserDefinition<AvatarTraverserTypes> = |
export const avatarTraverserDefinition: TraverserDefinitions<AvatarTraverserTypes> = |
||||||
{ |
{ |
||||||
Element: { |
Element: { |
||||||
kind: "primitive", |
kind: "primitive", |
@ -1,161 +1,19 @@ |
|||||||
import type { |
import { BrokenAvatarTransformer } from "./AvatarBrokenTransformer"; |
||||||
TraverserDefinitions, |
import { AvatarErroringTransformer } from "./AvatarErroringTransformer"; |
||||||
ValidateTraverserTypes, |
import { aang } from "./sampleData"; |
||||||
} from "../../../src"; |
|
||||||
import { InstanceGraph } from "../../../src/instanceGraph/instanceGraph"; |
|
||||||
|
|
||||||
describe("AvatarExample", () => { |
describe("Avatar", () => { |
||||||
/** |
it("Throws an error before entering an infinite loop", async () => { |
||||||
* Types |
await expect( |
||||||
*/ |
BrokenAvatarTransformer.transform(aang, "Bender", undefined), |
||||||
type Element = "Water" | "Earth" | "Fire" | "Air"; |
).rejects.toThrow( |
||||||
interface Bender { |
`Circular dependency found. Use the 'setReturnPointer' function. The loop includes the 'Bender' type`, |
||||||
name: string; |
); |
||||||
element: Element; |
|
||||||
friends: Person[]; |
|
||||||
} |
|
||||||
interface NonBender { |
|
||||||
name: string; |
|
||||||
friends: Person[]; |
|
||||||
} |
|
||||||
type Person = Bender | NonBender; |
|
||||||
|
|
||||||
/** |
|
||||||
* Raw Data to Traverse |
|
||||||
*/ |
|
||||||
const aang: Bender = { |
|
||||||
name: "Aang", |
|
||||||
element: "Air", |
|
||||||
friends: [], |
|
||||||
}; |
|
||||||
const sokka: NonBender = { |
|
||||||
name: "Sokka", |
|
||||||
friends: [], |
|
||||||
}; |
|
||||||
const katara: Bender = { |
|
||||||
name: "Katara", |
|
||||||
element: "Water", |
|
||||||
friends: [], |
|
||||||
}; |
|
||||||
aang.friends.push(sokka, katara); |
|
||||||
sokka.friends.push(aang, katara); |
|
||||||
katara.friends.push(aang, sokka); |
|
||||||
|
|
||||||
/** |
|
||||||
* Traverser Types |
|
||||||
*/ |
|
||||||
type AvatarTraverserTypes = ValidateTraverserTypes<{ |
|
||||||
Element: { |
|
||||||
kind: "primitive"; |
|
||||||
type: Element; |
|
||||||
}; |
|
||||||
Bender: { |
|
||||||
kind: "interface"; |
|
||||||
type: Bender; |
|
||||||
properties: { |
|
||||||
element: "Element"; |
|
||||||
friends: "Person"; |
|
||||||
}; |
|
||||||
}; |
|
||||||
NonBender: { |
|
||||||
kind: "interface"; |
|
||||||
type: NonBender; |
|
||||||
properties: { |
|
||||||
friends: "Person"; |
|
||||||
}; |
|
||||||
}; |
|
||||||
Person: { |
|
||||||
kind: "union"; |
|
||||||
type: Person; |
|
||||||
typeNames: "Bender" | "NonBender"; |
|
||||||
}; |
|
||||||
}>; |
|
||||||
|
|
||||||
/** |
|
||||||
* Create the traverser definition |
|
||||||
*/ |
|
||||||
const avatarTraverserDefinition: TraverserDefinitions<AvatarTraverserTypes> = |
|
||||||
{ |
|
||||||
Element: { |
|
||||||
kind: "primitive", |
|
||||||
}, |
|
||||||
Bender: { |
|
||||||
kind: "interface", |
|
||||||
properties: { |
|
||||||
element: "Element", |
|
||||||
friends: "Person", |
|
||||||
}, |
|
||||||
}, |
|
||||||
NonBender: { |
|
||||||
kind: "interface", |
|
||||||
properties: { |
|
||||||
friends: "Person", |
|
||||||
}, |
|
||||||
}, |
|
||||||
Person: { |
|
||||||
kind: "union", |
|
||||||
selector: (item) => { |
|
||||||
return (item as Bender).element ? "Bender" : "NonBender"; |
|
||||||
}, |
|
||||||
}, |
|
||||||
}; |
|
||||||
|
|
||||||
describe("Build Instance Graph", () => { |
|
||||||
it("returns child nodes when child methods are called.", () => { |
|
||||||
const graph = new InstanceGraph(avatarTraverserDefinition); |
|
||||||
const aangBender = graph.getNodeFor(aang, "Bender"); |
|
||||||
expect(aangBender.typeName).toBe("Bender"); |
|
||||||
expect(aangBender.instance.name).toBe("Aang"); |
|
||||||
// child
|
|
||||||
const aangElement = aangBender.child("element"); |
|
||||||
expect(aangElement.instance).toBe("Air"); |
|
||||||
expect(aangElement.typeName).toBe("Element"); |
|
||||||
const aangFriends = aangBender.child("friends"); |
|
||||||
expect(aangFriends.length).toBe(2); |
|
||||||
const sokkaPerson = aangFriends[0]; |
|
||||||
const kataraPerson = aangFriends[1]; |
|
||||||
expect(sokkaPerson.instance.name).toBe("Sokka"); |
|
||||||
expect(kataraPerson.instance.name).toBe("Katara"); |
|
||||||
expect(sokkaPerson.typeName).toBe("Person"); |
|
||||||
expect(kataraPerson.typeName).toBe("Person"); |
|
||||||
const sokkaNonBender = sokkaPerson.child(); |
|
||||||
expect(sokkaNonBender.instance.name).toBe("Sokka"); |
|
||||||
expect(sokkaNonBender.typeName).toBe("NonBender"); |
|
||||||
if (sokkaNonBender.typeName === "NonBender") { |
|
||||||
const aangPerson = sokkaNonBender.child("friends")[0]; |
|
||||||
const aangBender2 = aangPerson.child(); |
|
||||||
expect(aangBender2).toBe(aangBender); |
|
||||||
} |
|
||||||
// allChildren
|
|
||||||
const [childElemement, childSokka, childKatara] = |
|
||||||
aangBender.allChildren(); |
|
||||||
expect(childElemement.instance).toBe("Air"); |
|
||||||
expect((childSokka.instance as NonBender).name).toBe("Sokka"); |
|
||||||
expect((childKatara.instance as Bender).name).toBe("Katara"); |
|
||||||
const childOfSokkaPerson = sokkaPerson.allChildren(); |
|
||||||
expect(childOfSokkaPerson.length).toBe(1); |
|
||||||
expect(childOfSokkaPerson[0].instance.name).toBe("Sokka"); |
|
||||||
}); |
|
||||||
|
|
||||||
it("returns parent nodes when parent methods are called.", () => { |
|
||||||
const graph = new InstanceGraph(avatarTraverserDefinition); |
|
||||||
const aangBender = graph.getNodeFor(aang, "Bender"); |
|
||||||
// parent
|
|
||||||
const [aangPerson] = aangBender.parent("Person"); |
|
||||||
expect(aangPerson.instance.name).toBe("Aang"); |
|
||||||
expect(aangPerson.typeName).toBe("Person"); |
|
||||||
const [sokkaNonBender] = aangPerson.parent("NonBender", "friends"); |
|
||||||
const [kataraBender] = aangPerson.parent("Bender", "friends"); |
|
||||||
expect(sokkaNonBender.typeName).toBe("NonBender"); |
|
||||||
expect(sokkaNonBender.instance.name).toBe("Sokka"); |
|
||||||
expect(kataraBender.typeName).toBe("Bender"); |
|
||||||
expect(kataraBender.instance.name).toBe("Katara"); |
|
||||||
}); |
|
||||||
}); |
}); |
||||||
|
|
||||||
describe("Transformer", () => { |
it("Bubbles errors", async () => { |
||||||
it("transforms", () => { |
await expect( |
||||||
|
AvatarErroringTransformer.transform(aang, "Bender", undefined), |
||||||
}) |
).rejects.toThrow("No Non Benders Allowed"); |
||||||
}); |
}); |
||||||
}); |
}); |
||||||
|
@ -1,19 +0,0 @@ |
|||||||
import { BrokenAvatarTransformer } from "./AvatarBrokenTransformer"; |
|
||||||
import { AvatarErroringTransformer } from "./AvatarErroringTransformer"; |
|
||||||
import { aang } from "./sampleData"; |
|
||||||
|
|
||||||
describe("Avatar", () => { |
|
||||||
it("Throws an error before entering an infinite loop", async () => { |
|
||||||
await expect( |
|
||||||
BrokenAvatarTransformer.transform(aang, "Bender", undefined), |
|
||||||
).rejects.toThrow( |
|
||||||
`Circular dependency found. Use the 'setReturnPointer' function. The loop includes the 'Bender' type`, |
|
||||||
); |
|
||||||
}); |
|
||||||
|
|
||||||
it("Bubbles errors", async () => { |
|
||||||
await expect( |
|
||||||
AvatarErroringTransformer.transform(aang, "Bender", undefined), |
|
||||||
).rejects.toThrow("No Non Benders Allowed"); |
|
||||||
}); |
|
||||||
}); |
|
Loading…
Reference in new issue