From 92e839415da2e31edca76737c2677e0ce3e7aeb8 Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Wed, 26 Jan 2022 13:14:30 -0700 Subject: [PATCH 1/5] Fix sample iteration with single block arg (not tuple) --- CHANGELOG.md | 4 ++++ spec/issues/github_issue_41_spec.cr | 30 +++++++++++++++++++++++++++++ src/spectator/dsl/groups.cr | 12 +++++++++--- 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 spec/issues/github_issue_41_spec.cr diff --git a/CHANGELOG.md b/CHANGELOG.md index e4be897..a8e26df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +### +- Fixed usage of `sample` with single block argument. [#41](https://github.com/icy-arctic-fox/spectator/issues/41#issuecomment-1022525702) + ## [0.10.4] - 2022-01-11 ### Added - Support string interpolation for example name/description. [#41](https://github.com/icy-arctic-fox/spectator/issues/41) diff --git a/spec/issues/github_issue_41_spec.cr b/spec/issues/github_issue_41_spec.cr new file mode 100644 index 0000000..1b75e40 --- /dev/null +++ b/spec/issues/github_issue_41_spec.cr @@ -0,0 +1,30 @@ +require "../spec_helper" + +Spectator.describe "GitHub Issue #41" do + sample [1, 2, 3] do |i| + it "is itself" do + expect(i).to eq i + end + end + + def self.an_array + [1, 2, 3] + end + + sample an_array do |i| + it "is itself" do + expect(i).to eq(i) + end + end + + # NOTE: NamedTuple does not work, must be Enumerable(T) for `sample`. + def self.a_hash + {:a => "a", :b => "b", :c => "c"} + end + + sample a_hash do |k, v| + it "works on hashes" do + expect(v).to eq(k.to_s) + end + end +end diff --git a/src/spectator/dsl/groups.cr b/src/spectator/dsl/groups.cr index 6c678b7..0e7b47b 100644 --- a/src/spectator/dsl/groups.cr +++ b/src/spectator/dsl/groups.cr @@ -101,9 +101,15 @@ module Spectator::DSL ) \{% if block %} - \{% for arg, i in block.args %} - let(\{{arg}}) do |example| - example.group.as(::Spectator::ExampleGroupIteration(typeof(Group\%group.\%collection.first))).item[\{{i}}] + \{% if block.args.size > 1 %} + \{% for arg, i in block.args %} + let(\{{arg}}) do |example| + example.group.as(::Spectator::ExampleGroupIteration(typeof(Group\%group.\%collection.first))).item[\{{i}}] + end + \{% end %} + \{% else %} + let(\{{block.args[0]}}) do |example| + example.group.as(::Spectator::ExampleGroupIteration(typeof(Group\%group.\%collection.first))).item end \{% end %} From cbe05cd637c14387df553b0ea8f0a879c732c5fd Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Thu, 27 Jan 2022 13:04:03 -0700 Subject: [PATCH 2/5] Release v0.10.5 --- CHANGELOG.md | 9 +++++---- README.md | 2 +- shard.yml | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a8e26df..5ce5e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] -### +## [0.10.5] - 2022-01-27 +### Fixed - Fixed usage of `sample` with single block argument. [#41](https://github.com/icy-arctic-fox/spectator/issues/41#issuecomment-1022525702) ## [0.10.4] - 2022-01-11 @@ -371,8 +371,9 @@ This has been changed so that it compiles and raises an error at runtime with a First version ready for public use. -[Unreleased]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.10.4...master -[0.10.3]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.10.3...v0.10.4 +[Unreleased]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.10.5...master +[0.10.5]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.10.4...v0.10.5 +[0.10.4]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.10.3...v0.10.4 [0.10.3]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.10.2...v0.10.3 [0.10.2]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.10.1...v0.10.2 [0.10.1]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.10.0...v0.10.1 diff --git a/README.md b/README.md index 53d347d..78a6b17 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Add this to your application's `shard.yml`: development_dependencies: spectator: gitlab: arctic-fox/spectator - version: ~> 0.10.4 + version: ~> 0.10.5 ``` Usage diff --git a/shard.yml b/shard.yml index 343ed19..3eaf627 100644 --- a/shard.yml +++ b/shard.yml @@ -1,5 +1,5 @@ name: spectator -version: 0.10.4 +version: 0.10.5 description: | A feature-rich spec testing framework for Crystal with similarities to RSpec. From eb8bd8892798a8d46dc977d86a7e60d02f76c89d Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Mon, 21 Feb 2022 18:17:44 -0700 Subject: [PATCH 3/5] Handle case with typeless block Fixes syntax: `stub method(&block)` To stub a block with args, use: `stub method(&block : Type -> Type)` Addresses https://github.com/icy-arctic-fox/spectator/issues/42 --- spec/issues/github_issue_42_spec.cr | 43 +++++++++++++++++++++ src/spectator/mocks/stubs.cr | 60 +++++++++++++++++++++-------- 2 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 spec/issues/github_issue_42_spec.cr diff --git a/spec/issues/github_issue_42_spec.cr b/spec/issues/github_issue_42_spec.cr new file mode 100644 index 0000000..9ac6603 --- /dev/null +++ b/spec/issues/github_issue_42_spec.cr @@ -0,0 +1,43 @@ +require "../spec_helper" + +abstract class SdkInterface + abstract def register_hook(name, &block) +end + +class Example + def initialize(@sdk : Sdk) + end + + def configure + @sdk.register_hook("name") do + nil + end + end +end + +class Sdk < SdkInterface + def initialize + end + + def register_hook(name, &block) + nil + end +end + +Spectator.describe Example do + mock Sdk do + stub register_hook(name, &block) + end + + describe "#configure" do + it "registers a block on configure" do + sdk = Sdk.new + example_class = Example.new(sdk) + allow(sdk).to receive(register_hook()) + + example_class.configure + + expect(sdk).to have_received(register_hook()).with("name") + end + end +end diff --git a/src/spectator/mocks/stubs.cr b/src/spectator/mocks/stubs.cr index d35ceab..d2bd8fc 100644 --- a/src/spectator/mocks/stubs.cr +++ b/src/spectator/mocks/stubs.cr @@ -7,11 +7,13 @@ module Spectator::Mocks params = nil args = nil body = nil + block_arg = nil if definition.is_a?(Call) # stub foo { :bar } receiver = definition.receiver.id named = false name = definition.name.id params = definition.args + block_arg = definition.block_arg if params.last.is_a?(Call) body = params.last.block params[-1] = params.last.name @@ -100,28 +102,54 @@ module Spectator::Mocks end end - def {{receiver}}{{name}}({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %} - if (%harness = ::Spectator::Harness.current?) - %args = ::Spectator::Mocks::GenericArguments.create({{args.splat}}) - %call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args) - %harness.mocks.record_call(self, %call) - if (%stub = %harness.mocks.find_stub(self, %call)) - return %stub.call!(%args) { {{fallback}} { |*%ya| yield *%ya } } - end + {% if block_arg.is_a?(Call) %} + def {{receiver}}{{name}}({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %} + if (%harness = ::Spectator::Harness.current?) + %args = ::Spectator::Mocks::GenericArguments.create({{args.splat}}) + %call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args) + %harness.mocks.record_call(self, %call) + if (%stub = %harness.mocks.find_stub(self, %call)) + return %stub.call!(%args) { {{fallback}} { yield } } + end - {% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %} - %method({{args.splat}}) { |*%ya| yield *%ya } - {% else %} + {% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %} + %method({{args.splat}}) { yield } + {% else %} + {{fallback}} do + yield + end + {% end %} + else + {{fallback}} do + yield + end + end + end + + {% else %} + def {{receiver}}{{name}}({{params.splat}}){% if return_type.is_a?(ArrayLiteral) %} : {{return_type.type}}{% elsif return_type != :undefined %} : {{return_type.id}}{% elsif definition.is_a?(TypeDeclaration) %} : {{definition.type}}{% end %} + if (%harness = ::Spectator::Harness.current?) + %args = ::Spectator::Mocks::GenericArguments.create({{args.splat}}) + %call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args) + %harness.mocks.record_call(self, %call) + if (%stub = %harness.mocks.find_stub(self, %call)) + return %stub.call!(%args) { {{fallback}} { |*%ya| yield *%ya } } + end + + {% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %} + %method({{args.splat}}) { |*%ya| yield *%ya } + {% else %} + {{fallback}} do |*%yield_args| + yield *%yield_args + end + {% end %} + else {{fallback}} do |*%yield_args| yield *%yield_args end - {% end %} - else - {{fallback}} do |*%yield_args| - yield *%yield_args end end - end + {% end %} end end end From 35f8779ceb453a8c711ac031854023d7cf41cf4f Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Tue, 22 Feb 2022 16:36:16 -0700 Subject: [PATCH 4/5] Forward Example procsy to_s to example https://gitlab.com/arctic-fox/spectator/-/issues/70 --- CHANGELOG.md | 4 ++++ src/spectator/example.cr | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ce5e20..74b09f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] +### Changed +- Forward example procsy `to_s` to underlying example. [#70](https://gitlab.com/arctic-fox/spectator/-/issues/70) + ## [0.10.5] - 2022-01-27 ### Fixed - Fixed usage of `sample` with single block argument. [#41](https://github.com/icy-arctic-fox/spectator/issues/41#issuecomment-1022525702) diff --git a/src/spectator/example.cr b/src/spectator/example.cr index 93135a8..8f99e93 100644 --- a/src/spectator/example.cr +++ b/src/spectator/example.cr @@ -283,6 +283,12 @@ module Spectator # Allow instance to behave like an example. forward_missing_to @example + + # Constructs the full name or description of the example. + # This prepends names of groups this example is part of. + def to_s(io) : Nil + @example.to_s(io) + end end end end From 31d68a9ca3b66cbb7f4e3cc3589a47b1620c133b Mon Sep 17 00:00:00 2001 From: Michael Miller Date: Tue, 22 Feb 2022 16:55:11 -0700 Subject: [PATCH 5/5] Don't capture original block as proc --- src/spectator/mocks/exception_method_stub.cr | 2 +- src/spectator/mocks/method_stub.cr | 4 ++-- src/spectator/mocks/multi_value_method_stub.cr | 2 +- src/spectator/mocks/nil_method_stub.cr | 2 +- src/spectator/mocks/original_method_stub.cr | 2 +- src/spectator/mocks/proc_method_stub.cr | 2 +- src/spectator/mocks/value_method_stub.cr | 2 +- 7 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/spectator/mocks/exception_method_stub.cr b/src/spectator/mocks/exception_method_stub.cr index 2f223fd..ce34b64 100644 --- a/src/spectator/mocks/exception_method_stub.cr +++ b/src/spectator/mocks/exception_method_stub.cr @@ -7,7 +7,7 @@ module Spectator::Mocks super(name, location, args) end - def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT + def call(_args : GenericArguments(T, NT), & : -> RT) forall T, NT, RT raise @exception end end diff --git a/src/spectator/mocks/method_stub.cr b/src/spectator/mocks/method_stub.cr index 9ad78a3..bf9d0e6 100644 --- a/src/spectator/mocks/method_stub.cr +++ b/src/spectator/mocks/method_stub.cr @@ -14,9 +14,9 @@ module Spectator::Mocks @name == call.name end - abstract def call(args : GenericArguments(T, NT), &original : -> RT) forall T, NT, RT + abstract def call(args : GenericArguments(T, NT), & : -> RT) forall T, NT, RT - def call!(args : GenericArguments(T, NT), &original : -> RT) : RT forall T, NT, RT + def call!(args : GenericArguments(T, NT), & : -> RT) : RT forall T, NT, RT value = call(args) { |*ya| yield *ya } if value.is_a?(RT) value.as(RT) diff --git a/src/spectator/mocks/multi_value_method_stub.cr b/src/spectator/mocks/multi_value_method_stub.cr index f025271..944e985 100644 --- a/src/spectator/mocks/multi_value_method_stub.cr +++ b/src/spectator/mocks/multi_value_method_stub.cr @@ -10,7 +10,7 @@ module Spectator::Mocks raise ArgumentError.new("Values must have at least one item") if @values.size < 1 end - def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT + def call(_args : GenericArguments(T, NT), & : -> RT) forall T, NT, RT value = @values[@index] @index += 1 if @index < @values.size - 1 value diff --git a/src/spectator/mocks/nil_method_stub.cr b/src/spectator/mocks/nil_method_stub.cr index 1e87db6..38b1aab 100644 --- a/src/spectator/mocks/nil_method_stub.cr +++ b/src/spectator/mocks/nil_method_stub.cr @@ -4,7 +4,7 @@ require "./value_method_stub" module Spectator::Mocks class NilMethodStub < GenericMethodStub(Nil) - def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT + def call(_args : GenericArguments(T, NT), & : -> RT) forall T, NT, RT nil end diff --git a/src/spectator/mocks/original_method_stub.cr b/src/spectator/mocks/original_method_stub.cr index 2097eb6..041325a 100644 --- a/src/spectator/mocks/original_method_stub.cr +++ b/src/spectator/mocks/original_method_stub.cr @@ -3,7 +3,7 @@ require "./generic_method_stub" module Spectator::Mocks class OriginalMethodStub < GenericMethodStub(Nil) - def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT + def call(_args : GenericArguments(T, NT), & : -> RT) forall T, NT, RT yield end end diff --git a/src/spectator/mocks/proc_method_stub.cr b/src/spectator/mocks/proc_method_stub.cr index de3a68d..a72eb3c 100644 --- a/src/spectator/mocks/proc_method_stub.cr +++ b/src/spectator/mocks/proc_method_stub.cr @@ -11,7 +11,7 @@ module Spectator::Mocks ProcMethodStub.new(name, location, block, args) end - def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT + def call(_args : GenericArguments(T, NT), & : -> RT) forall T, NT, RT @proc.call end end diff --git a/src/spectator/mocks/value_method_stub.cr b/src/spectator/mocks/value_method_stub.cr index d3e7f5e..cda6b22 100644 --- a/src/spectator/mocks/value_method_stub.cr +++ b/src/spectator/mocks/value_method_stub.cr @@ -7,7 +7,7 @@ module Spectator::Mocks super(name, location, args) end - def call(_args : GenericArguments(T, NT), &_original : -> RT) forall T, NT, RT + def call(_args : GenericArguments(T, NT), & : -> RT) forall T, NT, RT @value end end