From 4057089c20290c1a983a8e21d19663cd4dc486a8 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Tue, 11 Jan 2022 15:53:53 -0700 Subject: [PATCH] Support multiple block arguments in sample block https://github.com/icy-arctic-fox/spectator/issues/41#issuecomment-1010192486 --- CHANGELOG.md | 1 + spec/features/metadata_spec.cr | 10 ++++++++++ src/spectator/dsl/groups.cr | 10 ++++------ .../iterative_example_group_builder.cr | 20 ++++++++++++++----- 4 files changed, 30 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5078721..412c4f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Support string interpolation for example name/description. [#41](https://github.com/icy-arctic-fox/spectator/issues/41) +- Support multiple block arguments in `sample` block (`Hash#each`). [#41](https://github.com/icy-arctic-fox/spectator/issues/41#issuecomment-1010192486) ### Changed - Source line reported by failure list changed to line containing `expect` instead of example start line. diff --git a/spec/features/metadata_spec.cr b/spec/features/metadata_spec.cr index ce0ce1f..08f7756 100644 --- a/spec/features/metadata_spec.cr +++ b/spec/features/metadata_spec.cr @@ -16,4 +16,14 @@ Spectator.describe "Spec metadata" do expect(example.name).to eq("works with #{string}") end end + + def self.a_hash + {"foo" => 42, "bar" => 123, "baz" => 7} + end + + sample a_hash do |key, value| + it "works with #{key} = #{value}" do |example| + expect(example.name).to eq("works with #{key} = #{value}") + end + end end diff --git a/src/spectator/dsl/groups.cr b/src/spectator/dsl/groups.cr index f131246..6c678b7 100644 --- a/src/spectator/dsl/groups.cr +++ b/src/spectator/dsl/groups.cr @@ -95,18 +95,16 @@ module Spectator::DSL ::Spectator::DSL::Builder.start_iterative_group( \%collection, \{{collection.stringify}}, - \{{block.args.empty? ? :nil.id : block.args.first.stringify}}, + [\{{block.args.empty? ? "".id : block.args.map(&.stringify).splat}}] of String, ::Spectator::Location.new(\{{block.filename}}, \{{block.line_number}}, \{{block.end_line_number}}), metadata ) \{% if block %} - \{% if block.args.size == 1 %} - let(\{{block.args.first}}) do |example| - example.group.as(::Spectator::ExampleGroupIteration(typeof(Group\%group.\%collection.first))).item + \{% for arg, i in block.args %} + let(\{{arg}}) do |example| + example.group.as(::Spectator::ExampleGroupIteration(typeof(Group\%group.\%collection.first))).item[\{{i}}] end - \{% elsif block.args.size > 1 %} - \{% raise "Expected 1 argument for 'sample' block, but got #{block.args.size}" %} \{% end %} \{{block.body}} diff --git a/src/spectator/iterative_example_group_builder.cr b/src/spectator/iterative_example_group_builder.cr index 6bc866b..39ad549 100644 --- a/src/spectator/iterative_example_group_builder.cr +++ b/src/spectator/iterative_example_group_builder.cr @@ -13,8 +13,8 @@ module Spectator # Initially, the builder will have no children and no hooks. # The *name*, *location*, and *metadata* will be applied to the `ExampleGroup` produced by `#build`. # The *collection* is the set of items to create sub-nodes for. - # The *iterator* is an optional name given to a single item in the collection. - def initialize(@collection : Enumerable(T), name : String? = nil, @iterator : String? = nil, + # The *iterators* is a list of optional names given to items in the collection. + def initialize(@collection : Enumerable(T), name : String? = nil, @iterators : Array(String) = [] of String, location : Location? = nil, metadata : Metadata = Metadata.new) super(name, location, metadata) end @@ -38,10 +38,20 @@ module Spectator # Constructs the name of an example group iteration. private def iteration_name(item) - if iterator = @iterator - "#{iterator}: #{item.inspect}" + if item.is_a?(Tuple) && @iterators.size > 1 + item.zip?(@iterators).map do |(subitem, iterator)| + if iterator + "#{iterator}: #{subitem.inspect}" + else + subitem.inspect + end + end.join("; ") else - item.inspect + if iterator = @iterators.first? + "#{iterator}: #{item.inspect}" + else + item.inspect + end end end end