transpec 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +36 -16
- data/README.md.erb +36 -16
- data/lib/transpec/option_parser.rb +1 -1
- data/lib/transpec/syntax/have.rb +126 -39
- data/lib/transpec/syntax/its.rb +15 -2
- data/lib/transpec/syntax/method_stub.rb +4 -4
- data/lib/transpec/syntax/mixin/any_instance.rb +43 -4
- data/lib/transpec/syntax/mixin/send.rb +4 -0
- data/lib/transpec/syntax/should_receive.rb +9 -9
- data/lib/transpec/version.rb +2 -2
- data/spec/transpec/cli_spec.rb +2 -2
- data/spec/transpec/converter_spec.rb +2 -2
- data/spec/transpec/static_context_inspector_spec.rb +5 -5
- data/spec/transpec/syntax/have_spec.rb +355 -0
- data/spec/transpec/syntax/its_spec.rb +49 -24
- data/spec/transpec/syntax/method_stub_spec.rb +467 -323
- data/spec/transpec/syntax/should_receive_spec.rb +212 -24
- data/transpec.gemspec +1 -0
- metadata +15 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c9cd93b43c3629f7c2f09782ee9616100b5147b8
|
4
|
+
data.tar.gz: 663a9de354fa930e147853a2024da3f904afae5a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94f4f9b4a08a5d8ca703b9588e3bdda7a63eef38dc3b67b21de62a87c9f60ea09f560151ed08e68994281e0361ce7d2b344635843380c410c053c9f3c14f9f37
|
7
|
+
data.tar.gz: 65afbbd2ddd3f72b9794d95ed261c6d7f6c4f7f54ea02e415eee927c36c1fa95e6a2a71f8f60cce3e83bb26a65e52dea067a81db6d6352cf09117f4bc512e515
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,15 @@
|
|
2
2
|
|
3
3
|
## Development
|
4
4
|
|
5
|
+
## v1.3.0
|
6
|
+
|
7
|
+
* Handle singular collection names like `have(n).item` ([#18](https://github.com/yujinakayama/transpec/issues/18))
|
8
|
+
* Handle collection accessors with arguments like `have(n).errors_on(...)` ([#18](https://github.com/yujinakayama/transpec/issues/18))
|
9
|
+
* Handle `described_class.any_instance` ([#18](https://github.com/yujinakayama/transpec/issues/18))
|
10
|
+
* Handle indirect `any_instance` subject with runtime information (e.g. `variable = SomeClass.any_instance; variable.stub(:message)`)
|
11
|
+
* Disable conversion of `have(n).items` automatically if `rspec-rails` or `rspec-collection_matchers` is loaded in the target project
|
12
|
+
* Disable conversion of `its` automatically if `rspec-its` is loaded in the target project
|
13
|
+
|
5
14
|
## v1.2.2
|
6
15
|
|
7
16
|
* Fix error `singleton can't be dumped (TypeError)` at the end of dynamic analysis ([#17](https://github.com/yujinakayama/transpec/issues/17))
|
data/README.md
CHANGED
@@ -117,11 +117,11 @@ Before converting your specs:
|
|
117
117
|
* Run `rspec` and check if all the specs pass.
|
118
118
|
* Ensure the Git repository is clean. (You don't want to mix up your changes and Transpec's changes, right?)
|
119
119
|
|
120
|
-
Then, run `transpec` (using
|
120
|
+
Then, run `transpec` (using `-m/--generate-commit-message` is recommended) in the project root directory:
|
121
121
|
|
122
122
|
```bash
|
123
123
|
$ cd some-project
|
124
|
-
$ transpec
|
124
|
+
$ transpec -m
|
125
125
|
Copying project for dynamic analysis...
|
126
126
|
Running dynamic analysis with command "bundle exec rspec"...
|
127
127
|
...............................................................................
|
@@ -214,12 +214,12 @@ $ transpec --keep should_receive,stub
|
|
214
214
|
#### Available syntax types
|
215
215
|
|
216
216
|
Type | Target Syntax | Converted Syntax
|
217
|
-
|
217
|
+
-----------------|----------------------------------|-----------------------------------
|
218
218
|
`should` | `obj.should matcher` | `expect(obj).to matcher`
|
219
219
|
`should_receive` | `obj.should_receive` | `expect(obj).to receive`
|
220
220
|
`stub` | `obj.stub` | `allow(obj).to receive`
|
221
221
|
`have_items` | `expect(obj).to have(x).items` | `expect(obj.size).to eq(x)`
|
222
|
-
`its` | `its(:attr) { }` | `describe { subject { } it { } }`
|
222
|
+
`its` | `its(:attr) { }` | `describe { subject { }; it { } }`
|
223
223
|
`deprecated` | `obj.stub!`, `mock('foo')`, etc. | `obj.stub`, `double('foo')`
|
224
224
|
|
225
225
|
See [Supported Conversions](#supported-conversions) for more details.
|
@@ -409,6 +409,8 @@ expect(obj).to be false
|
|
409
409
|
|
410
410
|
So, converting `be_true`/`be_false` to `be_truthy`/`be_falsey` never breaks your specs and this is the Transpec's default. If you are willing to test boolean values strictly, you can convert them to `be true`/`be false` with `--boolean-matcher true,false` option. Note that this may break your specs if your library codes don't return exact boolean values.
|
411
411
|
|
412
|
+
---
|
413
|
+
|
412
414
|
* Conversion can be disabled by: `--keep deprecated`
|
413
415
|
* Deprecation: Deprecated since RSpec 2.99, removed at RSpec 3.0
|
414
416
|
* See also: [Consider renaming `be_true` and `be_false` to `be_truthy` and `be_falsey` · rspec/rspec-expectations](https://github.com/rspec/rspec-expectations/issues/283)
|
@@ -429,6 +431,8 @@ expect(1.0 / 3.0).to be_within(0.001).of(0.333)
|
|
429
431
|
|
430
432
|
### `have(n).items` matcher
|
431
433
|
|
434
|
+
**This conversion will be disabled automatically if `rspec-collection_matchers` or `rspec-rails` is loaded in your spec.**
|
435
|
+
|
432
436
|
```ruby
|
433
437
|
# Targets
|
434
438
|
expect(collection).to have(3).items
|
@@ -455,8 +459,15 @@ expect(team.players.size).to eq(3)
|
|
455
459
|
expect(team.send(:players).size).to eq(3)
|
456
460
|
```
|
457
461
|
|
458
|
-
There's
|
459
|
-
If you choose so, disable this conversion
|
462
|
+
There's an option to continue using `have(n).items` matcher with [rspec-collection_matchers](https://github.com/rspec/rspec-collection_matchers) that is an external gem extracted from `rspec-expectations`.
|
463
|
+
If you choose so, disable this conversion by either:
|
464
|
+
|
465
|
+
* Specify `--keep have_items` option manually.
|
466
|
+
* Require `rspec-collection_matchers` or `rspec-rails` in your spec so that Transpec automatically disables this conversion.
|
467
|
+
|
468
|
+
Note: `rspec-rails` 3.0 [still uses `have(n).items` matcher with `rspec-collection_matchers`](https://github.com/rspec/rspec-rails/blob/v3.0.0.beta1/rspec-rails.gemspec#L41).
|
469
|
+
|
470
|
+
---
|
460
471
|
|
461
472
|
* Conversion can be disabled by: `--keep have_items`
|
462
473
|
* Deprecation: Deprecated since RSpec 2.99, removed at RSpec 3.0
|
@@ -501,11 +512,11 @@ lambda { do_something }.should_not raise_error # with `--keep should`
|
|
501
512
|
```ruby
|
502
513
|
# Targets
|
503
514
|
obj.should_receive(:foo)
|
504
|
-
|
515
|
+
Klass.any_instance.should_receive(:foo)
|
505
516
|
|
506
517
|
# Converted
|
507
518
|
expect(obj).to receive(:foo)
|
508
|
-
expect_any_instance_of(
|
519
|
+
expect_any_instance_of(Klass).to receive(:foo)
|
509
520
|
```
|
510
521
|
|
511
522
|
* Conversion can be disabled by: `--keep should_receive`
|
@@ -519,15 +530,15 @@ expect_any_instance_of(SomeClass).to receive(:foo)
|
|
519
530
|
obj.should_receive(:foo).any_number_of_times
|
520
531
|
obj.should_receive(:foo).at_least(0)
|
521
532
|
|
522
|
-
|
523
|
-
|
533
|
+
Klass.any_instance.should_receive(:foo).any_number_of_times
|
534
|
+
Klass.any_instance.should_receive(:foo).at_least(0)
|
524
535
|
|
525
536
|
# Converted
|
526
537
|
allow(obj).to receive(:foo)
|
527
538
|
obj.stub(:foo) # with `--keep stub`
|
528
539
|
|
529
|
-
allow_any_instance_of(
|
530
|
-
|
540
|
+
allow_any_instance_of(Klass).to receive(:foo)
|
541
|
+
Klass.any_instance.stub(:foo) # with `--keep stub`
|
531
542
|
```
|
532
543
|
|
533
544
|
* Conversion can be disabled by: `--keep deprecated`
|
@@ -544,7 +555,7 @@ obj.stub!(:foo)
|
|
544
555
|
|
545
556
|
obj.stub(:foo => 1, :bar => 2)
|
546
557
|
|
547
|
-
|
558
|
+
Klass.any_instance.stub(:foo)
|
548
559
|
|
549
560
|
# Converted
|
550
561
|
allow(obj).to receive(:foo)
|
@@ -558,7 +569,7 @@ allow(obj).to receive(:bar).and_return(2)
|
|
558
569
|
# If the target project's rspec gem dependency is 3.0 or later
|
559
570
|
allow(obj).to receive_messages(:foo => 1, :bar => 2)
|
560
571
|
|
561
|
-
allow_any_instance_of(
|
572
|
+
allow_any_instance_of(Klass).to receive(:foo)
|
562
573
|
```
|
563
574
|
|
564
575
|
Note: `allow(obj).to receive_messages(:foo => 1, :bar => 2)` that is designed to be the replacement for `obj.stub(:foo => 1, :bar => 2)` is available from RSpec 3.0 (though [it's now being considered to be backported to RSpec 2.99](https://github.com/rspec/rspec-mocks/issues/454)). So, in [the upgrade path to RSpec 3](http://myronmars.to/n/dev-blog/2013/07/the-plan-for-rspec-3#the_upgrade_path), if you want to convert them with keeping the syntax correspondence, you need to follow these steps:
|
@@ -570,6 +581,8 @@ Note: `allow(obj).to receive_messages(:foo => 1, :bar => 2)` that is designed to
|
|
570
581
|
|
571
582
|
Otherwise `obj.stub(:foo => 1, :bar => 2)` will be converted to two `allow(obj).to receive(...).and_return(...)` expressions on RSpec 2.99.
|
572
583
|
|
584
|
+
---
|
585
|
+
|
573
586
|
* Conversion can be disabled by: `--keep stub`
|
574
587
|
* Deprecation: Deprecated since RSpec 3.0
|
575
588
|
* See also:
|
@@ -625,6 +638,8 @@ double('something')
|
|
625
638
|
|
626
639
|
### Expectations on attribute of subject with `its`
|
627
640
|
|
641
|
+
**This conversion will be disabled automatically if `rspec-its` is loaded in your spec.**
|
642
|
+
|
628
643
|
```ruby
|
629
644
|
# Targets
|
630
645
|
describe 'example' do
|
@@ -658,8 +673,13 @@ describe 'example' do
|
|
658
673
|
end
|
659
674
|
```
|
660
675
|
|
661
|
-
There's
|
662
|
-
If you choose so, disable this conversion
|
676
|
+
There's an option to continue using `its` with [rspec-its](https://github.com/rspec/rspec-its) that is an external gem extracted from `rspec-core`.
|
677
|
+
If you choose so, disable this conversion by either:
|
678
|
+
|
679
|
+
* Specify `--keep its` option manually.
|
680
|
+
* Require `rspec-its` in your spec so that Transpec automatically disables this conversion.
|
681
|
+
|
682
|
+
---
|
663
683
|
|
664
684
|
* Conversion can be disabled by: `--keep its`
|
665
685
|
* Deprecation: Deprecated since RSpec 2.99, removed at RSpec 3.0
|
data/README.md.erb
CHANGED
@@ -90,11 +90,11 @@ Before converting your specs:
|
|
90
90
|
* Run `rspec` and check if all the specs pass.
|
91
91
|
* Ensure the Git repository is clean. (You don't want to mix up your changes and Transpec's changes, right?)
|
92
92
|
|
93
|
-
Then, run `transpec` (using
|
93
|
+
Then, run `transpec` (using `-m/--generate-commit-message` is recommended) in the project root directory:
|
94
94
|
|
95
95
|
```bash
|
96
96
|
$ cd some-project
|
97
|
-
$ transpec
|
97
|
+
$ transpec -m
|
98
98
|
Copying project for dynamic analysis...
|
99
99
|
Running dynamic analysis with command "bundle exec rspec"...
|
100
100
|
...............................................................................
|
@@ -187,14 +187,14 @@ $ transpec --keep should_receive,stub
|
|
187
187
|
#### Available syntax types
|
188
188
|
|
189
189
|
Type | Target Syntax | Converted Syntax
|
190
|
-
|
190
|
+
-----------------|----------------------------------|-----------------------------------
|
191
191
|
<%=
|
192
192
|
conversion_type_table = <<END
|
193
193
|
`should` | `obj.should matcher` | `expect(obj).to matcher`
|
194
194
|
`should_receive` | `obj.should_receive` | `expect(obj).to receive`
|
195
195
|
`stub` | `obj.stub` | `allow(obj).to receive`
|
196
196
|
`have_items` | `expect(obj).to have(x).items` | `expect(obj.size).to eq(x)`
|
197
|
-
`its` | `its(:attr) { }` | `describe { subject { } it { } }`
|
197
|
+
`its` | `its(:attr) { }` | `describe { subject { }; it { } }`
|
198
198
|
`deprecated` | `obj.stub!`, `mock('foo')`, etc. | `obj.stub`, `double('foo')`
|
199
199
|
END
|
200
200
|
|
@@ -405,6 +405,8 @@ expect(obj).to be false
|
|
405
405
|
|
406
406
|
So, converting `be_true`/`be_false` to `be_truthy`/`be_falsey` never breaks your specs and this is the Transpec's default. If you are willing to test boolean values strictly, you can convert them to `be true`/`be false` with `--boolean-matcher true,false` option. Note that this may break your specs if your library codes don't return exact boolean values.
|
407
407
|
|
408
|
+
---
|
409
|
+
|
408
410
|
* Conversion can be disabled by: `--keep deprecated`
|
409
411
|
* Deprecation: Deprecated since RSpec 2.99, removed at RSpec 3.0
|
410
412
|
* See also: [Consider renaming `be_true` and `be_false` to `be_truthy` and `be_falsey` · rspec/rspec-expectations](https://github.com/rspec/rspec-expectations/issues/283)
|
@@ -425,6 +427,8 @@ expect(1.0 / 3.0).to be_within(0.001).of(0.333)
|
|
425
427
|
|
426
428
|
### `have(n).items` matcher
|
427
429
|
|
430
|
+
**This conversion will be disabled automatically if `rspec-collection_matchers` or `rspec-rails` is loaded in your spec.**
|
431
|
+
|
428
432
|
```ruby
|
429
433
|
# Targets
|
430
434
|
expect(collection).to have(3).items
|
@@ -451,8 +455,15 @@ expect(team.players.size).to eq(3)
|
|
451
455
|
expect(team.send(:players).size).to eq(3)
|
452
456
|
```
|
453
457
|
|
454
|
-
There's
|
455
|
-
If you choose so, disable this conversion
|
458
|
+
There's an option to continue using `have(n).items` matcher with [rspec-collection_matchers](https://github.com/rspec/rspec-collection_matchers) that is an external gem extracted from `rspec-expectations`.
|
459
|
+
If you choose so, disable this conversion by either:
|
460
|
+
|
461
|
+
* Specify `--keep have_items` option manually.
|
462
|
+
* Require `rspec-collection_matchers` or `rspec-rails` in your spec so that Transpec automatically disables this conversion.
|
463
|
+
|
464
|
+
Note: `rspec-rails` 3.0 [still uses `have(n).items` matcher with `rspec-collection_matchers`](https://github.com/rspec/rspec-rails/blob/v3.0.0.beta1/rspec-rails.gemspec#L41).
|
465
|
+
|
466
|
+
---
|
456
467
|
|
457
468
|
* Conversion can be disabled by: `--keep have_items`
|
458
469
|
* Deprecation: Deprecated since RSpec 2.99, removed at RSpec 3.0
|
@@ -497,11 +508,11 @@ lambda { do_something }.should_not raise_error # with `--keep should`
|
|
497
508
|
```ruby
|
498
509
|
# Targets
|
499
510
|
obj.should_receive(:foo)
|
500
|
-
|
511
|
+
Klass.any_instance.should_receive(:foo)
|
501
512
|
|
502
513
|
# Converted
|
503
514
|
expect(obj).to receive(:foo)
|
504
|
-
expect_any_instance_of(
|
515
|
+
expect_any_instance_of(Klass).to receive(:foo)
|
505
516
|
```
|
506
517
|
|
507
518
|
* Conversion can be disabled by: `--keep should_receive`
|
@@ -515,15 +526,15 @@ expect_any_instance_of(SomeClass).to receive(:foo)
|
|
515
526
|
obj.should_receive(:foo).any_number_of_times
|
516
527
|
obj.should_receive(:foo).at_least(0)
|
517
528
|
|
518
|
-
|
519
|
-
|
529
|
+
Klass.any_instance.should_receive(:foo).any_number_of_times
|
530
|
+
Klass.any_instance.should_receive(:foo).at_least(0)
|
520
531
|
|
521
532
|
# Converted
|
522
533
|
allow(obj).to receive(:foo)
|
523
534
|
obj.stub(:foo) # with `--keep stub`
|
524
535
|
|
525
|
-
allow_any_instance_of(
|
526
|
-
|
536
|
+
allow_any_instance_of(Klass).to receive(:foo)
|
537
|
+
Klass.any_instance.stub(:foo) # with `--keep stub`
|
527
538
|
```
|
528
539
|
|
529
540
|
* Conversion can be disabled by: `--keep deprecated`
|
@@ -540,7 +551,7 @@ obj.stub!(:foo)
|
|
540
551
|
|
541
552
|
obj.stub(:foo => 1, :bar => 2)
|
542
553
|
|
543
|
-
|
554
|
+
Klass.any_instance.stub(:foo)
|
544
555
|
|
545
556
|
# Converted
|
546
557
|
allow(obj).to receive(:foo)
|
@@ -554,7 +565,7 @@ allow(obj).to receive(:bar).and_return(2)
|
|
554
565
|
# If the target project's rspec gem dependency is 3.0 or later
|
555
566
|
allow(obj).to receive_messages(:foo => 1, :bar => 2)
|
556
567
|
|
557
|
-
allow_any_instance_of(
|
568
|
+
allow_any_instance_of(Klass).to receive(:foo)
|
558
569
|
```
|
559
570
|
|
560
571
|
Note: `allow(obj).to receive_messages(:foo => 1, :bar => 2)` that is designed to be the replacement for `obj.stub(:foo => 1, :bar => 2)` is available from RSpec 3.0 (though [it's now being considered to be backported to RSpec 2.99](https://github.com/rspec/rspec-mocks/issues/454)). So, in [the upgrade path to RSpec 3](http://myronmars.to/n/dev-blog/2013/07/the-plan-for-rspec-3#the_upgrade_path), if you want to convert them with keeping the syntax correspondence, you need to follow these steps:
|
@@ -566,6 +577,8 @@ Note: `allow(obj).to receive_messages(:foo => 1, :bar => 2)` that is designed to
|
|
566
577
|
|
567
578
|
Otherwise `obj.stub(:foo => 1, :bar => 2)` will be converted to two `allow(obj).to receive(...).and_return(...)` expressions on RSpec 2.99.
|
568
579
|
|
580
|
+
---
|
581
|
+
|
569
582
|
* Conversion can be disabled by: `--keep stub`
|
570
583
|
* Deprecation: Deprecated since RSpec 3.0
|
571
584
|
* See also:
|
@@ -621,6 +634,8 @@ double('something')
|
|
621
634
|
|
622
635
|
### Expectations on attribute of subject with `its`
|
623
636
|
|
637
|
+
**This conversion will be disabled automatically if `rspec-its` is loaded in your spec.**
|
638
|
+
|
624
639
|
```ruby
|
625
640
|
# Targets
|
626
641
|
<%=
|
@@ -638,8 +653,13 @@ END
|
|
638
653
|
<%= Transpec::Converter.new.convert(its_target) -%>
|
639
654
|
```
|
640
655
|
|
641
|
-
There's
|
642
|
-
If you choose so, disable this conversion
|
656
|
+
There's an option to continue using `its` with [rspec-its](https://github.com/rspec/rspec-its) that is an external gem extracted from `rspec-core`.
|
657
|
+
If you choose so, disable this conversion by either:
|
658
|
+
|
659
|
+
* Specify `--keep its` option manually.
|
660
|
+
* Require `rspec-its` in your spec so that Transpec automatically disables this conversion.
|
661
|
+
|
662
|
+
---
|
643
663
|
|
644
664
|
* Conversion can be disabled by: `--keep its`
|
645
665
|
* Deprecation: Deprecated since RSpec 2.99, removed at RSpec 3.0
|
@@ -143,7 +143,7 @@ module Transpec
|
|
143
143
|
" #{'should_receive'.bright} (to #{'expect(obj).to receive'.underline})",
|
144
144
|
" #{'stub'.bright} (to #{'allow(obj).to receive'.underline})",
|
145
145
|
" #{'have_items'.bright} (to #{'expect(obj.size).to eq(x)'.underline})",
|
146
|
-
" #{'its'.bright} (to #{'describe { subject { } it { } }'.underline})",
|
146
|
+
" #{'its'.bright} (to #{'describe { subject { }; it { } }'.underline})",
|
147
147
|
" #{'deprecated'.bright} (e.g. from #{'mock'.underline} to #{'double'.underline})",
|
148
148
|
'These are all converted by default.'
|
149
149
|
],
|
data/lib/transpec/syntax/have.rb
CHANGED
@@ -39,30 +39,11 @@ module Transpec
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def register_request_for_dynamic_analysis(rewriter)
|
42
|
-
|
43
|
-
|
44
|
-
# `expect(owner).to have(n).things` invokes private owner#things with Object#__send__
|
45
|
-
# if the owner does not respond to any of #size, #count and #length.
|
46
|
-
#
|
47
|
-
# rubocop:disable LineLength
|
48
|
-
# https://github.com/rspec/rspec-expectations/blob/v2.14.3/lib/rspec/matchers/built_in/have.rb#L48-L58
|
49
|
-
# rubocop:enable LineLength
|
50
|
-
key = :subject_is_owner_of_collection?
|
51
|
-
code = "respond_to?(#{items_name.inspect}) || " +
|
52
|
-
"(methods & #{QUERY_METHOD_PRIORITIES.inspect}).empty?"
|
53
|
-
rewriter.register_request(node, key, code)
|
54
|
-
|
55
|
-
key = :available_query_methods
|
56
|
-
code = "target = #{code} ? #{items_name} : self; " +
|
57
|
-
"target.methods & #{QUERY_METHOD_PRIORITIES.inspect}"
|
58
|
-
rewriter.register_request(node, key, code)
|
59
|
-
|
60
|
-
key = :collection_accessor_is_private?
|
61
|
-
code = "private_methods.include?(#{items_name.inspect})"
|
62
|
-
rewriter.register_request(node, key, code)
|
42
|
+
DynamicInspector.register_request(self, rewriter)
|
63
43
|
end
|
64
44
|
|
65
45
|
def convert_to_standard_expectation!(parenthesize_matcher_arg = true)
|
46
|
+
return if project_requires_collection_matcher?
|
66
47
|
replace(@expectation.subject_range, replacement_subject_source)
|
67
48
|
replace(expression_range, replacement_matcher_source(size_source, parenthesize_matcher_arg))
|
68
49
|
register_record
|
@@ -78,6 +59,10 @@ module Transpec
|
|
78
59
|
|
79
60
|
alias_method :items_node, :node
|
80
61
|
|
62
|
+
def items_method_has_arguments?
|
63
|
+
items_node.children.size > 2
|
64
|
+
end
|
65
|
+
|
81
66
|
def have_method_name
|
82
67
|
have_node.children[1]
|
83
68
|
end
|
@@ -86,21 +71,31 @@ module Transpec
|
|
86
71
|
items_node.children[1]
|
87
72
|
end
|
88
73
|
|
74
|
+
def project_requires_collection_matcher?
|
75
|
+
runtime_subject_data && runtime_subject_data[:project_requires_collection_matcher?].result
|
76
|
+
end
|
77
|
+
|
78
|
+
def collection_accessor
|
79
|
+
if runtime_subject_data && runtime_subject_data[:collection_accessor].result
|
80
|
+
runtime_subject_data[:collection_accessor].result.to_sym
|
81
|
+
else
|
82
|
+
items_name
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
89
86
|
def subject_is_owner_of_collection?
|
90
|
-
|
91
|
-
|
87
|
+
return true if items_method_has_arguments?
|
88
|
+
runtime_subject_data && !runtime_subject_data[:collection_accessor].result.nil?
|
92
89
|
end
|
93
90
|
|
94
91
|
def collection_accessor_is_private?
|
95
|
-
|
96
|
-
node_data && node_data[:collection_accessor_is_private?].result
|
92
|
+
runtime_subject_data && runtime_subject_data[:collection_accessor_is_private?].result
|
97
93
|
end
|
98
94
|
|
99
95
|
def query_method
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
(QUERY_METHOD_PRIORITIES & available_query_methods).first
|
96
|
+
if runtime_subject_data && runtime_subject_data[:available_query_methods]
|
97
|
+
available_query_methods = runtime_subject_data[:available_query_methods].result
|
98
|
+
(QUERY_METHOD_PRIORITIES & available_query_methods.map(&:to_sym)).first
|
104
99
|
else
|
105
100
|
default_query_method
|
106
101
|
end
|
@@ -121,13 +116,20 @@ module Transpec
|
|
121
116
|
|
122
117
|
private
|
123
118
|
|
119
|
+
def runtime_subject_data
|
120
|
+
return @runtime_subject_data if instance_variable_defined?(:@runtime_subject_data)
|
121
|
+
@runtime_subject_data = runtime_node_data(@expectation.subject_node)
|
122
|
+
end
|
123
|
+
|
124
124
|
def replacement_subject_source
|
125
125
|
source = @expectation.subject_range.source
|
126
126
|
if subject_is_owner_of_collection?
|
127
127
|
if collection_accessor_is_private?
|
128
|
-
source << ".send(#{
|
128
|
+
source << ".send(#{collection_accessor.inspect}"
|
129
|
+
source << ", #{args_range.source}" if items_method_has_arguments?
|
130
|
+
source << ')'
|
129
131
|
else
|
130
|
-
source << ".#{
|
132
|
+
source << ".#{collection_accessor}#{parentheses_range.source}"
|
131
133
|
end
|
132
134
|
end
|
133
135
|
source << ".#{query_method}"
|
@@ -160,15 +162,91 @@ module Transpec
|
|
160
162
|
size_node.loc.expression.source
|
161
163
|
end
|
162
164
|
|
163
|
-
def dot_items_range
|
164
|
-
map = items_node.loc
|
165
|
-
map.dot.join(map.selector)
|
166
|
-
end
|
167
|
-
|
168
165
|
def register_record
|
169
166
|
@report.records << HaveRecord.new(self)
|
170
167
|
end
|
171
168
|
|
169
|
+
class DynamicInspector
|
170
|
+
def self.register_request(have, rewriter)
|
171
|
+
new(have, rewriter).register_request
|
172
|
+
end
|
173
|
+
|
174
|
+
def initialize(have, rewriter)
|
175
|
+
@have = have
|
176
|
+
@rewriter = rewriter
|
177
|
+
end
|
178
|
+
|
179
|
+
def target_node
|
180
|
+
@have.expectation.subject_node
|
181
|
+
end
|
182
|
+
|
183
|
+
def register_request
|
184
|
+
key = :collection_accessor
|
185
|
+
code = collection_accessor_inspection_code
|
186
|
+
@rewriter.register_request(target_node, key, code)
|
187
|
+
|
188
|
+
# Give up inspecting query methods of collection accessor with arguments
|
189
|
+
# (e.g. have(2).errors_on(variable)) since this is a context of #instance_eval.
|
190
|
+
unless @have.items_method_has_arguments?
|
191
|
+
key = :available_query_methods
|
192
|
+
code = "collection_accessor = #{code}; " +
|
193
|
+
'target = collection_accessor ? __send__(collection_accessor) : self; ' +
|
194
|
+
"target.methods & #{QUERY_METHOD_PRIORITIES.inspect}"
|
195
|
+
@rewriter.register_request(target_node, key, code)
|
196
|
+
end
|
197
|
+
|
198
|
+
key = :collection_accessor_is_private?
|
199
|
+
code = "private_methods.include?(#{@have.items_name.inspect})"
|
200
|
+
@rewriter.register_request(target_node, key, code)
|
201
|
+
|
202
|
+
key = :project_requires_collection_matcher?
|
203
|
+
code = 'defined?(RSpec::Rails) || defined?(RSpec::CollectionMatchers)'
|
204
|
+
@rewriter.register_request(target_node, key, code, :context)
|
205
|
+
end
|
206
|
+
|
207
|
+
# rubocop:disable MethodLength
|
208
|
+
def collection_accessor_inspection_code
|
209
|
+
# `expect(owner).to have(n).things` invokes private owner#things with Object#__send__
|
210
|
+
# if the owner does not respond to any of #size, #count and #length.
|
211
|
+
#
|
212
|
+
# rubocop:disable LineLength
|
213
|
+
# https://github.com/rspec/rspec-expectations/blob/v2.14.3/lib/rspec/matchers/built_in/have.rb#L48-L58
|
214
|
+
# rubocop:enable LineLength
|
215
|
+
<<-END.gsub(/^\s+\|/, '').chomp
|
216
|
+
|begin
|
217
|
+
| exact_name = #{@have.items_name.inspect}
|
218
|
+
|
|
219
|
+
| inflector = if defined?(ActiveSupport::Inflector) &&
|
220
|
+
| ActiveSupport::Inflector.respond_to?(:pluralize)
|
221
|
+
| ActiveSupport::Inflector
|
222
|
+
| elsif defined?(Inflector)
|
223
|
+
| Inflector
|
224
|
+
| else
|
225
|
+
| nil
|
226
|
+
| end
|
227
|
+
|
|
228
|
+
| if inflector
|
229
|
+
| pluralized_name = inflector.pluralize(exact_name).to_sym
|
230
|
+
| respond_to_pluralized_name = respond_to?(pluralized_name)
|
231
|
+
| end
|
232
|
+
|
|
233
|
+
| respond_to_query_methods = !(methods & #{QUERY_METHOD_PRIORITIES.inspect}).empty?
|
234
|
+
|
|
235
|
+
| if respond_to?(exact_name)
|
236
|
+
| exact_name
|
237
|
+
| elsif respond_to_pluralized_name
|
238
|
+
| pluralized_name
|
239
|
+
| elsif respond_to_query_methods
|
240
|
+
| nil
|
241
|
+
| else
|
242
|
+
| exact_name
|
243
|
+
| end
|
244
|
+
|end
|
245
|
+
END
|
246
|
+
end
|
247
|
+
# rubocop:enable MethodLength
|
248
|
+
end
|
249
|
+
|
172
250
|
class HaveRecord < Record
|
173
251
|
def initialize(have)
|
174
252
|
@have = have
|
@@ -210,7 +288,11 @@ module Transpec
|
|
210
288
|
|
211
289
|
def original_items
|
212
290
|
if @have.subject_is_owner_of_collection?
|
213
|
-
@have.
|
291
|
+
if @have.items_method_has_arguments?
|
292
|
+
"#{@have.collection_accessor}(...)"
|
293
|
+
else
|
294
|
+
@have.collection_accessor
|
295
|
+
end
|
214
296
|
else
|
215
297
|
'items'
|
216
298
|
end
|
@@ -218,11 +300,16 @@ module Transpec
|
|
218
300
|
|
219
301
|
def converted_subject
|
220
302
|
if @have.subject_is_owner_of_collection?
|
303
|
+
subject = 'obj.'
|
221
304
|
if @have.collection_accessor_is_private?
|
222
|
-
"
|
305
|
+
subject << "send(#{@have.collection_accessor.inspect}"
|
306
|
+
subject << ', ...' if @have.items_method_has_arguments?
|
307
|
+
subject << ')'
|
223
308
|
else
|
224
|
-
"
|
309
|
+
subject << "#{@have.collection_accessor}"
|
310
|
+
subject << '(...)' if @have.items_method_has_arguments?
|
225
311
|
end
|
312
|
+
subject << ".#{@have.query_method}"
|
226
313
|
else
|
227
314
|
"collection.#{@have.default_query_method}"
|
228
315
|
end
|
data/lib/transpec/syntax/its.rb
CHANGED
@@ -13,7 +13,15 @@ module Transpec
|
|
13
13
|
receiver_node.nil? && method_name == :its
|
14
14
|
end
|
15
15
|
|
16
|
+
def register_request_for_dynamic_analysis(rewriter)
|
17
|
+
key = :project_requires_its?
|
18
|
+
code = 'defined?(RSpec::Its)'
|
19
|
+
rewriter.register_request(@node, key, code, :context)
|
20
|
+
end
|
21
|
+
|
16
22
|
def convert_to_describe_subject_it!
|
23
|
+
return if project_requires_its?
|
24
|
+
|
17
25
|
front, rear = build_wrapper_codes
|
18
26
|
|
19
27
|
insert_before(beginning_of_line_range, front)
|
@@ -37,6 +45,11 @@ module Transpec
|
|
37
45
|
@node.parent_node
|
38
46
|
end
|
39
47
|
|
48
|
+
def project_requires_its?
|
49
|
+
node_data = runtime_node_data(@node)
|
50
|
+
node_data && node_data[:project_requires_its?].result
|
51
|
+
end
|
52
|
+
|
40
53
|
private
|
41
54
|
|
42
55
|
def build_wrapper_codes
|
@@ -99,9 +112,9 @@ module Transpec
|
|
99
112
|
|
100
113
|
def converted_syntax
|
101
114
|
if attribute_expression.brackets?
|
102
|
-
"describe '[:key]' do subject { super()[:key] } it { } end"
|
115
|
+
"describe '[:key]' do subject { super()[:key] }; it { } end"
|
103
116
|
else
|
104
|
-
"describe 'attr' do subject { super().attr } it { } end"
|
117
|
+
"describe 'attr' do subject { super().attr }; it { } end"
|
105
118
|
end
|
106
119
|
end
|
107
120
|
|
@@ -23,6 +23,7 @@ module Transpec
|
|
23
23
|
:allow_to_receive_available?,
|
24
24
|
[:allow, :receive]
|
25
25
|
)
|
26
|
+
register_request_of_any_instance_inspection(rewriter)
|
26
27
|
end
|
27
28
|
|
28
29
|
def allow_to_receive_available?
|
@@ -101,8 +102,7 @@ module Transpec
|
|
101
102
|
|
102
103
|
def allow_source
|
103
104
|
if any_instance?
|
104
|
-
|
105
|
-
"allow_any_instance_of(#{class_source})"
|
105
|
+
"allow_any_instance_of(#{any_instance_target_class_source})"
|
106
106
|
else
|
107
107
|
"allow(#{subject_range.source})"
|
108
108
|
end
|
@@ -127,7 +127,7 @@ module Transpec
|
|
127
127
|
end
|
128
128
|
|
129
129
|
def original_syntax
|
130
|
-
syntax = any_instance? ? '
|
130
|
+
syntax = any_instance? ? 'Klass.any_instance' : 'obj'
|
131
131
|
syntax << ".#{method_name}"
|
132
132
|
syntax << (arg_node.type == :hash ? '(:message => value)' : '(:message)')
|
133
133
|
end
|
@@ -135,7 +135,7 @@ module Transpec
|
|
135
135
|
def converted_syntax(conversion_type)
|
136
136
|
case conversion_type
|
137
137
|
when :allow_to_receive, :allow_to_receive_messages
|
138
|
-
syntax = any_instance? ? 'allow_any_instance_of(
|
138
|
+
syntax = any_instance? ? 'allow_any_instance_of(Klass)' : 'allow(obj)'
|
139
139
|
syntax << '.to '
|
140
140
|
if conversion_type == :allow_to_receive
|
141
141
|
syntax << 'receive(:message)'
|