spec_selector 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_selector'
4
+ require 'stringio'
5
+ require 'factory_bot'
6
+ require 'timeout'
7
+ require 'shared'
8
+
9
+ RCN = RSpec::Core::Notifications
10
+ EXAMPLE_STUBS = { description: 'description',
11
+ execution_result: 'result',
12
+ full_description: 'full_description' }.freeze
13
+
14
+ alias ivar instance_variable_get
15
+ alias ivar_set instance_variable_set
16
+
17
+ RSpec.configure do |config|
18
+ config.expect_with :rspec do |expectations|
19
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
20
+ end
21
+
22
+ config.filter_run_when_matching :focus
23
+
24
+ config.mock_with :rspec do |mocks|
25
+ mocks.verify_partial_doubles = true
26
+ end
27
+
28
+ config.shared_context_metadata_behavior = :apply_to_host_groups
29
+
30
+ config.include_context 'shared', include_shared: true
31
+
32
+ config.include FactoryBot::Syntax::Methods
33
+
34
+ config.before(:suite) do
35
+ FactoryBot.find_definitions
36
+ end
37
+
38
+ config.around(:example, break_loop: true) do |example|
39
+ begin
40
+ Timeout.timeout(0.001) do
41
+ example.run
42
+ end
43
+ rescue Timeout::Error
44
+ nil
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe SpecSelector do
4
+ include_context 'shared'
5
+
6
+ let(:spec_selector) { described_class.new(StringIO.new) }
7
+
8
+ let(:output) { spec_selector.ivar(:@output).string }
9
+ let(:pass_result) { build(:execution_result) }
10
+ let(:pending_result) { build(:execution_result, status: :pending) }
11
+
12
+ let(:fail_result) do
13
+ build(:execution_result, status: :failed, exception: 'error')
14
+ end
15
+
16
+ describe '#message' do
17
+ let(:notification) { RCN::MessageNotification.new('message') }
18
+
19
+ it 'stores the message string in the @messages array' do
20
+ messages = spec_selector.ivar(:@messages)
21
+ spec_selector.message(notification)
22
+ expect(messages).to include('message')
23
+ end
24
+ end
25
+
26
+ describe '#example_group_started' do
27
+ let(:group) do
28
+ instance_double('ExampleGroup', metadata: { block: 'key' }, examples: [])
29
+ end
30
+
31
+ let(:notification) { RCN::GroupNotification.new(group) }
32
+
33
+ it 'passes the example group to SpecSelector#map_group' do
34
+ expect(spec_selector).to receive(:map_group).with(group)
35
+ spec_selector.example_group_started(notification)
36
+ end
37
+
38
+ it 'stores example group in the @groups hash' do
39
+ spec_selector.example_group_started(notification)
40
+ groups = spec_selector.ivar(:@groups)
41
+ expect(groups.values).to include(group)
42
+ end
43
+ end
44
+
45
+ describe '#example_passed' do
46
+ let(:example) { instance_double('Example', execution_result: pass_result) }
47
+ let(:notification) { RCN::ExampleNotification.send(:new, example) }
48
+
49
+ before do
50
+ allow(spec_selector).to receive(:map_example)
51
+ allow(spec_selector).to receive(:check_inclusion_status)
52
+ spec_selector.example_passed(notification)
53
+ end
54
+
55
+ it 'stores example in @passed array' do
56
+ passed = spec_selector.ivar(:@passed)
57
+ expect(passed).to include(example)
58
+ end
59
+
60
+ it 'increments @pass_count' do
61
+ pass_count = spec_selector.ivar(:@pass_count)
62
+ expect(pass_count).to eq(1)
63
+ end
64
+
65
+ it 'updates passing example status display' do
66
+ expect(output).to match(/PASS: \d+/)
67
+ end
68
+ end
69
+
70
+ describe '#example_pending' do
71
+ let(:example) { instance_double('Example', execution_result: pending_result) }
72
+ let(:notification) { RCN::ExampleNotification.send(:new, example) }
73
+
74
+ before do
75
+ allow(spec_selector).to receive(:map_example)
76
+ allow(spec_selector).to receive(:check_inclusion_status)
77
+ spec_selector.example_pending(notification)
78
+ end
79
+
80
+ it 'stores example in @pending array' do
81
+ pending = spec_selector.ivar(:@pending)
82
+ expect(pending).to include(notification.example)
83
+ end
84
+
85
+ it 'increments @pending_count' do
86
+ pending_count = spec_selector.ivar(:@pending_count)
87
+ expect(pending_count).to eq(1)
88
+ end
89
+
90
+ it 'updates pending status display' do
91
+ expect(output).to match(/PENDING: \d+/)
92
+ end
93
+ end
94
+
95
+ describe '#example_failed' do
96
+ let(:example) do
97
+ instance_double('Example',
98
+ full_description: 'full description',
99
+ execution_result: fail_result)
100
+ end
101
+
102
+ let(:notification) { RCN::FailedExampleNotification.send(:new, example) }
103
+
104
+ before do
105
+ allow(spec_selector).to receive(:map_example)
106
+ allow(spec_selector).to receive(:check_inclusion_status)
107
+ spec_selector.example_failed(notification)
108
+ end
109
+
110
+ it 'stores example in @failed array' do
111
+ failed = spec_selector.ivar(:@failed)
112
+ expect(failed).to include(notification.example)
113
+ end
114
+
115
+ it 'increments @fail_count' do
116
+ fail_count = spec_selector.ivar(:@fail_count)
117
+ expect(fail_count).to eq(1)
118
+ end
119
+
120
+ it 'calls #status_count' do
121
+ expect(output).to match(/FAIL: \d+/)
122
+ end
123
+ end
124
+
125
+ describe '#dump_summary', break_loop: true do
126
+ let(:notification) { build(:summary_notification) }
127
+
128
+ context 'when errors outside of examples have occurred' do
129
+ let(:notification) do
130
+ build(:summary_notification, errors_outside_of_examples_count: 2)
131
+ end
132
+
133
+ it 'passes notification to #print_errors' do
134
+ allow(spec_selector).to receive(:print_errors).and_call_original
135
+ spec_selector.dump_summary(notification)
136
+ expect(spec_selector).to have_received(:print_errors).with(notification)
137
+ end
138
+ end
139
+
140
+ context 'when errors outside of examples have not occured' do
141
+ it 'does not call #print_errors' do
142
+ allow(spec_selector).to receive(:print_errors).and_call_original
143
+ spec_selector.dump_summary(notification)
144
+ expect(spec_selector).not_to have_received(:print_errors)
145
+ end
146
+ end
147
+
148
+ context 'when non-error messages are present with no examples' do
149
+ it 'calls #messages only' do
150
+ spec_selector.ivar(:@messages) << 'some message'
151
+ spec_selector.dump_summary(notification)
152
+ end
153
+ end
154
+
155
+ context 'when examples successfully executed' do
156
+ it 'passes notification to #examples_summary' do
157
+ ivar_set(:@map, mixed_map)
158
+ allow(spec_selector).to receive(:examples_summary).and_call_original
159
+ spec_selector.dump_summary(notification)
160
+ expect(spec_selector).to have_received(:examples_summary)
161
+ .with(notification)
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,98 @@
1
+ describe 'SpecSelectorUtil::DataMap' do
2
+ subject(:spec_selector) { SpecSelector.new(StringIO.new) }
3
+
4
+ let(:example_group) { build(:example_group) }
5
+
6
+ let(:map) { spec_selector.ivar(:@map) }
7
+
8
+ describe '#top_level_push' do
9
+ before { spec_selector.top_level_push(example_group) }
10
+
11
+ it 'lazy-initializes @map[:top_level] to an array' do
12
+ expect(map[:top_level]).to be_an(Array)
13
+ end
14
+
15
+ it 'stores example group in @map[:top_level]' do
16
+ expect(map[:top_level]).to include(example_group)
17
+ end
18
+ end
19
+
20
+ # takes takes the metadata hash from an example group or from an
21
+ # example as its argument
22
+ describe '#parent_data' do
23
+ context 'when metadata hash is from an example' do
24
+ let(:example_metadata) { { example_group: example_group.metadata } }
25
+ let(:example) { instance_double('Example', metadata: example_metadata) }
26
+
27
+ it 'returns metadata of the example group to which the example belongs' do
28
+ expect(spec_selector.parent_data(example_metadata))
29
+ .to eq(example_group.metadata)
30
+ end
31
+ end
32
+
33
+ context 'when metadata hash is from an example group' do
34
+ context 'when the example group has a parent group' do
35
+ let(:example_group) do
36
+ instance_double('ExampleGroup', metadata: {
37
+ parent_example_group: { block: :parent_example_block }
38
+ })
39
+ end
40
+
41
+ it 'returns the parent group metadata' do
42
+ expect(spec_selector.parent_data(example_group.metadata))
43
+ .to eq(example_group.metadata[:parent_example_group])
44
+ end
45
+ end
46
+
47
+ context 'when the example group does not have a parent group' do
48
+ it 'returns nil' do
49
+ expect(spec_selector.parent_data(example_group.metadata)).to be_nil
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ describe '#map_group' do
56
+ let(:map) { spec_selector.ivar(:@map) }
57
+
58
+ before { spec_selector.map_group(example_group) }
59
+
60
+ context 'when example group has parent group' do
61
+ let(:example_group) do
62
+ instance_double('ExampleGroup', metadata: {
63
+ parent_example_group: { block: :parent_block }
64
+ })
65
+ end
66
+
67
+ it 'stores the parent block as a key in @map initialized to an array' do
68
+ expect(map[:parent_block]).to be_an(Array)
69
+ end
70
+
71
+ it 'stores the example group in the parent block array' do
72
+ expect(map[:parent_block]).to include(example_group)
73
+ end
74
+ end
75
+
76
+ context 'when example group does not have parent group' do
77
+ it 'passes the example group to #top_level_push' do
78
+ expect(map[:top_level]).to include(example_group)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe '#map_example' do
84
+ let(:example) do
85
+ build(:example, example_group: example_group)
86
+ end
87
+
88
+ before do
89
+ map[example_group.metadata[:block]] = []
90
+ example_group.examples << example
91
+ spec_selector.map_example(example)
92
+ end
93
+
94
+ it 'appends the example to its example group in @map' do
95
+ expect(map[example_group.metadata[:block]]).to include(example)
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe 'SpecSelectorUtil::DataPresentation' do
4
+ include_context 'shared'
5
+
6
+ let(:spec_selector) { SpecSelector.new(StringIO.new) }
7
+ let(:output) { spec_selector.ivar(:@output).string }
8
+ let(:notification) { build(:summary_notification) }
9
+
10
+ before do
11
+ allow(spec_selector).to receive(:exit_only)
12
+ end
13
+
14
+ describe '#test_data_summary' do
15
+ before do
16
+ allow_methods(:status_count, :print_summary)
17
+ spec_selector.test_data_summary
18
+ end
19
+
20
+ it 'calls #status_count' do
21
+ expect(spec_selector).to have_received(:status_count)
22
+ end
23
+
24
+ it 'calls #print_summary' do
25
+ expect(spec_selector).to have_received(:print_summary)
26
+ end
27
+ end
28
+
29
+ describe '#print_errors' do
30
+ let(:notification) do
31
+ build(:summary_notification, errors_outside_of_examples_count: 2)
32
+ end
33
+
34
+ before do
35
+ spec_selector.ivar(:@messages) << 'some message'
36
+ spec_selector.print_errors(notification)
37
+ end
38
+
39
+ it 'calls #print_messages' do
40
+ expect(output).to match(/[some message]/)
41
+ end
42
+
43
+ it 'passes notification to #errors_summary' do
44
+ expect(output).to match(/[2 errors occurred outside of examples]/)
45
+ end
46
+ end
47
+
48
+ describe '#print_messages' do
49
+ before do
50
+ spec_selector.ivar(:@messages) << 'example message one'
51
+ spec_selector.ivar(:@messages) << 'example message two'
52
+ end
53
+
54
+ it 'prints each message' do
55
+ spec_selector.print_messages
56
+ expect(output).to match(/example message one/)
57
+ expect(output).to match(/example message two/)
58
+ end
59
+ end
60
+
61
+ describe '#examples_summary' do
62
+ before do
63
+ allow(spec_selector).to receive(:status_summary).with(notification)
64
+ allow(spec_selector).to receive(:selector)
65
+ spec_selector.ivar(:@map)[:top_level] = :top_level
66
+ spec_selector.examples_summary(notification)
67
+ end
68
+
69
+ it 'assigns the summary notification to an instance variable' do
70
+ summary_notification = spec_selector.ivar(:@summary_notification)
71
+ expect(summary_notification).to eq(notification)
72
+ end
73
+
74
+ it 'passes the notification object to #status_summary' do
75
+ expect(spec_selector).to have_received(:status_summary).with(notification)
76
+ end
77
+
78
+ it 'sets the value of @list to @map[:top_level]' do
79
+ expect(spec_selector.ivar(:@list)).to eq(:top_level)
80
+ end
81
+
82
+ it 'calls #selector' do
83
+ expect(spec_selector).to have_received(:selector)
84
+ end
85
+ end
86
+
87
+ describe '#errors_summary' do
88
+ it 'prints text indicating number of errors outside examples' do
89
+ allow(notification).to receive(:errors_outside_of_examples_count).and_return(3)
90
+ allow(notification).to receive(:duration)
91
+ allow(notification).to receive(:load_time)
92
+ spec_selector.errors_summary(notification)
93
+ expect(output).to match(/3 errors occurred outside of examples/)
94
+ end
95
+ end
96
+
97
+ describe '#status_count' do
98
+ it 'calls #pass_count' do
99
+ spec_selector.ivar_set(:@pass_count, 5)
100
+ spec_selector.status_count
101
+ expect(output).to match(/PASS: 5/)
102
+ end
103
+
104
+ it 'calls #fail_count' do
105
+ spec_selector.ivar_set(:@fail_count, 3)
106
+ spec_selector.status_count
107
+ expect(output).to match(/FAIL: 3/)
108
+ end
109
+
110
+ context 'when there are pending examples' do
111
+ it 'calls #pending_count' do
112
+ spec_selector.ivar_set(:@pending_count, 2)
113
+ spec_selector.status_count
114
+ expect(output).to match(/PENDING: 2/)
115
+ end
116
+ end
117
+
118
+ context 'when there no pending examples' do
119
+ it 'does not call #pending_count' do
120
+ expect(output).not_to match(/PENDING:/)
121
+ end
122
+ end
123
+ end
124
+
125
+ describe '#print_summary' do
126
+ before do
127
+ spec_selector.ivar_set(:@example_count, 30)
128
+ allow(notification).to receive(:duration).and_return(1.5)
129
+ allow(notification).to receive(:load_time).and_return(2.3)
130
+ spec_selector.status_summary(notification)
131
+ spec_selector.print_summary
132
+ end
133
+
134
+ it 'prints total examples' do
135
+ expect(output).to match(/Total Examples: 30/)
136
+ end
137
+
138
+ it 'prints total time for examples to run' do
139
+ expect(output).to match(/Finished in 1.5 seconds/)
140
+ end
141
+
142
+ it 'prints total time for files to load' do
143
+ expect(output).to match(/Files loaded in 2.3 seconds/)
144
+ end
145
+ end
146
+
147
+ describe '#exclude_passing!' do
148
+ before do
149
+ spec_selector.ivar_set(:@map, mixed_map)
150
+ spec_selector.exclude_passing!
151
+ end
152
+
153
+ it 'sets @active_map to map that excludes all-passing example groups' do
154
+ expect(spec_selector.ivar(:@active_map)[:top_level]).not_to include(pass_group)
155
+ expect(spec_selector.ivar(:@active_map)[:top_level]).to include(fail_group)
156
+ end
157
+
158
+ it 'sets @exclude_passing to true' do
159
+ expect(spec_selector.ivar(:@exclude_passing)).to be true
160
+ end
161
+ end
162
+
163
+ describe '#include_passing!' do
164
+ before do
165
+ spec_selector.ivar_set(:@map, mixed_map)
166
+ spec_selector.exclude_passing!
167
+ end
168
+
169
+ it 'sets @active_map to @map' do
170
+ expect(spec_selector.ivar(:@active)).not_to eq(spec_selector.ivar(:@map))
171
+ spec_selector.include_passing!
172
+ expect(spec_selector.ivar(:@active_map)).to eq(spec_selector.ivar(:@map))
173
+ end
174
+
175
+ it 'sets @exclude_passing to false' do
176
+ expect(spec_selector.ivar(:@exclude_passing)).to be true
177
+ spec_selector.include_passing!
178
+ expect(spec_selector.ivar(:@exclude_passing)).to be false
179
+ end
180
+ end
181
+
182
+ describe '#toggle_passing' do
183
+ before do
184
+ allow_methods(:display_list, :navigate)
185
+ map = mixed_map
186
+ list = mixed_list
187
+ group = pass_group
188
+ ivars_set(:@map => map, :@list => list, :@selected => group)
189
+ end
190
+
191
+ context 'when displayed list includes all-passing example groups' do
192
+ it 'removes all-passing example groups from displayed list' do
193
+ spec_selector.toggle_passing
194
+ expect(spec_selector.ivar(:@list)).not_to include(pass_group)
195
+ end
196
+ end
197
+
198
+ context 'when all-passing example groups are already excluded' do
199
+ it 'reverses the exclusion of all-passing example groups' do
200
+ spec_selector.toggle_passing
201
+ expect(spec_selector.ivar(:@list)).not_to include(pass_group)
202
+ spec_selector.toggle_passing
203
+ expect(spec_selector.ivar(:@list)).to include(pass_group)
204
+ end
205
+ end
206
+ end
207
+
208
+ describe '#status_summary' do
209
+ let(:summary) { spec_selector.ivar(:@summary) }
210
+
211
+ before do
212
+ spec_selector.ivar_set(:@example_count, 25)
213
+ spec_selector.status_summary(notification)
214
+ end
215
+
216
+ it 'stores message indicating example total in @summary' do
217
+ expect(summary).to include(/Total Examples: 25/)
218
+ end
219
+
220
+ it 'stores message indicating total time to run examples in @summary' do
221
+ expect(summary).to include(/Finished in 1.5 seconds/)
222
+ end
223
+
224
+ it 'stores message indicating total time to load files in @summary' do
225
+ expect(summary).to include(/Files loaded in 0.5 seconds/)
226
+ end
227
+ end
228
+
229
+ describe '#display_list' do
230
+ before { allow(spec_selector).to receive(:test_data_summary) }
231
+
232
+ context 'when all examples have passed' do
233
+ it 'displays message indicating that all examples have passed' do
234
+ ivars_set(:@map => all_passing_map, :@list => [pass_group, pass_group])
235
+ allow(spec_selector).to receive(:all_passing?).and_return(true)
236
+ spec_selector.display_list
237
+ expect(output).to match(/ALL EXAMPLES PASSED/)
238
+ end
239
+ end
240
+
241
+ context 'when not all examples have passed' do
242
+ it 'does not display message indicating that all examples have passed' do
243
+ ivars_set(:@map => mixed_map, :@list => mixed_list)
244
+ allow(spec_selector).to receive(:all_passing?).and_return(false)
245
+ spec_selector.display_list
246
+ expect(output).not_to match(/ALL EXAMPLES PASSED/)
247
+ end
248
+ end
249
+
250
+ it 'displays list of example groups or examples in current level' do
251
+ ivars_set(:@map => mixed_map, :@list => mixed_list)
252
+ spec_selector.display_list
253
+ expect(output).to match(/[passing example group]/)
254
+ expect(output).to match(/[non-passing example group]/)
255
+ end
256
+ end
257
+
258
+ describe '#display_example' do
259
+ before { allow_methods(:test_data_summary, :navigate) }
260
+
261
+ context 'when example is pending' do
262
+ it 'displays example summary' do
263
+ summary_settings(pending_example)
264
+ spec_selector.display_example
265
+ expect(output).to match(/[pending example]/)
266
+ end
267
+ end
268
+
269
+ context 'when example is failed' do
270
+ it 'displays example summary' do
271
+ summary_settings(failed_example)
272
+ spec_selector.display_example
273
+ expect(output).to match(/[failed example]/)
274
+ end
275
+ end
276
+
277
+ context 'when example is passing' do
278
+ it 'displays text indicating the example passed' do
279
+ ivars_set(:@selected => passing_example, :@passed => [passing_example])
280
+ spec_selector.display_example
281
+ expect(output).to match(/[PASSED]/)
282
+ end
283
+ end
284
+ end
285
+
286
+ describe '#example_list' do
287
+ it 'returns example list corresponding to execution result status' do
288
+ spec_selector.ivar_set(:@selected, failed_example)
289
+ expect(spec_selector.example_list).to include(@failed)
290
+ end
291
+
292
+ context 'when example failed' do
293
+ let(:notification) do
294
+ build(:failed_example_notification, example: failed_example)
295
+ end
296
+
297
+ it 'returns failure summary of selected example' do
298
+ summary_settings(failed_example)
299
+ expect(spec_selector.example_list).to include(notification)
300
+ end
301
+ end
302
+
303
+ context 'when example is pending' do
304
+ let(:notification) do
305
+ build(:skipped_example_notification, example: pending_example)
306
+ end
307
+
308
+ it 'returns pending summary of selected example' do
309
+ summary_settings(pending_example)
310
+ expect(spec_selector.example_list).to include(notification)
311
+ end
312
+ end
313
+ end
314
+ end