synvert-core 0.63.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/main.yml +1 -1
  3. data/.gitignore +4 -0
  4. data/CHANGELOG.md +9 -1
  5. data/Guardfile +11 -2
  6. data/README.md +74 -34
  7. data/Rakefile +15 -1
  8. data/lib/synvert/core/array_ext.rb +41 -0
  9. data/lib/synvert/core/configuration.rb +12 -0
  10. data/lib/synvert/core/engine/erb.rb +9 -8
  11. data/lib/synvert/core/exceptions.rb +0 -4
  12. data/lib/synvert/core/node_ext.rb +232 -128
  13. data/lib/synvert/core/node_query/compiler/array.rb +34 -0
  14. data/lib/synvert/core/node_query/compiler/attribute.rb +51 -0
  15. data/lib/synvert/core/node_query/compiler/attribute_list.rb +24 -0
  16. data/lib/synvert/core/node_query/compiler/boolean.rb +23 -0
  17. data/lib/synvert/core/node_query/compiler/comparable.rb +79 -0
  18. data/lib/synvert/core/node_query/compiler/dynamic_attribute.rb +51 -0
  19. data/lib/synvert/core/node_query/compiler/expression.rb +88 -0
  20. data/lib/synvert/core/node_query/compiler/float.rb +23 -0
  21. data/lib/synvert/core/node_query/compiler/identifier.rb +41 -0
  22. data/lib/synvert/core/node_query/compiler/integer.rb +23 -0
  23. data/lib/synvert/core/node_query/compiler/invalid_operator_error.rb +7 -0
  24. data/lib/synvert/core/node_query/compiler/nil.rb +23 -0
  25. data/lib/synvert/core/node_query/compiler/parse_error.rb +7 -0
  26. data/lib/synvert/core/node_query/compiler/regexp.rb +37 -0
  27. data/lib/synvert/core/node_query/compiler/selector.rb +51 -0
  28. data/lib/synvert/core/node_query/compiler/string.rb +34 -0
  29. data/lib/synvert/core/node_query/compiler/symbol.rb +23 -0
  30. data/lib/synvert/core/node_query/compiler.rb +24 -0
  31. data/lib/synvert/core/node_query/lexer.rex +96 -0
  32. data/lib/synvert/core/node_query/lexer.rex.rb +293 -0
  33. data/lib/synvert/core/node_query/parser.racc.rb +518 -0
  34. data/lib/synvert/core/node_query/parser.y +84 -0
  35. data/lib/synvert/core/node_query.rb +36 -0
  36. data/lib/synvert/core/rewriter/action/append_action.rb +4 -3
  37. data/lib/synvert/core/rewriter/action/delete_action.rb +17 -8
  38. data/lib/synvert/core/rewriter/action/insert_action.rb +16 -7
  39. data/lib/synvert/core/rewriter/action/insert_after_action.rb +3 -2
  40. data/lib/synvert/core/rewriter/action/prepend_action.rb +3 -2
  41. data/lib/synvert/core/rewriter/action/remove_action.rb +16 -10
  42. data/lib/synvert/core/rewriter/action/replace_action.rb +15 -5
  43. data/lib/synvert/core/rewriter/action/replace_erb_stmt_with_expr_action.rb +18 -11
  44. data/lib/synvert/core/rewriter/action/replace_with_action.rb +6 -5
  45. data/lib/synvert/core/rewriter/action/wrap_action.rb +16 -7
  46. data/lib/synvert/core/rewriter/action.rb +22 -10
  47. data/lib/synvert/core/rewriter/any_value.rb +1 -0
  48. data/lib/synvert/core/rewriter/condition/if_exist_condition.rb +4 -0
  49. data/lib/synvert/core/rewriter/condition/if_only_exist_condition.rb +4 -0
  50. data/lib/synvert/core/rewriter/condition/unless_exist_condition.rb +4 -0
  51. data/lib/synvert/core/rewriter/condition.rb +11 -3
  52. data/lib/synvert/core/rewriter/gem_spec.rb +6 -3
  53. data/lib/synvert/core/rewriter/helper.rb +7 -4
  54. data/lib/synvert/core/rewriter/instance.rb +217 -104
  55. data/lib/synvert/core/rewriter/ruby_version.rb +4 -4
  56. data/lib/synvert/core/rewriter/scope/goto_scope.rb +5 -6
  57. data/lib/synvert/core/rewriter/scope/query_scope.rb +36 -0
  58. data/lib/synvert/core/rewriter/scope/within_scope.rb +10 -5
  59. data/lib/synvert/core/rewriter/scope.rb +8 -0
  60. data/lib/synvert/core/rewriter/warning.rb +1 -1
  61. data/lib/synvert/core/rewriter.rb +91 -43
  62. data/lib/synvert/core/version.rb +1 -1
  63. data/lib/synvert/core.rb +22 -6
  64. data/spec/synvert/core/engine/erb_spec.rb +2 -2
  65. data/spec/synvert/core/node_ext_spec.rb +36 -12
  66. data/spec/synvert/core/node_query/lexer_spec.rb +525 -0
  67. data/spec/synvert/core/node_query/parser_spec.rb +270 -0
  68. data/spec/synvert/core/rewriter/action_spec.rb +0 -4
  69. data/spec/synvert/core/rewriter/condition/if_only_exist_condition_spec.rb +1 -6
  70. data/spec/synvert/core/rewriter/gem_spec_spec.rb +1 -1
  71. data/spec/synvert/core/rewriter/helper_spec.rb +4 -1
  72. data/spec/synvert/core/rewriter/instance_spec.rb +31 -20
  73. data/spec/synvert/core/rewriter/scope/query_scope_spec.rb +74 -0
  74. data/spec/synvert/core/rewriter/scope/within_scope_spec.rb +12 -9
  75. data/spec/synvert/core/rewriter_spec.rb +4 -2
  76. data/synvert-core-ruby.gemspec +7 -2
  77. metadata +91 -4
@@ -0,0 +1,525 @@
1
+ require 'spec_helper'
2
+ require 'oedipus_lex'
3
+
4
+ module Synvert::Core::NodeQuery
5
+ RSpec.describe Lexer do
6
+ let(:lexer) { described_class.new }
7
+
8
+ def assert_tokens(source, expected_tokens)
9
+ lexer.parse(source)
10
+ tokens = []
11
+ while token = lexer.next_token
12
+ tokens << token
13
+ end
14
+ expect(tokens).to eq expected_tokens
15
+ end
16
+
17
+ context 'ast node type' do
18
+ it 'matches node type' do
19
+ source = '.send'
20
+ expected_tokens = [[:tNODE_TYPE, "send"]]
21
+ assert_tokens source, expected_tokens
22
+ end
23
+ end
24
+
25
+ context 'attribute value' do
26
+ it 'matches =' do
27
+ source = '.send[message=create]'
28
+ expected_tokens = [
29
+ [:tNODE_TYPE, "send"],
30
+ [:tOPEN_ATTRIBUTE, "["],
31
+ [:tKEY, "message"],
32
+ [:tEQUAL, "="],
33
+ [:tIDENTIFIER_VALUE, "create"],
34
+ [:tCLOSE_ATTRIBUTE, "]"]
35
+ ]
36
+ assert_tokens source, expected_tokens
37
+ end
38
+
39
+ it 'matches nil' do
40
+ source = '.send[receiver=nil]'
41
+ expected_tokens = [
42
+ [:tNODE_TYPE, "send"],
43
+ [:tOPEN_ATTRIBUTE, "["],
44
+ [:tKEY, "receiver"],
45
+ [:tEQUAL, "="],
46
+ [:tNIL, nil],
47
+ [:tCLOSE_ATTRIBUTE, "]"]
48
+ ]
49
+ assert_tokens source, expected_tokens
50
+ end
51
+
52
+ it 'matches string' do
53
+ source = '.send[message="create"]'
54
+ expected_tokens = [
55
+ [:tNODE_TYPE, "send"],
56
+ [:tOPEN_ATTRIBUTE, "["],
57
+ [:tKEY, "message"],
58
+ [:tEQUAL, "="],
59
+ [:tSTRING, "create"],
60
+ [:tCLOSE_ATTRIBUTE, "]"]
61
+ ]
62
+ assert_tokens source, expected_tokens
63
+ end
64
+
65
+ it 'matches "[]"' do
66
+ source = '.send[message="[]"]'
67
+ expected_tokens = [
68
+ [:tNODE_TYPE, "send"],
69
+ [:tOPEN_ATTRIBUTE, "["],
70
+ [:tKEY, "message"],
71
+ [:tEQUAL, "="],
72
+ [:tSTRING, "[]"],
73
+ [:tCLOSE_ATTRIBUTE, "]"]
74
+ ]
75
+ assert_tokens source, expected_tokens
76
+ end
77
+
78
+ it 'matches symbol' do
79
+ source = '.send[message=:create]'
80
+ expected_tokens = [
81
+ [:tNODE_TYPE, "send"],
82
+ [:tOPEN_ATTRIBUTE, "["],
83
+ [:tKEY, "message"],
84
+ [:tEQUAL, "="],
85
+ [:tSYMBOL, :create],
86
+ [:tCLOSE_ATTRIBUTE, "]"]
87
+ ]
88
+ assert_tokens source, expected_tokens
89
+ end
90
+
91
+ it 'matches integer' do
92
+ source = '[value=1]'
93
+ expected_tokens = [
94
+ [:tOPEN_ATTRIBUTE, "["],
95
+ [:tKEY, "value"],
96
+ [:tEQUAL, "="],
97
+ [:tINTEGER, 1],
98
+ [:tCLOSE_ATTRIBUTE, "]"]
99
+ ]
100
+ assert_tokens source, expected_tokens
101
+ end
102
+
103
+ it 'matches float' do
104
+ source = '.send[value=1.1]'
105
+ expected_tokens = [
106
+ [:tNODE_TYPE, "send"],
107
+ [:tOPEN_ATTRIBUTE, "["],
108
+ [:tKEY, "value"],
109
+ [:tEQUAL, "="],
110
+ [:tFLOAT, 1.1],
111
+ [:tCLOSE_ATTRIBUTE, "]"]
112
+ ]
113
+ assert_tokens source, expected_tokens
114
+ end
115
+
116
+ it 'matches boolean' do
117
+ source = '.send[value=true]'
118
+ expected_tokens = [
119
+ [:tNODE_TYPE, "send"],
120
+ [:tOPEN_ATTRIBUTE, "["],
121
+ [:tKEY, "value"],
122
+ [:tEQUAL, "="],
123
+ [:tBOOLEAN, true],
124
+ [:tCLOSE_ATTRIBUTE, "]"]
125
+ ]
126
+ assert_tokens source, expected_tokens
127
+ end
128
+
129
+ it 'identifier can contain !' do
130
+ source = '.send[message=create!]'
131
+ expected_tokens = [
132
+ [:tNODE_TYPE, "send"],
133
+ [:tOPEN_ATTRIBUTE, "["],
134
+ [:tKEY, "message"],
135
+ [:tEQUAL, "="],
136
+ [:tIDENTIFIER_VALUE, "create!"],
137
+ [:tCLOSE_ATTRIBUTE, "]"]
138
+ ]
139
+ assert_tokens source, expected_tokens
140
+ end
141
+
142
+ it 'identifier can contain ?' do
143
+ source = '.send[message=empty?]'
144
+ expected_tokens = [
145
+ [:tNODE_TYPE, "send"],
146
+ [:tOPEN_ATTRIBUTE, "["],
147
+ [:tKEY, "message"],
148
+ [:tEQUAL, "="],
149
+ [:tIDENTIFIER_VALUE, "empty?"],
150
+ [:tCLOSE_ATTRIBUTE, "]"]
151
+ ]
152
+ assert_tokens source, expected_tokens
153
+ end
154
+
155
+ it 'matches attribute value' do
156
+ source = '.pair[key={{value}}]'
157
+ expected_tokens = [
158
+ [:tNODE_TYPE, "pair"],
159
+ [:tOPEN_ATTRIBUTE, "["],
160
+ [:tKEY, "key"],
161
+ [:tEQUAL, "="],
162
+ [:tOPEN_DYNAMIC_ATTRIBUTE, "{{"],
163
+ [:tDYNAMIC_ATTRIBUTE, "value"],
164
+ [:tCLOSE_DYNAMIC_ATTRIBUTE, "}}"],
165
+ [:tCLOSE_ATTRIBUTE, "]"]
166
+ ]
167
+ assert_tokens source, expected_tokens
168
+ end
169
+
170
+ it 'matches nested value' do
171
+ source = <<~EOS
172
+ .send[
173
+ receiver=
174
+ .send[message=:create]
175
+ ]
176
+ EOS
177
+ expected_tokens = [
178
+ [:tNODE_TYPE, "send"],
179
+ [:tOPEN_ATTRIBUTE, "["],
180
+ [:tKEY, "receiver"],
181
+ [:tEQUAL, "="],
182
+ [:tNODE_TYPE, "send"],
183
+ [:tOPEN_ATTRIBUTE, "["],
184
+ [:tKEY, "message"],
185
+ [:tEQUAL, "="],
186
+ [:tSYMBOL, :create],
187
+ [:tCLOSE_ATTRIBUTE, "]"],
188
+ [:tCLOSE_ATTRIBUTE, "]"]
189
+ ]
190
+ assert_tokens source, expected_tokens
191
+ end
192
+
193
+ it 'matches deep nested value' do
194
+ source = <<~EOS
195
+ .send[
196
+ arguments=[size=2][first=.str][last=.str]
197
+ ]
198
+ EOS
199
+ expected_tokens = [
200
+ [:tNODE_TYPE, "send"],
201
+ [:tOPEN_ATTRIBUTE, "["],
202
+ [:tKEY, "arguments"],
203
+ [:tEQUAL, "="],
204
+ [:tOPEN_ATTRIBUTE, "["],
205
+ [:tKEY, "size"],
206
+ [:tEQUAL, "="],
207
+ [:tINTEGER, 2],
208
+ [:tCLOSE_ATTRIBUTE, "]"],
209
+ [:tOPEN_ATTRIBUTE, "["],
210
+ [:tKEY, "first"],
211
+ [:tEQUAL, "="],
212
+ [:tNODE_TYPE, "str"],
213
+ [:tCLOSE_ATTRIBUTE, "]"],
214
+ [:tOPEN_ATTRIBUTE, "["],
215
+ [:tKEY, "last"],
216
+ [:tEQUAL, "="],
217
+ [:tNODE_TYPE, "str"],
218
+ [:tCLOSE_ATTRIBUTE, "]"],
219
+ [:tCLOSE_ATTRIBUTE, "]"]
220
+ ]
221
+ assert_tokens source, expected_tokens
222
+ end
223
+ end
224
+
225
+ context 'attribute condition' do
226
+ it 'matches !=' do
227
+ source = '.send[message != create]'
228
+ expected_tokens = [
229
+ [:tNODE_TYPE, "send"],
230
+ [:tOPEN_ATTRIBUTE, "["],
231
+ [:tKEY, "message"],
232
+ [:tNOT_EQUAL, "!="],
233
+ [:tIDENTIFIER_VALUE, "create"],
234
+ [:tCLOSE_ATTRIBUTE, "]"]
235
+ ]
236
+ assert_tokens source, expected_tokens
237
+ end
238
+
239
+ it 'matches >' do
240
+ source = '[value > 1]'
241
+ expected_tokens = [
242
+ [:tOPEN_ATTRIBUTE, "["],
243
+ [:tKEY, "value"],
244
+ [:tGREATER_THAN, ">"],
245
+ [:tINTEGER, 1],
246
+ [:tCLOSE_ATTRIBUTE, "]"]
247
+ ]
248
+ assert_tokens source, expected_tokens
249
+ end
250
+
251
+ it 'matches <' do
252
+ source = '[value < 1]'
253
+ expected_tokens = [
254
+ [:tOPEN_ATTRIBUTE, "["],
255
+ [:tKEY, "value"],
256
+ [:tLESS_THAN, "<"],
257
+ [:tINTEGER, 1],
258
+ [:tCLOSE_ATTRIBUTE, "]"]
259
+ ]
260
+ assert_tokens source, expected_tokens
261
+ end
262
+
263
+ it 'matches >=' do
264
+ source = '[value >= 1]'
265
+ expected_tokens = [
266
+ [:tOPEN_ATTRIBUTE, "["],
267
+ [:tKEY, "value"],
268
+ [:tGREATER_THAN_OR_EQUAL, ">="],
269
+ [:tINTEGER, 1],
270
+ [:tCLOSE_ATTRIBUTE, "]"]
271
+ ]
272
+ assert_tokens source, expected_tokens
273
+ end
274
+
275
+ it 'matches <=' do
276
+ source = '[value <= 1]'
277
+ expected_tokens = [
278
+ [:tOPEN_ATTRIBUTE, "["],
279
+ [:tKEY, "value"],
280
+ [:tLESS_THAN_OR_EQUAL, "<="],
281
+ [:tINTEGER, 1],
282
+ [:tCLOSE_ATTRIBUTE, "]"]
283
+ ]
284
+ assert_tokens source, expected_tokens
285
+ end
286
+
287
+ it 'matches =~' do
288
+ source = '.send[message=~/create/i]'
289
+ expected_tokens = [
290
+ [:tNODE_TYPE, "send"],
291
+ [:tOPEN_ATTRIBUTE, "["],
292
+ [:tKEY, "message"],
293
+ [:tMATCH, "=~"],
294
+ [:tREGEXP, /create/i],
295
+ [:tCLOSE_ATTRIBUTE, "]"]
296
+ ]
297
+ assert_tokens source, expected_tokens
298
+ end
299
+
300
+ it 'matches !~' do
301
+ source = '.send[message!~/create/i]'
302
+ expected_tokens = [
303
+ [:tNODE_TYPE, "send"],
304
+ [:tOPEN_ATTRIBUTE, "["],
305
+ [:tKEY, "message"],
306
+ [:tNOT_MATCH, "!~"],
307
+ [:tREGEXP, /create/i],
308
+ [:tCLOSE_ATTRIBUTE, "]"]
309
+ ]
310
+ assert_tokens source, expected_tokens
311
+ end
312
+
313
+ it 'matche empty array' do
314
+ source = '.send[arguments=()]'
315
+ expected_tokens = [
316
+ [:tNODE_TYPE, "send"],
317
+ [:tOPEN_ATTRIBUTE, "["],
318
+ [:tKEY, "arguments"],
319
+ [:tEQUAL, "="],
320
+ [:tOPEN_ARRAY, "("],
321
+ [:tCLOSE_ARRAY, ")"],
322
+ [:tCLOSE_ATTRIBUTE, "]"]
323
+ ]
324
+ assert_tokens source, expected_tokens
325
+ end
326
+
327
+ it 'matche equal array' do
328
+ source = '.send[arguments=(:create)]'
329
+ expected_tokens = [
330
+ [:tNODE_TYPE, "send"],
331
+ [:tOPEN_ATTRIBUTE, "["],
332
+ [:tKEY, "arguments"],
333
+ [:tEQUAL, "="],
334
+ [:tOPEN_ARRAY, "("],
335
+ [:tSYMBOL, :create],
336
+ [:tCLOSE_ARRAY, ")"],
337
+ [:tCLOSE_ATTRIBUTE, "]"]
338
+ ]
339
+ assert_tokens source, expected_tokens
340
+ end
341
+
342
+ it 'matche not equal array' do
343
+ source = '.send[arguments!=(:create)]'
344
+ expected_tokens = [
345
+ [:tNODE_TYPE, "send"],
346
+ [:tOPEN_ATTRIBUTE, "["],
347
+ [:tKEY, "arguments"],
348
+ [:tNOT_EQUAL, "!="],
349
+ [:tOPEN_ARRAY, "("],
350
+ [:tSYMBOL, :create],
351
+ [:tCLOSE_ARRAY, ")"],
352
+ [:tCLOSE_ATTRIBUTE, "]"]
353
+ ]
354
+ assert_tokens source, expected_tokens
355
+ end
356
+
357
+ it 'matches IN' do
358
+ source = '.send[message IN (create, build)]'
359
+ expected_tokens = [
360
+ [:tNODE_TYPE, "send"],
361
+ [:tOPEN_ATTRIBUTE, "["],
362
+ [:tKEY, "message"],
363
+ [:tIN, "IN"],
364
+ [:tOPEN_ARRAY, "("],
365
+ [:tIDENTIFIER_VALUE, "create"],
366
+ [:tCOMMA, ","],
367
+ [:tIDENTIFIER_VALUE, "build"],
368
+ [:tCLOSE_ARRAY, ")"],
369
+ [:tCLOSE_ATTRIBUTE, "]"]
370
+ ]
371
+ assert_tokens source, expected_tokens
372
+ end
373
+
374
+ it 'matches NOT IN' do
375
+ source = '.send[message NOT IN (create, build)]'
376
+ expected_tokens = [
377
+ [:tNODE_TYPE, "send"],
378
+ [:tOPEN_ATTRIBUTE, "["],
379
+ [:tKEY, "message"],
380
+ [:tNOT_IN, "NOT IN"],
381
+ [:tOPEN_ARRAY, "("],
382
+ [:tIDENTIFIER_VALUE, "create"],
383
+ [:tCOMMA, ","],
384
+ [:tIDENTIFIER_VALUE, "build"],
385
+ [:tCLOSE_ARRAY, ")"],
386
+ [:tCLOSE_ATTRIBUTE, "]"]
387
+ ]
388
+ assert_tokens source, expected_tokens
389
+ end
390
+
391
+ it 'matches INCLUDES' do
392
+ source = '.send[arguments INCLUDES &block]'
393
+ expected_tokens = [
394
+ [:tNODE_TYPE, "send"],
395
+ [:tOPEN_ATTRIBUTE, "["],
396
+ [:tKEY, "arguments"],
397
+ [:tINCLUDES, "INCLUDES"],
398
+ [:tIDENTIFIER_VALUE, "&block"],
399
+ [:tCLOSE_ATTRIBUTE, "]"]
400
+ ]
401
+ assert_tokens source, expected_tokens
402
+ end
403
+ end
404
+
405
+ context 'nested attribute' do
406
+ it 'matches' do
407
+ source = '.send[receiver.message=:create]'
408
+ expected_tokens = [
409
+ [:tNODE_TYPE, "send"],
410
+ [:tOPEN_ATTRIBUTE, "["],
411
+ [:tKEY, "receiver.message"],
412
+ [:tEQUAL, "="],
413
+ [:tSYMBOL, :create],
414
+ [:tCLOSE_ATTRIBUTE, "]"]
415
+ ]
416
+ assert_tokens source, expected_tokens
417
+ end
418
+ end
419
+
420
+ context 'position' do
421
+ it 'matches :first-child' do
422
+ source = '.send[arguments=:create]:first-child'
423
+ expected_tokens = [
424
+ [:tNODE_TYPE, "send"],
425
+ [:tOPEN_ATTRIBUTE, "["],
426
+ [:tKEY, "arguments"],
427
+ [:tEQUAL, "="],
428
+ [:tSYMBOL, :create],
429
+ [:tCLOSE_ATTRIBUTE, "]"],
430
+ [:tINDEX, 0]
431
+ ]
432
+ assert_tokens source, expected_tokens
433
+ end
434
+
435
+ it 'matches :last-child' do
436
+ source = '.send[arguments=:create]:last-child'
437
+ expected_tokens = [
438
+ [:tNODE_TYPE, "send"],
439
+ [:tOPEN_ATTRIBUTE, "["],
440
+ [:tKEY, "arguments"],
441
+ [:tEQUAL, "="],
442
+ [:tSYMBOL, :create],
443
+ [:tCLOSE_ATTRIBUTE, "]"],
444
+ [:tINDEX, -1]
445
+ ]
446
+ assert_tokens source, expected_tokens
447
+ end
448
+
449
+ it 'matches :nth-child(1)' do
450
+ source = '.send[arguments=:create]:nth-child(1)'
451
+ expected_tokens = [
452
+ [:tNODE_TYPE, "send"],
453
+ [:tOPEN_ATTRIBUTE, "["],
454
+ [:tKEY, "arguments"],
455
+ [:tEQUAL, "="],
456
+ [:tSYMBOL, :create],
457
+ [:tCLOSE_ATTRIBUTE, "]"],
458
+ [:tINDEX, 0]
459
+ ]
460
+ assert_tokens source, expected_tokens
461
+ end
462
+
463
+ it 'matches :nth-last-child(1)' do
464
+ source = '.send[arguments=:create]:nth-last-child(1)'
465
+ expected_tokens = [
466
+ [:tNODE_TYPE, "send"],
467
+ [:tOPEN_ATTRIBUTE, "["],
468
+ [:tKEY, "arguments"],
469
+ [:tEQUAL, "="],
470
+ [:tSYMBOL, :create],
471
+ [:tCLOSE_ATTRIBUTE, "]"],
472
+ [:tINDEX, -1]
473
+ ]
474
+ assert_tokens source, expected_tokens
475
+ end
476
+ end
477
+
478
+ context 'descendant' do
479
+ it 'matches' do
480
+ source = '.class .send'
481
+ expected_tokens = [[:tNODE_TYPE, "class"], [:tNODE_TYPE, "send"]]
482
+ assert_tokens source, expected_tokens
483
+ end
484
+ end
485
+
486
+ context 'child' do
487
+ it 'matches' do
488
+ source = '.def > .send'
489
+ expected_tokens = [[:tNODE_TYPE, "def"], [:tCHILD, ">"], [:tNODE_TYPE, "send"]]
490
+ assert_tokens source, expected_tokens
491
+ end
492
+ end
493
+
494
+ context 'subsequent sibling' do
495
+ it 'matches' do
496
+ source = '.send ~ .send'
497
+ expected_tokens = [[:tNODE_TYPE, "send"], [:tSUBSEQUENT_SIBLING, "~"], [:tNODE_TYPE, "send"]]
498
+ assert_tokens source, expected_tokens
499
+ end
500
+ end
501
+
502
+ context 'next sibling' do
503
+ it 'matches' do
504
+ source = '.send + .send'
505
+ expected_tokens = [[:tNODE_TYPE, "send"], [:tNEXT_SIBLING, "+"], [:tNODE_TYPE, "send"]]
506
+ assert_tokens source, expected_tokens
507
+ end
508
+ end
509
+
510
+ context ':has' do
511
+ it 'matches' do
512
+ source = '.class:has(> .def)'
513
+ expected_tokens = [
514
+ [:tNODE_TYPE, "class"],
515
+ [:tHAS, "has"],
516
+ [:tOPEN_SELECTOR, "("],
517
+ [:tCHILD, ">"],
518
+ [:tNODE_TYPE, "def"],
519
+ [:tCLOSE_SELECTOR, ")"]
520
+ ]
521
+ assert_tokens source, expected_tokens
522
+ end
523
+ end
524
+ end
525
+ end