Skip to content

Commit a5a01bd

Browse files
committed
Add --single-process-tag option to run specific Cucumber/Gherkin scenarios sequentially
1 parent 9bc9233 commit a5a01bd

File tree

3 files changed

+53
-12
lines changed

3 files changed

+53
-12
lines changed

lib/parallel_tests/cli.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,11 @@ def parse_options!(argv)
208208
(options[:single_process] ||= []) << /#{pattern}/
209209
end
210210

211-
opts.on("-i", "--isolate", "Do not run any other tests in the group used by --single(-s)") do
211+
opts.on("--single-tag [TAG]", "Run all Cucumber/Gherkin scenarios with specified tag in the same process") do |tag|
212+
options[:single_process_tag] = tag
213+
end
214+
215+
opts.on("-i", "--isolate", "Do not run any other tests in the group used by --single(-s) or --single-tag") do
212216
options[:isolate] = true
213217
end
214218

lib/parallel_tests/cucumber/scenario_line_logger.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def visit_feature_element(uri, feature_element, feature_tags, line_numbers: [])
2121
# or if it is not at the correct location
2222
return if line_numbers.any? && !line_numbers.include?(test_line)
2323

24-
@scenarios << [uri, feature_element.source_line].join(":")
24+
@scenarios << [[uri, feature_element.source_line].join(":"), scenario_tags]
2525
else # :ScenarioOutline
2626
feature_element.examples.each do |example|
2727
example_tags = example.tags.map(&:name)
@@ -31,7 +31,7 @@ def visit_feature_element(uri, feature_element, feature_tags, line_numbers: [])
3131
test_line = row.source_line
3232
next if line_numbers.any? && !line_numbers.include?(test_line)
3333

34-
@scenarios << [uri, test_line].join(':')
34+
@scenarios << [[uri, test_line].join(':'), scenario_tags]
3535
end
3636
end
3737
end

lib/parallel_tests/grouper.rb

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# frozen_string_literal: true
22
module ParallelTests
33
class Grouper
4+
BY_SCENARIOS_SUPPORTED_OPTIONS = [:single_process_tag].freeze
5+
46
class << self
57
def by_steps(tests, num_groups, options)
68
features_with_steps = group_by_features_with_steps(tests, options)
@@ -9,20 +11,16 @@ def by_steps(tests, num_groups, options)
911

1012
def by_scenarios(tests, num_groups, options = {})
1113
scenarios = group_by_scenarios(tests, options)
12-
in_even_groups_by_size(scenarios, num_groups)
14+
in_even_groups_by_size(scenarios, num_groups, options.slice(BY_SCENARIOS_SUPPORTED_OPTIONS))
1315
end
1416

1517
def in_even_groups_by_size(items, num_groups, options = {})
1618
groups = Array.new(num_groups) { { items: [], size: 0 } }
1719

1820
return specify_groups(items, num_groups, options, groups) if options[:specify_groups]
1921

20-
# add all files that should run in a single process to one group
21-
single_process_patterns = options[:single_process] || []
22-
23-
single_items, items = items.partition do |item, _size|
24-
single_process_patterns.any? { |pattern| item =~ pattern }
25-
end
22+
# add all files/scenarios that should run in a single process to one group
23+
single_items, items = separate_single_items(items, options)
2624

2725
isolate_count = isolate_count(options)
2826

@@ -41,7 +39,7 @@ def in_even_groups_by_size(items, num_groups, options = {})
4139
group_features_by_size(items_to_group(items), groups[isolate_count..-1])
4240
else
4341
# add all files that should run in a single non-isolated process to first group
44-
single_items.each { |item, size| add_to_group(groups.first, item, size) }
42+
group_features_by_size(items_to_group(single_items), [groups.first])
4543

4644
# group all by size
4745
group_features_by_size(items_to_group(items), groups)
@@ -129,6 +127,22 @@ def group_by_scenarios(tests, options = {})
129127
ParallelTests::Cucumber::Scenarios.all(tests, options)
130128
end
131129

130+
def separate_single_items(items, options)
131+
items.partition { |item, _size| to_single_items?(item, options) }
132+
end
133+
134+
def to_single_items?(item, options)
135+
if options[:single_process]
136+
options[:single_process].any? { |pattern| item =~ pattern }
137+
elsif options[:single_process_tag]
138+
raise "--single-tag option can be used only with '--group-by scenarios'" unless item_with_tags?(item)
139+
item_tags = item[1]
140+
item_tags.any? { |tag| tag.match?(options[:single_process_tag]) }
141+
else
142+
false
143+
end
144+
end
145+
132146
def group_features_by_size(items, groups_to_fill)
133147
items.each do |item, size|
134148
size ||= 1
@@ -138,7 +152,30 @@ def group_features_by_size(items, groups_to_fill)
138152
end
139153

140154
def items_to_group(items)
141-
items.first && items.first.size == 2 ? largest_first(items) : items
155+
return items_without_tags(items) if items_with_tags?(items)
156+
return largest_first(items) if items_with_size?(items)
157+
158+
items
159+
end
160+
161+
def items_with_tags?(items)
162+
items.first.is_a?(Array) && item_with_tags?(items.first)
163+
end
164+
165+
def items_with_size?(items)
166+
items.first.is_a?(Array) && item_with_size?(items.first)
167+
end
168+
169+
def item_with_tags?(item)
170+
item[1].is_a?(Array)
171+
end
172+
173+
def item_with_size?(item)
174+
item[1].is_a?(Numeric)
175+
end
176+
177+
def items_without_tags(items)
178+
items.map(&:first)
142179
end
143180
end
144181
end

0 commit comments

Comments
 (0)