knapsack_pro 7.14.0 → 8.0.0

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +36 -2
  3. data/CHANGELOG.md +18 -0
  4. data/knapsack_pro.gemspec +5 -5
  5. data/lib/knapsack_pro/adapters/base_adapter.rb +2 -2
  6. data/lib/knapsack_pro/adapters/rspec_adapter.rb +34 -16
  7. data/lib/knapsack_pro/base_allocator_builder.rb +5 -5
  8. data/lib/knapsack_pro/config/env.rb +12 -17
  9. data/lib/knapsack_pro/formatters/time_tracker.rb +27 -13
  10. data/lib/knapsack_pro/formatters/time_tracker_fetcher.rb +2 -2
  11. data/lib/knapsack_pro/runners/cucumber_runner.rb +1 -1
  12. data/lib/knapsack_pro/runners/minitest_runner.rb +1 -1
  13. data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +1 -1
  14. data/lib/knapsack_pro/runners/queue/minitest_runner.rb +1 -1
  15. data/lib/knapsack_pro/runners/queue/rspec_runner.rb +8 -4
  16. data/lib/knapsack_pro/runners/rspec_runner.rb +2 -4
  17. data/lib/knapsack_pro/runners/spinach_runner.rb +1 -1
  18. data/lib/knapsack_pro/runners/test_unit_runner.rb +1 -1
  19. data/lib/knapsack_pro/test_case_detectors/rspec_test_example_detector.rb +8 -2
  20. data/lib/knapsack_pro/test_case_mergers/rspec_merger.rb +1 -7
  21. data/lib/knapsack_pro/tracker.rb +1 -1
  22. data/lib/knapsack_pro/urls.rb +0 -2
  23. data/lib/knapsack_pro/version.rb +1 -1
  24. data/lib/tasks/rspec.rake +1 -1
  25. data/spec/integration/runners/queue/rspec_runner_spec.rb +126 -44
  26. data/spec/knapsack_pro/adapters/base_adapter_spec.rb +9 -9
  27. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +54 -70
  28. data/spec/knapsack_pro/config/env_spec.rb +51 -105
  29. data/spec/knapsack_pro/formatters/time_tracker_fetcher_spec.rb +2 -5
  30. data/spec/knapsack_pro/formatters/time_tracker_specs.rb +4 -2
  31. data/spec/knapsack_pro/runners/cucumber_runner_spec.rb +1 -1
  32. data/spec/knapsack_pro/runners/minitest_runner_spec.rb +1 -1
  33. data/spec/knapsack_pro/runners/queue/cucumber_runner_spec.rb +1 -1
  34. data/spec/knapsack_pro/runners/queue/minitest_runner_spec.rb +1 -1
  35. data/spec/knapsack_pro/runners/rspec_runner_spec.rb +2 -2
  36. data/spec/knapsack_pro/runners/spinach_runner_spec.rb +1 -1
  37. data/spec/knapsack_pro/runners/test_unit_runner_spec.rb +1 -1
  38. data/spec/knapsack_pro/test_case_detectors/rspec_test_example_detector_spec.rb +35 -11
  39. metadata +48 -48
@@ -1959,23 +1959,20 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
1959
1959
  end
1960
1960
  end
1961
1961
 
1962
- context 'when the RSpec split by examples is enabled' do
1962
+ context 'when the RSpec split by test examples is enabled' do
1963
1963
  before do
1964
- ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
1965
-
1966
- # remember to stub Queue API batches to include test examples (example: a_spec.rb[1:1])
1967
- # for the following slow test files
1964
+ # Remember to stub the Queue API batches to include test examples (example: a_spec.rb[1:1])
1965
+ # for the following slow test files.
1968
1966
  ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
1969
1967
 
1970
1968
  ENV['KNAPSACK_PRO_CI_NODE_TOTAL'] = '2'
1971
1969
  end
1972
1970
  after do
1973
- ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
1974
1971
  ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
1975
1972
  ENV.delete('KNAPSACK_PRO_CI_NODE_TOTAL')
1976
1973
  end
1977
1974
 
1978
- it 'splits slow test files by examples AND ensures the test examples are executed only once' do
1975
+ it 'splits slow test files by test examples AND ensures the test examples are executed only once' do
1979
1976
  rspec_options = '--format d'
1980
1977
 
1981
1978
  spec_a = Spec.new('a_spec.rb', <<~SPEC)
@@ -2058,23 +2055,20 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
2058
2055
  end
2059
2056
  end
2060
2057
 
2061
- context 'when the RSpec split by examples is enabled AND --tag is set' do
2058
+ context 'when the RSpec split by test examples is enabled AND --tag is set' do
2062
2059
  before do
2063
- ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
2064
-
2065
- # remember to stub Queue API batches to include test examples (example: a_spec.rb[1:1])
2066
- # for the following slow test files
2060
+ # Remember to stub the Queue API batches to include test examples (example: a_spec.rb[1:2])
2061
+ # for the following slow test files.
2067
2062
  ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
2068
2063
 
2069
2064
  ENV['KNAPSACK_PRO_CI_NODE_TOTAL'] = '2'
2070
2065
  end
2071
2066
  after do
2072
- ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
2073
2067
  ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
2074
2068
  ENV.delete('KNAPSACK_PRO_CI_NODE_TOTAL')
2075
2069
  end
2076
2070
 
2077
- it 'sets 1 as exit code AND raises an error (a test example path as a_spec.rb[1:1] would always be executed even when it does not have the tag that is set via the --tag option. We cannot run tests because it could lead to running unintentional tests)' do
2071
+ it 'splits slow test files by test examples AND ensures the test examples are executed only once' do
2078
2072
  rspec_options = '--format d --tag my_tag'
2079
2073
 
2080
2074
  spec_a = Spec.new('a_spec.rb', <<~SPEC)
@@ -2082,7 +2076,7 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
2082
2076
  it 'A1 test example' do
2083
2077
  expect(1).to eq 1
2084
2078
  end
2085
- it 'A2 test example' do
2079
+ it 'A2 test example', :my_tag do
2086
2080
  expect(1).to eq 1
2087
2081
  end
2088
2082
  end
@@ -2101,7 +2095,7 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
2101
2095
 
2102
2096
  spec_c = Spec.new('c_spec.rb', <<~SPEC)
2103
2097
  describe 'C_describe' do
2104
- it 'C1 test example' do
2098
+ it 'C1 test example', :my_tag do
2105
2099
  expect(1).to eq 1
2106
2100
  end
2107
2101
  it 'C2 test example' do
@@ -2114,43 +2108,39 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
2114
2108
  [spec_a, spec_b, spec_c]
2115
2109
  ])
2116
2110
  stub_test_cases_for_slow_test_files([
2117
- "#{spec_a.path}[1:1]",
2118
- "#{spec_a.path}[1:2]",
2111
+ "#{spec_a.path}[1:2]", # only this test example is tagged
2119
2112
  ])
2120
2113
  stub_spec_batches([
2121
- ["#{spec_a.path}[1:1]", spec_b.path],
2114
+ [spec_b.path],
2122
2115
  ["#{spec_a.path}[1:2]", spec_c.path],
2123
2116
  ])
2124
2117
 
2125
2118
  actual = subject
2126
2119
 
2127
- expect(actual.stdout).to include('ERROR -- : [knapsack_pro] It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature. Please see: https://knapsackpro.com/perma/ruby/rspec-split-by-test-examples-tag')
2128
-
2129
2120
  expect(actual.stdout).to_not include('A1 test example')
2130
- expect(actual.stdout).to_not include('A2 test example')
2131
- expect(actual.stdout).to_not include('B1 test example')
2132
- expect(actual.stdout).to_not include('B2 test example')
2133
- expect(actual.stdout).to_not include('C1 test example')
2121
+ expect(actual.stdout).to include('A2 test example')
2122
+ expect(actual.stdout).to include('B1 test example')
2123
+ expect(actual.stdout).to include('B2 test example')
2124
+ expect(actual.stdout).to include('C1 test example')
2134
2125
  expect(actual.stdout).to_not include('C2 test example')
2135
2126
 
2136
- expect(actual.exit_code).to eq 1
2127
+ expect(actual.stdout).to include('4 examples, 0 failures')
2128
+
2129
+ expect(actual.exit_code).to eq 0
2137
2130
  end
2138
2131
  end
2139
2132
 
2140
- context 'when the RSpec split by examples is enabled AND JSON formatter is used' do
2133
+ context 'when the RSpec split by test examples is enabled AND JSON formatter is used' do
2141
2134
  let(:json_file) { "#{SPEC_DIRECTORY}/rspec.json" }
2142
2135
 
2143
2136
  before do
2144
- ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
2145
-
2146
- # remember to stub Queue API batches to include test examples (example: a_spec.rb[1:1])
2147
- # for the following slow test files
2137
+ # Remember to stub the Queue API batches to include test examples (example: a_spec.rb[1:1])
2138
+ # for the following slow test files.
2148
2139
  ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
2149
2140
 
2150
2141
  ENV['KNAPSACK_PRO_CI_NODE_TOTAL'] = '2'
2151
2142
  end
2152
2143
  after do
2153
- ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
2154
2144
  ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
2155
2145
  ENV.delete('KNAPSACK_PRO_CI_NODE_TOTAL')
2156
2146
  end
@@ -2239,20 +2229,17 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
2239
2229
  end
2240
2230
  end
2241
2231
 
2242
- context 'when the RSpec split by examples is enabled AND JUnit XML formatter is used' do
2232
+ context 'when the RSpec split by test examples is enabled AND JUnit XML formatter is used' do
2243
2233
  let(:xml_file) { "#{SPEC_DIRECTORY}/rspec.xml" }
2244
2234
 
2245
2235
  before do
2246
- ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
2247
-
2248
- # remember to stub Queue API batches to include test examples (example: a_spec.rb[1:1])
2249
- # for the following slow test files
2236
+ # Remember to stub the Queue API batches to include test examples (example: a_spec.rb[1:1])
2237
+ # for the following slow test files.
2250
2238
  ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
2251
2239
 
2252
2240
  ENV['KNAPSACK_PRO_CI_NODE_TOTAL'] = '2'
2253
2241
  end
2254
2242
  after do
2255
- ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
2256
2243
  ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
2257
2244
  ENV.delete('KNAPSACK_PRO_CI_NODE_TOTAL')
2258
2245
  end
@@ -2338,21 +2325,18 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
2338
2325
  end
2339
2326
  end
2340
2327
 
2341
- context 'when the RSpec split by examples is enabled AND simplecov is used' do
2328
+ context 'when the RSpec split by test examples is enabled AND simplecov is used' do
2342
2329
  let(:coverage_dir) { "#{KNAPSACK_PRO_TMP_DIR}/coverage" }
2343
2330
  let(:coverage_file) { "#{coverage_dir}/index.html" }
2344
2331
 
2345
2332
  before do
2346
- ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
2347
-
2348
- # remember to stub Queue API batches to include test examples (example: a_spec.rb[1:1])
2349
- # for the following slow test files
2333
+ # Remember to stub the Queue API batches to include test examples (example: a_spec.rb[1:1])
2334
+ # for the following slow test files.
2350
2335
  ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = "#{SPEC_DIRECTORY}/a_spec.rb"
2351
2336
 
2352
2337
  ENV['KNAPSACK_PRO_CI_NODE_TOTAL'] = '2'
2353
2338
  end
2354
2339
  after do
2355
- ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
2356
2340
  ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
2357
2341
  ENV.delete('KNAPSACK_PRO_CI_NODE_TOTAL')
2358
2342
  end
@@ -2427,6 +2411,104 @@ describe "#{KnapsackPro::Runners::Queue::RSpecRunner} - Integration tests", :cle
2427
2411
  end
2428
2412
  end
2429
2413
 
2414
+ context 'when the RSpec split by test examples is enabled AND test files are split by test examples AND slow test files are not detected (for example, the user could have passed test examples like a_spec.rb[1:1] directly using KNAPSACK_PRO_TEST_FILE_LIST_SOURCE_FILE or KNAPSACK_PRO_TEST_FILE_LIST)' do
2415
+ before do
2416
+ ENV['KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES'] = 'true'
2417
+ ENV['KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN'] = ""
2418
+ ENV['KNAPSACK_PRO_CI_NODE_TOTAL'] = '2'
2419
+ end
2420
+ after do
2421
+ ENV.delete('KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES')
2422
+ ENV.delete('KNAPSACK_PRO_SLOW_TEST_FILE_PATTERN')
2423
+ ENV.delete('KNAPSACK_PRO_CI_NODE_TOTAL')
2424
+ end
2425
+
2426
+ it 'detects test execution times correctly for individual test examples even though they are not considered slow test files' do
2427
+ ENV['TEST__LOG_EXECUTION_TIMES'] = 'true'
2428
+ rspec_options = '--format d'
2429
+
2430
+ spec_a = Spec.new('a_spec.rb', <<~SPEC)
2431
+ describe 'A_describe' do
2432
+ it 'A1 test example' do
2433
+ expect(1).to eq 1
2434
+ end
2435
+ it 'A2 test example' do
2436
+ expect(1).to eq 1
2437
+ end
2438
+ end
2439
+ SPEC
2440
+
2441
+ spec_b = Spec.new('b_spec.rb', <<~SPEC)
2442
+ describe 'B_describe' do
2443
+ it 'B1 test example' do
2444
+ expect(1).to eq 1
2445
+ end
2446
+ it 'B2 test example' do
2447
+ expect(1).to eq 1
2448
+ end
2449
+ end
2450
+ SPEC
2451
+
2452
+ spec_c = Spec.new('c_spec.rb', <<~SPEC)
2453
+ describe 'C_describe' do
2454
+ it 'C1 test example' do
2455
+ expect(1).to eq 1
2456
+ end
2457
+ it 'C2 test example' do
2458
+ expect(1).to eq 1
2459
+ end
2460
+ end
2461
+ SPEC
2462
+
2463
+ generate_specs(spec_helper_with_knapsack, rspec_options, [
2464
+ [spec_a, spec_b, spec_c]
2465
+ ])
2466
+ stub_test_cases_for_slow_test_files([
2467
+ "#{spec_a.path}[1:1]",
2468
+ "#{spec_a.path}[1:2]",
2469
+ ])
2470
+ stub_spec_batches([
2471
+ ["#{spec_a.path}[1:1]", spec_b.path],
2472
+ ["#{spec_a.path}[1:2]", spec_c.path],
2473
+ ])
2474
+
2475
+ actual = subject
2476
+
2477
+ expect(actual.stdout).to include('DEBUG -- : [knapsack_pro] Detected 0 slow test files: []')
2478
+
2479
+ expect(actual.stdout).to include(
2480
+ <<~OUTPUT
2481
+ A_describe
2482
+ A1 test example
2483
+
2484
+ B_describe
2485
+ B1 test example
2486
+ B2 test example
2487
+ OUTPUT
2488
+ )
2489
+
2490
+ expect(actual.stdout).to include(
2491
+ <<~OUTPUT
2492
+ A_describe
2493
+ A2 test example
2494
+
2495
+ C_describe
2496
+ C1 test example
2497
+ C2 test example
2498
+ OUTPUT
2499
+ )
2500
+
2501
+ expect(actual.stdout.scan(/A1 test example/).size).to eq 1
2502
+ expect(actual.stdout.scan(/A2 test example/).size).to eq 1
2503
+
2504
+ expect(actual.stdout).to include('6 examples, 0 failures')
2505
+
2506
+ expect(actual.stdout).to include('[INTEGRATION TEST] test_files: 4, test files have execution time: true')
2507
+
2508
+ expect(actual.exit_code).to eq 0
2509
+ end
2510
+ end
2511
+
2430
2512
  context 'when the example_status_persistence_file_path option is used and multiple batches of tests are fetched from the Queue API and some tests are pending and failing' do
2431
2513
  let(:examples_file_path) { "#{SPEC_DIRECTORY}/examples.txt" }
2432
2514
 
@@ -135,21 +135,21 @@ describe KnapsackPro::Adapters::BaseAdapter do
135
135
 
136
136
  describe '#bind' do
137
137
  let(:temp_directory_path) { '.knapsack_pro' }
138
- let(:recording_enabled?) { false }
139
- let(:queue_recording_enabled?) { false }
138
+ let(:regular_mode?) { false }
139
+ let(:queue_mode?) { false }
140
140
 
141
141
  before do
142
142
  expect(KnapsackPro::Config::TempFiles).to receive(:ensure_temp_directory_exists!)
143
143
  expect(File).to receive(:write).with('.knapsack_pro/KnapsackPro-Adapters-BaseAdapter-bind_method_called_for_node_0.txt', nil)
144
144
 
145
- expect(KnapsackPro::Config::Env).to receive(:recording_enabled?).and_return(recording_enabled?)
146
- expect(KnapsackPro::Config::Env).to receive(:queue_recording_enabled?).and_return(queue_recording_enabled?)
145
+ expect(KnapsackPro::Config::Env).to receive(:regular_mode?).and_return(regular_mode?)
146
+ expect(KnapsackPro::Config::Env).to receive(:queue_mode?).and_return(queue_mode?)
147
147
  end
148
148
 
149
149
  after { subject.bind }
150
150
 
151
- context 'when recording enabled' do
152
- let(:recording_enabled?) { true }
151
+ context 'when regular mode enabled' do
152
+ let(:regular_mode?) { true }
153
153
 
154
154
  before do
155
155
  allow(subject).to receive(:bind_time_tracker)
@@ -165,8 +165,8 @@ describe KnapsackPro::Adapters::BaseAdapter do
165
165
  it { expect(subject).to receive(:bind_save_report) }
166
166
  end
167
167
 
168
- context 'when queue recording enabled' do
169
- let(:queue_recording_enabled?) { true }
168
+ context 'when queue mode enabled' do
169
+ let(:queue_mode?) { true }
170
170
 
171
171
  it 'calls queue hooks in proper order before binding time tracker' do
172
172
  logger = instance_double(Logger)
@@ -179,7 +179,7 @@ describe KnapsackPro::Adapters::BaseAdapter do
179
179
  end
180
180
  end
181
181
 
182
- context 'when recording disabled' do
182
+ context 'when regular mode and queue mode disabled' do
183
183
  it { expect(subject).not_to receive(:bind_save_report) }
184
184
  it { expect(subject).not_to receive(:bind_before_queue_hook) }
185
185
  it { expect(subject).not_to receive(:bind_after_queue_hook) }
@@ -89,76 +89,6 @@ describe KnapsackPro::Adapters::RSpecAdapter do
89
89
  end
90
90
  end
91
91
 
92
- describe '.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!' do
93
- let(:cli_args) { double }
94
-
95
- subject { described_class.ensure_no_tag_option_when_rspec_split_by_test_examples_enabled!(cli_args) }
96
-
97
- before do
98
- expect(KnapsackPro::Config::Env).to receive(:rspec_split_by_test_examples?).and_return(rspec_split_by_test_examples_enabled)
99
- end
100
-
101
- context 'when RSpec split by test examples enabled' do
102
- let(:rspec_split_by_test_examples_enabled) { true }
103
-
104
- before do
105
- expect(described_class).to receive(:has_tag_option?).with(cli_args).and_return(has_tag_option)
106
- end
107
-
108
- context 'when RSpec tag option is provided' do
109
- let(:has_tag_option) { true }
110
-
111
- it do
112
- expect { subject }.to raise_error(/It is not allowed to use the RSpec tag option together with the RSpec split by test examples feature/)
113
- end
114
- end
115
-
116
- context 'when RSpec tag option is not provided' do
117
- let(:has_tag_option) { false }
118
-
119
- it 'does nothing' do
120
- expect(subject).to be_nil
121
- end
122
- end
123
- end
124
-
125
- context 'when RSpec split by test examples disabled' do
126
- let(:rspec_split_by_test_examples_enabled) { false }
127
-
128
- it 'does nothing' do
129
- expect(subject).to be_nil
130
- end
131
- end
132
- end
133
-
134
- describe '.has_tag_option?' do
135
- subject { described_class.has_tag_option?(cli_args) }
136
-
137
- context 'when tag option is provided as -t' do
138
- let(:cli_args) { ['-t', 'mytag'] }
139
-
140
- it { expect(subject).to be true }
141
- end
142
-
143
- context 'when tag option is provided as --tag' do
144
- let(:cli_args) { ['--tag', 'mytag'] }
145
-
146
- it { expect(subject).to be true }
147
- end
148
-
149
- context 'when tag option is provided without delimiter' do
150
- let(:cli_args) { ['-tmytag'] }
151
-
152
- it { expect(subject).to be true }
153
- end
154
-
155
- context 'when tag option is not provided' do
156
- let(:cli_args) { ['--fake', 'value'] }
157
-
158
- it { expect(subject).to be false }
159
- end
160
- end
161
-
162
92
  describe '.has_format_option?' do
163
93
  subject { described_class.has_format_option?(cli_args) }
164
94
 
@@ -215,6 +145,28 @@ describe KnapsackPro::Adapters::RSpecAdapter do
215
145
  end
216
146
  end
217
147
 
148
+ describe '.id_path?' do
149
+ subject { described_class.id_path?(path) }
150
+
151
+ context 'when the path resembles the RSpec path with id' do
152
+ let(:path) { 'spec/features/a_spec.rb[1:1:7:1]' }
153
+
154
+ it { is_expected.to be true }
155
+ end
156
+
157
+ context 'when the path resembles the RSpec path with multiple ids' do
158
+ let(:path) { 'spec/features/a_spec.rb[1:1:7:1, 1:2]' }
159
+
160
+ it { is_expected.to be true }
161
+ end
162
+
163
+ context "when the path doesn't resemble the RSpec path with id" do
164
+ let(:path) { 'spec/features/a_spec.rb' }
165
+
166
+ it { is_expected.to be false }
167
+ end
168
+ end
169
+
218
170
  describe '.rails_helper_exists?' do
219
171
  subject { described_class.rails_helper_exists?(test_dir) }
220
172
 
@@ -293,6 +245,18 @@ describe KnapsackPro::Adapters::RSpecAdapter do
293
245
  end
294
246
  end
295
247
 
248
+ describe '.remove_formatters' do
249
+ subject { described_class.remove_formatters(cli_args) }
250
+
251
+ context "when CLI args include formatters" do
252
+ let(:cli_args) { ['-t', 'awesome_tag', '-f', 'documentation', '-o', '/tmp/documentation.txt', '--tag', 'mytag', '--format', 'json', '--out', '/tmp/file.json', '--no-color'] }
253
+
254
+ it 'removes formatters and the related output file options' do
255
+ expect(subject).to eq ['-t', 'awesome_tag', '--tag', 'mytag', '--no-color']
256
+ end
257
+ end
258
+ end
259
+
296
260
  describe '.file_path_for' do
297
261
  let(:current_example) { ::RSpec.describe.example }
298
262
 
@@ -370,6 +334,26 @@ describe KnapsackPro::Adapters::RSpecAdapter do
370
334
  end
371
335
  end
372
336
 
337
+ describe 'private .scheduled_paths' do
338
+ subject { described_class.send(:scheduled_paths) }
339
+
340
+ context 'when the RSpec configuration has files or directories to run' do
341
+ it 'returns list of test files passed to RSpec (if this fails then the internal RSpec API changed and we must start supporting a new RSpec version as well)' do
342
+ expect(subject).not_to be_empty
343
+ end
344
+ end
345
+
346
+ context "when the RSpec configuration has no files or directories to run (when the internal RSpec API changed and we don't support the new RSpec version yet)" do
347
+ before do
348
+ expect(described_class).to receive(:rspec_configuration).and_return(double(:object_with_no_instance_variables))
349
+ end
350
+
351
+ it 'fallbacks to an empty array to not blow up Knapsack Pro' do
352
+ expect(subject).to eq([])
353
+ end
354
+ end
355
+ end
356
+
373
357
  describe 'bind methods' do
374
358
  let(:config) { double }
375
359
 
@@ -603,89 +603,43 @@ describe KnapsackPro::Config::Env do
603
603
  end
604
604
  end
605
605
 
606
- describe '.recording_enabled' do
607
- subject { described_class.recording_enabled }
608
-
609
- context 'when ENV exists' do
610
- let(:recording_enabled) { 'true' }
611
- before { stub_const("ENV", { 'KNAPSACK_PRO_RECORDING_ENABLED' => recording_enabled }) }
612
- it { should eq recording_enabled }
613
- end
614
-
615
- context "when ENV doesn't exist" do
616
- it { should be_nil }
617
- end
618
- end
619
-
620
606
  describe '.regular_mode?' do
621
607
  subject { described_class.regular_mode? }
622
608
 
623
- before do
624
- expect(described_class).to receive(:recording_enabled?).and_return(recording_enabled)
625
- end
626
-
627
- context 'when recording is enabled' do
628
- let(:recording_enabled) { true }
629
- it { should be true }
630
- end
631
-
632
- context 'when recording is not enabled' do
633
- let(:recording_enabled) { false }
634
- it { should be false }
635
- end
636
- end
637
-
638
- describe '.recording_enabled?' do
639
- subject { described_class.recording_enabled? }
640
-
641
- before do
642
- expect(described_class).to receive(:recording_enabled).and_return(recording_enabled)
643
- end
644
-
645
- context 'when enabled' do
646
- let(:recording_enabled) { 'true' }
647
-
648
- it { should be true }
649
- end
650
-
651
- context 'when disabled' do
652
- let(:recording_enabled) { nil }
653
-
654
- it { should be false }
609
+ context 'when regular mode is enabled' do
610
+ let(:regular_mode_enabled) { 'true' }
611
+ before { stub_const("ENV", { 'KNAPSACK_PRO_REGULAR_MODE_ENABLED' => regular_mode_enabled }) }
612
+ it { should eq true }
655
613
  end
656
- end
657
614
 
658
- describe '.queue_recording_enabled' do
659
- subject { described_class.queue_recording_enabled }
660
-
661
- context 'when ENV exists' do
662
- let(:queue_recording_enabled) { 'true' }
663
- before { stub_const("ENV", { 'KNAPSACK_PRO_QUEUE_RECORDING_ENABLED' => queue_recording_enabled }) }
664
- it { should eq queue_recording_enabled }
615
+ context 'when regular mode is disabled' do
616
+ let(:regular_mode_enabled) { 'false' }
617
+ before { stub_const("ENV", { 'KNAPSACK_PRO_REGULAR_MODE_ENABLED' => regular_mode_enabled }) }
618
+ it { should eq false }
665
619
  end
666
620
 
667
621
  context "when ENV doesn't exist" do
668
- it { should be_nil }
622
+ it { should false }
669
623
  end
670
624
  end
671
625
 
672
- describe '.queue_recording_enabled?' do
673
- subject { described_class.queue_recording_enabled? }
626
+ describe '.queue_mode?' do
627
+ subject { described_class.queue_mode? }
674
628
 
675
- before do
676
- expect(described_class).to receive(:queue_recording_enabled).and_return(queue_recording_enabled)
629
+ context 'when queue mode is enabled' do
630
+ let(:queue_mode_enabled) { 'true' }
631
+ before { stub_const("ENV", { 'KNAPSACK_PRO_QUEUE_MODE_ENABLED' => queue_mode_enabled }) }
632
+ it { should eq true }
677
633
  end
678
634
 
679
- context 'when enabled' do
680
- let(:queue_recording_enabled) { 'true' }
681
-
682
- it { should be true }
635
+ context 'when queue mode is disabled' do
636
+ let(:queue_mode_enabled) { 'false' }
637
+ before { stub_const("ENV", { 'KNAPSACK_PRO_QUEUE_MODE_ENABLED' => queue_mode_enabled }) }
638
+ it { should eq false }
683
639
  end
684
640
 
685
- context 'when disabled' do
686
- let(:queue_recording_enabled) { nil }
687
-
688
- it { should be false }
641
+ context "when ENV doesn't exist" do
642
+ it { should false }
689
643
  end
690
644
  end
691
645
 
@@ -1016,51 +970,43 @@ describe KnapsackPro::Config::Env do
1016
970
  describe '.rspec_split_by_test_examples?' do
1017
971
  subject { described_class.rspec_split_by_test_examples? }
1018
972
 
1019
- after(:each) do
1020
- described_class.remove_instance_variable(:@rspec_split_by_test_examples)
1021
- end
1022
-
1023
- context 'when KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES=true AND KNAPSACK_PRO_CI_NODE_TOTAL >= 2' do
1024
- before do
1025
- stub_const("ENV", { 'KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES' => 'true', 'KNAPSACK_PRO_CI_NODE_TOTAL' => '2' })
1026
- expect(KnapsackPro).not_to receive(:logger)
1027
- end
1028
-
1029
- it { should be true }
973
+ before do
974
+ described_class.remove_instance_variable(:@rspec_split_by_test_examples) if described_class.instance_variable_defined?(:@rspec_split_by_test_examples)
1030
975
  end
1031
-
1032
- context 'when KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES=false AND KNAPSACK_PRO_CI_NODE_TOTAL >= 2' do
1033
- before do
1034
- stub_const("ENV", { 'KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES' => 'false', 'KNAPSACK_PRO_CI_NODE_TOTAL' => '2' })
1035
- expect(KnapsackPro).not_to receive(:logger)
1036
- end
1037
-
1038
- it { should be false }
976
+ after do
977
+ described_class.remove_instance_variable(:@rspec_split_by_test_examples)
1039
978
  end
1040
979
 
1041
- context 'when KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES=true AND KNAPSACK_PRO_CI_NODE_TOTAL < 2' do
1042
- before { stub_const("ENV", { 'KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES' => 'true', 'KNAPSACK_PRO_CI_NODE_TOTAL' => '1' }) }
1043
-
1044
- it { should be false }
1045
-
1046
- context 'when called twice' do
1047
- it 'logs a debug message only once' do
1048
- logger = instance_double(Logger)
1049
- expect(KnapsackPro).to receive(:logger).and_return(logger)
1050
- expect(logger).to receive(:debug).with('Skipping split of test files by test examples because you are running tests on a single CI node (no parallelism)')
980
+ [
981
+ ['false', '2', nil, false],
982
+ ['true', '2', nil, true],
983
+ [nil, '2', nil, true],
984
+ ['false', '1', nil, false],
985
+ ['true', '1', nil, false, :debug, 'Skipping split by test examples because tests are running on a single CI node (no parallelism)'],
986
+ [nil, '1', nil, false, :debug, 'Skipping split by test examples because tests are running on a single CI node (no parallelism)'],
987
+ ['false', '2', 'true', false],
988
+ ['true', '2', 'true', true],
989
+ [nil, '2', 'true', false, :warn, "Skipping split by test examples because test file names encryption is enabled:\nhttps://knapsackpro.com/perma/ruby/encryption\nhttps://knapsackpro.com/perma/ruby/split-by-test-examples"],
990
+ ['false', '1', 'true', false],
991
+ ['true', '1', 'true', false, :debug, 'Skipping split by test examples because tests are running on a single CI node (no parallelism)'],
992
+ [nil, '1', 'true', false, :warn, "Skipping split by test examples because test file names encryption is enabled:\nhttps://knapsackpro.com/perma/ruby/encryption\nhttps://knapsackpro.com/perma/ruby/split-by-test-examples"],
993
+ ].each do |sbte, node_total, encrypted, expected, log_level, log_message|
994
+ context "KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES=#{sbte.inspect} AND KNAPSACK_PRO_CI_NODE_TOTAL=#{node_total.inspect} AND KNAPSACK_PRO_TEST_FILES_ENCRYPTED=#{encrypted.inspect}" do
995
+ before do
996
+ stub_const("ENV", { 'KNAPSACK_PRO_RSPEC_SPLIT_BY_TEST_EXAMPLES' => sbte, 'KNAPSACK_PRO_CI_NODE_TOTAL' => node_total, 'KNAPSACK_PRO_TEST_FILES_ENCRYPTED' => encrypted }.compact)
1051
997
 
1052
- 2.times { described_class.rspec_split_by_test_examples? }
998
+ if log_level && log_message
999
+ logger = instance_double(Logger)
1000
+ expect(KnapsackPro).to receive(:logger).and_return(logger)
1001
+ expect(logger).to receive(log_level).once.with(log_message)
1002
+ end
1053
1003
  end
1054
- end
1055
- end
1056
1004
 
1057
- context "when ENV doesn't exist" do
1058
- before do
1059
- stub_const("ENV", {})
1060
- expect(KnapsackPro).not_to receive(:logger)
1005
+ it do
1006
+ expect(described_class.rspec_split_by_test_examples?).to eq(expected)
1007
+ expect(described_class.rspec_split_by_test_examples?).to eq(expected)
1008
+ end
1061
1009
  end
1062
-
1063
- it { should be false }
1064
1010
  end
1065
1011
  end
1066
1012