diff --git a/.rubocop.yml b/.rubocop.yml index 766d3f8..308b92b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,4 @@ -require: +plugins: - rubocop-performance AllCops: diff --git a/allure-cucumber/lib/allure_cucumber/models/cucumber_model.rb b/allure-cucumber/lib/allure_cucumber/models/cucumber_model.rb index 6573c3f..18fa86e 100644 --- a/allure-cucumber/lib/allure_cucumber/models/cucumber_model.rb +++ b/allure-cucumber/lib/allure_cucumber/models/cucumber_model.rb @@ -26,6 +26,7 @@ def test_result(test_case) description_html: scenario.description, history_id: scenario.id, full_name: scenario.name, + title_path: scenario.title_path, labels: parser.labels, links: parser.links, parameters: parser.parameters, diff --git a/allure-cucumber/lib/allure_cucumber/models/scenario.rb b/allure-cucumber/lib/allure_cucumber/models/scenario.rb index 91598b3..1c4da62 100644 --- a/allure-cucumber/lib/allure_cucumber/models/scenario.rb +++ b/allure-cucumber/lib/allure_cucumber/models/scenario.rb @@ -25,6 +25,12 @@ def feature_name @feature_name ||= feature.name end + # Title path without the final scenario display name. + # @return [Array] + def title_path + [feature_path, feature_name, rule_name].compact + end + # Scenario name # @return [String] def name @@ -52,19 +58,61 @@ def tags # Feature file name # @return [String] def feature_file_name - @feature_file_name ||= test_case.location.file.split("/").last.gsub(".feature", "") + @feature_file_name ||= File.basename(location_file, ".feature") end # Feature folder # @return [String] def feature_folder - @feature_folder ||= test_case.location.file.split("/")[-2] + @feature_folder ||= begin + directory = File.dirname(location_file) + directory == "." ? nil : File.basename(directory) + end end private attr_reader :test_case, :scenario_source, :feature + # @return [String] + def feature_path + @feature_path ||= location_file.delete_prefix("./") + end + + # @return [String] + def location_file + @location_file ||= test_case.location.file + end + + # @return [String, nil] + def rule_name + @rule_name ||= rule&.name + end + + # @return [Object, nil] + def rule + return unless scenario_id + + @rule ||= Array(feature.children).find do |child| + child.respond_to?(:rule) && child.rule && + rule_scenario_ids(child.rule).include?(scenario_id) + end&.rule + end + + # @return [Array] + def rule_scenario_ids(rule) + Array(rule.children).filter_map do |child| + next unless child.respond_to?(:scenario) + + child.scenario&.id + end + end + + # @return [String, nil] + def scenario_id + @scenario_id ||= scenario.respond_to?(:id) ? scenario.id : nil + end + # Is scenario outline # @return [Boolean] def scenario_outline? diff --git a/allure-cucumber/spec/unit/formatter_test_case_started_spec.rb b/allure-cucumber/spec/unit/formatter_test_case_started_spec.rb index 1c0630d..5646bfe 100644 --- a/allure-cucumber/spec/unit/formatter_test_case_started_spec.rb +++ b/allure-cucumber/spec/unit/formatter_test_case_started_spec.rb @@ -56,6 +56,7 @@ expect(arg.name).to eq(scenario) expect(arg.description).to eq("Simple scenario description") expect(arg.full_name).to eq(scenario) + expect(arg.title_path).to eq(["#{test_tmp_dir}/features/test.feature", feature]) expect(arg.links).to be_empty expect(arg.parameters).to be_empty expect(arg.labels).to match_array(labels) @@ -223,4 +224,20 @@ end end end + + context "feature hierarchy" do + it "adds title path for rules" do + run_cucumber_cli(<<~FEATURE) + Feature: #{feature} + + Rule: Core scenarios + Scenario: #{scenario} + Given a is 5 + FEATURE + + expect(lifecycle).to have_received(:start_test_case).once do |arg| + expect(arg.title_path).to eq(["#{test_tmp_dir}/features/test.feature", feature, "Core scenarios"]) + end + end + end end diff --git a/allure-rspec/lib/allure_rspec/formatter.rb b/allure-rspec/lib/allure_rspec/formatter.rb index 4a36488..e475a00 100644 --- a/allure-rspec/lib/allure_rspec/formatter.rb +++ b/allure-rspec/lib/allure_rspec/formatter.rb @@ -108,6 +108,7 @@ def test_result(example) description_html: "Location - #{strip_relative(parser.location)}", history_id: example.id, full_name: example.full_description, + title_path: parser.title_path, labels: parser.labels, links: parser.links, status_details: parser.status_details, diff --git a/allure-rspec/lib/allure_rspec/metadata_parser.rb b/allure-rspec/lib/allure_rspec/metadata_parser.rb index e843d6f..63aa627 100644 --- a/allure-rspec/lib/allure_rspec/metadata_parser.rb +++ b/allure-rspec/lib/allure_rspec/metadata_parser.rb @@ -67,6 +67,12 @@ def status_details ) end + # Title path without the final example display name. + # @return [Array] + def title_path + [strip_relative(example.file_path), *SuiteLabels.new(example.example_group).title_path] + end + # Example location # # @return [String] diff --git a/allure-rspec/lib/allure_rspec/suite_labels.rb b/allure-rspec/lib/allure_rspec/suite_labels.rb index 1eb89d1..b77d199 100644 --- a/allure-rspec/lib/allure_rspec/suite_labels.rb +++ b/allure-rspec/lib/allure_rspec/suite_labels.rb @@ -13,9 +13,7 @@ def initialize(example_group) # Get test suite labels # @return [Array] def fetch - parents = example_group.parent_groups.map do |group| - group.description.empty? ? "Anonymous" : group.description - end + parents = parent_groups labels = [] labels << Allure::ResultUtils.suite_label(suite(parents)) @@ -25,10 +23,23 @@ def fetch labels end + # Get title path from outermost group to innermost group. + # @return [Array] + def title_path + parent_groups.reverse + end + private attr_reader :example_group + # @return [Array] + def parent_groups + @parent_groups ||= example_group.parent_groups.map do |group| + group.description.empty? ? "Anonymous" : group.description + end + end + # @param [Array] parents # @return [String] def suite(parents) diff --git a/allure-rspec/spec/unit/formatter_example_started_spec.rb b/allure-rspec/spec/unit/formatter_example_started_spec.rb index f9883e5..c993b79 100644 --- a/allure-rspec/spec/unit/formatter_example_started_spec.rb +++ b/allure-rspec/spec/unit/formatter_example_started_spec.rb @@ -44,6 +44,7 @@ expect(arg.name).to eq(spec) expect(arg.description).to eq("Location - #{test_tmp_dir}/spec/test_spec.rb:10") expect(arg.full_name).to eq("#{suite} #{spec}") + expect(arg.title_path).to eq(["#{test_tmp_dir}/spec/test_spec.rb", suite]) expect(arg.links).to be_empty expect(arg.parameters).to be_empty expect(arg.history_id).to eq(Digest::MD5.hexdigest("./#{test_tmp_dir}/spec/test_spec.rb[1:1]")) @@ -256,15 +257,20 @@ aggregate_failures "Examples should contain correct suite labels" do expect(examples.first.labels).to include(Allure::ResultUtils.suite_label("Suite")) + expect(examples.first.title_path).to eq(["#{test_tmp_dir}/spec/test_spec.rb", "Suite"]) expect(examples[1].labels).to include( Allure::ResultUtils.suite_label("Nested Suite 1"), Allure::ResultUtils.parent_suite_label("Suite") ) + expect(examples[1].title_path).to eq(["#{test_tmp_dir}/spec/test_spec.rb", "Suite", "Nested Suite 1"]) expect(examples.last.labels).to include( Allure::ResultUtils.suite_label("Nested Suite 2"), Allure::ResultUtils.parent_suite_label("Suite"), Allure::ResultUtils.sub_suite_label("Nested Suite 2:1:1 > Nested Suite 2:1") ) + expect(examples.last.title_path).to eq( + ["#{test_tmp_dir}/spec/test_spec.rb", "Suite", "Nested Suite 2", "Nested Suite 2:1", "Nested Suite 2:1:1"] + ) end end end diff --git a/allure-ruby-commons/lib/allure_ruby_commons/model/test_result.rb b/allure-ruby-commons/lib/allure_ruby_commons/model/test_result.rb index 9f4134f..84e01f6 100644 --- a/allure-ruby-commons/lib/allure_ruby_commons/model/test_result.rb +++ b/allure-ruby-commons/lib/allure_ruby_commons/model/test_result.rb @@ -9,6 +9,7 @@ class TestResult < ExecutableItem # @param [Hash] options # @option options [String] :name # @option options [String] :full_name + # @option options [Array] :title_path # @option options [String] :description # @option options [String] :description_html # @option options [String] :status ('broken') @@ -26,6 +27,7 @@ def initialize(uuid: SecureRandom.uuid, history_id: SecureRandom.uuid, environme @uuid = uuid @history_id = history_id @full_name = options[:full_name] || "Unnamed" + @title_path = options[:title_path] @labels = options[:labels] || [] @links = options[:links] || [] @parameters << Parameter.new("environment", environment) if environment @@ -33,6 +35,7 @@ def initialize(uuid: SecureRandom.uuid, history_id: SecureRandom.uuid, environme attr_accessor :uuid, :full_name, + :title_path, :labels, :links diff --git a/allure-ruby-commons/spec/unit/test_result_spec.rb b/allure-ruby-commons/spec/unit/test_result_spec.rb index d9c016e..6a7d884 100644 --- a/allure-ruby-commons/spec/unit/test_result_spec.rb +++ b/allure-ruby-commons/spec/unit/test_result_spec.rb @@ -80,6 +80,20 @@ end end + context "title path" do + let(:environment) { nil } + let(:title_path) { ["spec/test_spec.rb", "Suite", "Nested Suite"] } + let!(:test_case) { start_test_case(name: "Test Case", title_path: title_path, environment: environment) } + + it "stores title path on test results" do + expect(test_case.title_path).to eq(title_path) + end + + it "serializes title path as titlePath" do + expect(test_case.to_hash["titlePath"]).to eq(title_path) + end + end + context "with allure environment" do let(:environment) { "test" }