@@ -90,7 +90,217 @@ class VisitorTests: XCTestCase {
9090 }
9191 ) )
9292 }
93-
93+
94+ func testAllowsEditingANodeBothOnEnterAndOnLeave( ) throws {
95+ let ast = try parse ( source: " { a, b, c { a, b, c } } " , noLocation: true )
96+
97+ var selectionSet : SelectionSet ? = nil
98+
99+ let editedASTNode = visit ( root: ast, visitor: . init(
100+ enter: { node, key, parent, path, ancestors in
101+ if let node = node as? OperationDefinition {
102+ checkVisitorFnArgs ( ast, node, key, parent, path, ancestors)
103+ selectionSet = node. selectionSet
104+ let newName = node. name
105+ . map { Name ( loc: $0. loc, value: $0. value + " .enter " ) } ??
106+ Name ( value: " enter " )
107+ node. set ( value: . node( newName) , key: " name " )
108+ node. set ( value: . node( SelectionSet ( selections: [ ] ) ) , key: " selectionSet " )
109+ return . node( node)
110+ }
111+ return . continue
112+ } ,
113+ leave: { node, key, parent, path, ancestors in
114+ if let node = node as? OperationDefinition {
115+ checkVisitorFnArgs ( ast, node, key, parent, path, ancestors, isEdited: true )
116+ let newName = node. name
117+ . map { Name ( loc: $0. loc, value: $0. value + " .leave " ) } ??
118+ Name ( value: " leave " )
119+ node. set ( value: . node( newName) , key: " name " )
120+ node. set ( value: . node( selectionSet!) , key: " selectionSet " )
121+ return . node( node)
122+ }
123+ return . continue
124+ }
125+ ) )
126+
127+ let editedAST = try XCTUnwrap ( editedASTNode as? Document )
128+ let operations = try XCTUnwrap ( editedAST. definitions as? [ OperationDefinition ] )
129+ XCTAssertEqual ( operations. count, 1 )
130+ XCTAssertEqual ( operations. first? . name? . value, " enter.leave " )
131+ let operationSelections = try XCTUnwrap ( operations. first? . selectionSet. selections)
132+ XCTAssertEqual ( operationSelections. count, 3 )
133+ }
134+
135+ func testAllowsEditingTheRootNodeOnEnterAndOnLeave( ) throws {
136+ let ast = try parse ( source: " { a, b, c { a, b, c } } " , noLocation: true )
137+
138+ let editedASTNode = visit ( root: ast, visitor: . init(
139+ enter: { node, key, parent, path, ancestors in
140+ if let node = node as? Document {
141+ checkVisitorFnArgs ( ast, node, key, parent, path, ancestors)
142+ var newDefinitions = node. definitions
143+ newDefinitions. append (
144+ DirectiveDefinition (
145+ name: . init( value: " enter " ) ,
146+ locations: [ . init( value: " root " ) ]
147+ )
148+ )
149+ node. set (
150+ value: . array( newDefinitions) ,
151+ key: " definitions "
152+ )
153+ return . node( node)
154+ }
155+ return . continue
156+ } ,
157+ leave: { node, key, parent, path, ancestors in
158+ if let node = node as? Document {
159+ checkVisitorFnArgs ( ast, node, key, parent, path, ancestors, isEdited: true )
160+ var newDefinitions = node. definitions
161+ newDefinitions. append (
162+ DirectiveDefinition (
163+ name: . init( value: " leave " ) ,
164+ locations: [ . init( value: " root " ) ]
165+ )
166+ )
167+ node. set (
168+ value: . array( newDefinitions) ,
169+ key: " definitions "
170+ )
171+ return . node( node)
172+ }
173+ return . continue
174+ }
175+ ) )
176+
177+ let editedAST = try XCTUnwrap ( editedASTNode as? Document )
178+ XCTAssertEqual ( editedAST. definitions. count, 3 )
179+ try XCTAssertEqual (
180+ XCTUnwrap ( editedAST. definitions [ 1 ] as? DirectiveDefinition ) . name. value,
181+ " enter "
182+ )
183+ try XCTAssertEqual (
184+ XCTUnwrap ( editedAST. definitions [ 2 ] as? DirectiveDefinition ) . name. value,
185+ " leave "
186+ )
187+ }
188+
189+ func testAllowsForEditingOnEnter( ) throws {
190+ let ast = try parse ( source: " { a, b, c { a, b, c } } " , noLocation: true )
191+
192+ let editedASTNode = visit ( root: ast, visitor: . init(
193+ enter: { node, key, parent, path, ancestors in
194+ if let node = node as? Field {
195+ checkVisitorFnArgs ( ast, node, key, parent, path, ancestors)
196+ if node. name. value == " b " {
197+ return . node( nil )
198+ }
199+ }
200+ return . continue
201+ }
202+ ) )
203+
204+ let editedAST = try XCTUnwrap ( editedASTNode as? Document )
205+ let operation = try XCTUnwrap ( editedAST. definitions [ 0 ] as? OperationDefinition )
206+ XCTAssertEqual (
207+ operation. selectionSet. selections. count,
208+ 2 // "b" is ignored
209+ )
210+
211+ let cField = try XCTUnwrap ( operation. selectionSet. selections [ 1 ] as? Field )
212+ XCTAssertEqual (
213+ cField. selectionSet? . selections. count,
214+ 2 // "b" is ignored
215+ )
216+ }
217+
218+ func testAllowsForEditingOnLeave( ) throws {
219+ let ast = try parse ( source: " { a, b, c { a, b, c } } " , noLocation: true )
220+
221+ let editedASTNode = visit ( root: ast, visitor: . init(
222+ leave: { node, key, parent, path, ancestors in
223+ if let node = node as? Field {
224+ checkVisitorFnArgs ( ast, node, key, parent, path, ancestors)
225+ if node. name. value == " b " {
226+ return . node( nil )
227+ }
228+ }
229+ return . continue
230+ }
231+ ) )
232+
233+ let editedAST = try XCTUnwrap ( editedASTNode as? Document )
234+ let operation = try XCTUnwrap ( editedAST. definitions [ 0 ] as? OperationDefinition )
235+ XCTAssertEqual (
236+ operation. selectionSet. selections. count,
237+ 2 // "b" is removed
238+ )
239+
240+ let cField = try XCTUnwrap ( operation. selectionSet. selections [ 1 ] as? Field )
241+ XCTAssertEqual (
242+ cField. selectionSet? . selections. count,
243+ 2 // "b" is removed
244+ )
245+ }
246+
247+ func testIgnoresSkipReturnedOnLeave( ) throws {
248+ let ast = try parse ( source: " { a, b, c { a, b, c } } " , noLocation: true )
249+
250+ let editedASTNode = visit ( root: ast, visitor: . init(
251+ leave: { _, _, _, _, _ in
252+ . skip // graphql-js 'false' is Swift '.skip'
253+ }
254+ ) )
255+
256+ let editedAST = try XCTUnwrap ( editedASTNode as? Document )
257+ let operation = try XCTUnwrap ( editedAST. definitions [ 0 ] as? OperationDefinition )
258+ XCTAssertEqual (
259+ operation. selectionSet. selections. count,
260+ 3 // "b" remains
261+ )
262+
263+ let cField = try XCTUnwrap ( operation. selectionSet. selections [ 2 ] as? Field )
264+ XCTAssertEqual (
265+ cField. selectionSet? . selections. count,
266+ 3 // "b" remains
267+ )
268+ }
269+
270+ func testVisitsEditedNode( ) throws {
271+ let addedField = Field (
272+ name: Name ( value: " __typename " )
273+ )
274+
275+ var didVisitAddedField = false
276+
277+ let ast = try parse ( source: " { a { x } } " , noLocation: true )
278+ visit ( root: ast, visitor: . init(
279+ enter: { node, key, parent, path, ancestors in
280+ checkVisitorFnArgs ( ast, node, key, parent, path, ancestors, isEdited: true )
281+ if let node = node as? Field , node. name. value == " a " {
282+ if let selectionSet = node. selectionSet {
283+ var newSelections = selectionSet. selections
284+ newSelections. append ( addedField)
285+
286+ let newSelectionSet = selectionSet
287+ newSelectionSet. set ( value: . array( newSelections) , key: " selections " )
288+
289+ let newNode = node
290+ newNode. set ( value: . node( newSelectionSet) , key: " selectionSet " )
291+ return . node( newNode)
292+ }
293+ }
294+ if let node = node as? Field , node. name. value == " __typename " {
295+ didVisitAddedField = true
296+ }
297+ return . continue
298+ }
299+ ) )
300+
301+ XCTAssert ( didVisitAddedField)
302+ }
303+
94304 func testAllowsSkippingASubTree( ) throws {
95305 struct VisitedElement : Equatable {
96306 let direction : VisitDirection
0 commit comments