rubocop-rspec_parity 1.5.0 → 1.6.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/rubocop/cop/rspec_parity/sufficient_contexts.rb +38 -21
- data/lib/rubocop/rspec_parity/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2fc4286fcc69b46b490ae21db9ce9681ca7dd25577521e329d0bc3d89b33940a
|
|
4
|
+
data.tar.gz: da7872a16a143f8d44a178c7b1912996c8d5213fb1538a8f4231e9ceedf361dc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3d9f782158d6c053256401c6b687eb8b22dd1780f444b42783609b428ea0f82898a33e5c502818df86be8e56847a7be8012a99ead9c3aaffe91e5420d7be5a66
|
|
7
|
+
data.tar.gz: 44b7a03a46444bcacbec096e659fd435dc288278ba1f14d72d87311d1bca1b4feb32643c68c6fcb3ff2fbb5be1118adddddb8e4ec164b4d710cc00efbb287bc5
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.6.0] - 2026-06-11
|
|
4
|
+
|
|
5
|
+
Fixed: `SufficientContexts` now counts each `it`/`example` within a single context as a separate scenario, so specs that cover branches with multiple examples (instead of separate contexts) no longer trigger false violations
|
|
6
|
+
Updated: `SufficientContexts` violation message now explains that each branch needs one `context` or `it`, and that compound conditions like `a && b` need a scenario per operand
|
|
7
|
+
|
|
3
8
|
## [1.5.0] - 2026-05-21
|
|
4
9
|
|
|
5
10
|
Added: `SufficientContexts` now inlines branches from private/protected helpers that are called from exactly one site in the same class (controlled by the new `TraceSingleUsePrivateMethods` config key, default `true`). Violation messages list the helpers whose branches were counted.
|
|
@@ -42,8 +42,9 @@ module RuboCop
|
|
|
42
42
|
include DepartmentConfig
|
|
43
43
|
include SpecFileFinder
|
|
44
44
|
|
|
45
|
-
MSG = "Method `%<method_name>s` has %<branches>d %<branch_word>s but
|
|
46
|
-
"
|
|
45
|
+
MSG = "Method `%<method_name>s` has %<branches>d %<branch_word>s but the spec covers only " \
|
|
46
|
+
"%<contexts>d %<scenario_word>s. Add %<missing>d more %<missing_word>s " \
|
|
47
|
+
"(one `context` or `it` per branch; compound conditions like `a && b` need a scenario per operand)."
|
|
47
48
|
|
|
48
49
|
TRACED_SUFFIX = " (including branches from: %<traced>s)"
|
|
49
50
|
|
|
@@ -59,6 +60,9 @@ module RuboCop
|
|
|
59
60
|
/^autosave_/
|
|
60
61
|
].freeze
|
|
61
62
|
|
|
63
|
+
# Tallies extracted from a spec's text for a single method describe block.
|
|
64
|
+
ParsedSpec = Struct.new(:context_count, :example_count, :has_examples, :has_direct_examples)
|
|
65
|
+
|
|
62
66
|
def initialize(config = nil, options = nil)
|
|
63
67
|
super
|
|
64
68
|
@ignore_memoization = cop_config.fetch("IgnoreMemoization", true)
|
|
@@ -121,9 +125,9 @@ module RuboCop
|
|
|
121
125
|
branches: branches,
|
|
122
126
|
branch_word: pluralize("branch", branches),
|
|
123
127
|
contexts: contexts,
|
|
124
|
-
|
|
128
|
+
scenario_word: pluralize("scenario", contexts),
|
|
125
129
|
missing: missing,
|
|
126
|
-
missing_word: pluralize("
|
|
130
|
+
missing_word: pluralize("scenario", missing))
|
|
127
131
|
message += format(TRACED_SUFFIX, traced: traced_methods.join(", ")) if traced_methods.any?
|
|
128
132
|
message
|
|
129
133
|
end
|
|
@@ -231,21 +235,38 @@ module RuboCop
|
|
|
231
235
|
|
|
232
236
|
def count_contexts_for_method(spec_content, method_name)
|
|
233
237
|
method_pattern = Regexp.escape(method_name)
|
|
234
|
-
|
|
238
|
+
result = parse_spec_content(spec_content, method_pattern)
|
|
239
|
+
|
|
240
|
+
scenario_count(
|
|
241
|
+
result.context_count, result.example_count,
|
|
242
|
+
has_examples: result.has_examples, has_direct_examples: result.has_direct_examples
|
|
243
|
+
)
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# A test scenario is the smaller unit between "a context block" and "an
|
|
247
|
+
# `it`/`example`". A single context whose examples each exercise a branch
|
|
248
|
+
# covers as many scenarios as it has examples, so the scenario count is
|
|
249
|
+
# the larger of the context-based count and the raw example count. Empty
|
|
250
|
+
# placeholder contexts (no examples) still count via the context-based
|
|
251
|
+
# path, so this never under-counts relative to the old behaviour.
|
|
252
|
+
def scenario_count(context_count, example_count, has_examples:, has_direct_examples:)
|
|
253
|
+
context_based =
|
|
254
|
+
if context_count.positive? && has_direct_examples
|
|
255
|
+
context_count + 1
|
|
256
|
+
elsif context_count.zero? && has_examples
|
|
257
|
+
1
|
|
258
|
+
else
|
|
259
|
+
context_count
|
|
260
|
+
end
|
|
235
261
|
|
|
236
|
-
|
|
237
|
-
context_count + 1
|
|
238
|
-
elsif context_count.zero? && has_examples
|
|
239
|
-
1
|
|
240
|
-
else
|
|
241
|
-
context_count
|
|
242
|
-
end
|
|
262
|
+
[context_based, example_count].max
|
|
243
263
|
end
|
|
244
264
|
|
|
245
265
|
# rubocop:disable Metrics/MethodLength
|
|
246
266
|
def parse_spec_content(spec_content, method_pattern) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
247
267
|
in_method_block = false
|
|
248
268
|
context_count = 0
|
|
269
|
+
example_count = 0
|
|
249
270
|
has_examples = false
|
|
250
271
|
has_direct_examples = false
|
|
251
272
|
base_indent = 0
|
|
@@ -269,6 +290,7 @@ module RuboCop
|
|
|
269
290
|
context_count += 1
|
|
270
291
|
elsif nested_example?(line)
|
|
271
292
|
has_examples = true
|
|
293
|
+
example_count += 1
|
|
272
294
|
child_indent ||= current_indent
|
|
273
295
|
has_direct_examples = true if current_indent == child_indent
|
|
274
296
|
end
|
|
@@ -277,7 +299,7 @@ module RuboCop
|
|
|
277
299
|
end
|
|
278
300
|
end
|
|
279
301
|
|
|
280
|
-
|
|
302
|
+
ParsedSpec.new(context_count, example_count, has_examples, has_direct_examples)
|
|
281
303
|
end
|
|
282
304
|
# rubocop:enable Metrics/MethodLength
|
|
283
305
|
|
|
@@ -307,7 +329,6 @@ module RuboCop
|
|
|
307
329
|
|
|
308
330
|
case word
|
|
309
331
|
when "branch" then "branches"
|
|
310
|
-
when "context" then "contexts"
|
|
311
332
|
else "#{word}s"
|
|
312
333
|
end
|
|
313
334
|
end
|
|
@@ -447,6 +468,7 @@ module RuboCop
|
|
|
447
468
|
# Count contexts/describes and examples at the top level (under class describe)
|
|
448
469
|
base_indent = lines[describe_line_index].match(/^(\s*)/)[1].length
|
|
449
470
|
context_count = 0
|
|
471
|
+
example_count = 0
|
|
450
472
|
has_examples = false
|
|
451
473
|
has_direct_examples = false
|
|
452
474
|
child_indent = nil
|
|
@@ -462,18 +484,13 @@ module RuboCop
|
|
|
462
484
|
context_count += 1
|
|
463
485
|
elsif line.match?(/^\s*(?:it|example|specify)\s+/)
|
|
464
486
|
has_examples = true
|
|
487
|
+
example_count += 1
|
|
465
488
|
child_indent ||= indent
|
|
466
489
|
has_direct_examples = true if indent == child_indent
|
|
467
490
|
end
|
|
468
491
|
end
|
|
469
492
|
|
|
470
|
-
|
|
471
|
-
context_count + 1
|
|
472
|
-
elsif context_count.zero? && has_examples
|
|
473
|
-
1
|
|
474
|
-
else
|
|
475
|
-
context_count
|
|
476
|
-
end
|
|
493
|
+
scenario_count(context_count, example_count, has_examples:, has_direct_examples:)
|
|
477
494
|
end
|
|
478
495
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
479
496
|
end
|