1+ import { Substitute } from './Substitute'
2+ import { PropertyType , isSubstituteMethod } from './Utilities'
3+ import { SubstituteJS as SubstituteBase , SubstituteException } from './SubstituteBase'
4+ import { RecordedArguments } from './RecordedArguments'
5+
6+ import { Node } from './linked-list/Node'
7+
8+ export class ContextNode extends Node < Substitute , ContextNode > {
9+ private _property : PropertyKey
10+ private _propertyType : PropertyType = PropertyType . property
11+ private _arguments : RecordedArguments = RecordedArguments . noArguments ( )
12+
13+ private _hasSubstitution : boolean = false
14+ private _isSubstitution : boolean = false
15+
16+ private _proxy : typeof SubstituteBase
17+
18+ constructor ( property : PropertyKey ) {
19+ super ( )
20+
21+ this . _property = property
22+ const nodeProxy : typeof SubstituteBase = new Proxy ( SubstituteBase , {
23+ apply : ( _target , _this , rawArguments ?: any [ ] ) => {
24+ this . handleMethod ( rawArguments )
25+ return this . returnProxyOrSubstitution ( )
26+ } ,
27+ getPrototypeOf ( ) {
28+ return SubstituteBase . prototype
29+ } ,
30+ // set: (_target, property, value) => (this.initialState.set(this, property, value), true),
31+ get : ( _target , property : PropertyKey ) => {
32+ if ( property === 'prototype' ) Reflect . get ( _target , property )
33+
34+ const newNode = this . createNewNode ( property )
35+ this . child = newNode
36+
37+ return newNode . proxy
38+ }
39+ } )
40+
41+ this . _proxy = nodeProxy
42+ }
43+
44+ public labelAsSubstitution ( ) : void {
45+ this . _isSubstitution = true
46+ }
47+
48+ get isSubstitution ( ) : boolean {
49+ return this . _isSubstitution
50+ }
51+
52+ public enableSubstitution ( ) : void {
53+ this . _hasSubstitution = true
54+ }
55+
56+ get hasSubstitution ( ) : boolean {
57+ return this . _hasSubstitution
58+ }
59+
60+ get property ( ) {
61+ return this . _property
62+ }
63+
64+ get propertyType ( ) {
65+ return this . _propertyType
66+ }
67+
68+ get proxy ( ) {
69+ return this . _proxy
70+ }
71+
72+ get arguments ( ) {
73+ return this . _arguments
74+ }
75+
76+ public returnProxyOrSubstitution ( ) {
77+ const allSubstitutions = this . findAllSubstitutions ( )
78+ const mostSuitableSubstitution = this . getMostSuitableSubstitution ( allSubstitutions )
79+
80+ return mostSuitableSubstitution instanceof ContextNode ?
81+ mostSuitableSubstitution . executeSubstitution ( ) :
82+ this . proxy
83+ }
84+
85+ public executeSubstitution ( ) {
86+ return this . child . arguments . value [ 0 ] // Todo: support all substitutions, and multiple mockvalues
87+ }
88+
89+ private createNewNode ( property : PropertyKey ) : ContextNode {
90+ const newNode = new ContextNode ( property )
91+ newNode . parent = this
92+ newNode . head = this . head
93+
94+ return newNode
95+ }
96+
97+ private handleMethod ( rawArguments : any [ ] ) : void {
98+ this . _propertyType = PropertyType . method
99+ this . _arguments = RecordedArguments . from ( rawArguments )
100+
101+ if ( isSubstituteMethod ( this . property ) ) {
102+ this . labelAsSubstitution ( )
103+ if ( this . isIntermediateNode ( ) ) this . parent . enableSubstitution ( )
104+ }
105+ }
106+
107+ private findAllSubstitutions ( ) : ContextNode [ ] {
108+ const firstNode = this . head
109+ const root = firstNode . parent
110+
111+ const allSiblings = root . getSiblingsOf ( firstNode )
112+ return allSiblings . filter ( node => node . hasSubstitution &&
113+ node . propertyType === this . propertyType &&
114+ node . arguments . match ( this . arguments )
115+ )
116+ }
117+
118+ private getMostSuitableSubstitution ( nodes : ContextNode [ ] ) : ContextNode {
119+ const sortedNodes = RecordedArguments . sort ( nodes )
120+ return sortedNodes [ 0 ]
121+ }
122+ }
0 commit comments