diff --git a/spec/std/array_spec.cr b/spec/std/array_spec.cr index 63b0f87aeaf4..19b068be439e 100644 --- a/spec/std/array_spec.cr +++ b/spec/std/array_spec.cr @@ -1096,7 +1096,7 @@ describe "Array" do describe "to_s" do it "does to_s" do - it { [1, 2, 3].to_s.should eq("[1, 2, 3]") } + [1, 2, 3].to_s.should eq("[1, 2, 3]") end it "does with recursive" do diff --git a/spec/std/char_spec.cr b/spec/std/char_spec.cr index 65d31d984d4e..dce9d853ba42 100644 --- a/spec/std/char_spec.cr +++ b/spec/std/char_spec.cr @@ -373,10 +373,10 @@ describe "Char" do end it "does number?" do - it { '1'.number?.should be_true } - it { '٠'.number?.should be_true } - it { '٢'.number?.should be_true } - it { 'a'.number?.should be_false } + '1'.number?.should be_true + '٠'.number?.should be_true + '٢'.number?.should be_true + 'a'.number?.should be_false end it "does ascii_control?" do diff --git a/spec/std/deque_spec.cr b/spec/std/deque_spec.cr index 2f3824bebb56..00d4a6ebbfe9 100644 --- a/spec/std/deque_spec.cr +++ b/spec/std/deque_spec.cr @@ -545,7 +545,7 @@ describe "Deque" do describe "to_s" do it "does to_s" do - it { Deque{1, 2, 3}.to_s.should eq("Deque{1, 2, 3}") } + Deque{1, 2, 3}.to_s.should eq("Deque{1, 2, 3}") end it "does with recursive" do diff --git a/spec/std/float_spec.cr b/spec/std/float_spec.cr index a3f4609e0aa5..b14c6a07c59b 100644 --- a/spec/std/float_spec.cr +++ b/spec/std/float_spec.cr @@ -14,7 +14,7 @@ describe "Float" do describe "%" do it "uses modulo behavior, not remainder behavior" do - it { ((-11.5) % 4.0).should eq(0.5) } + ((-11.5) % 4.0).should eq(0.5) end end diff --git a/spec/std/spec_spec.cr b/spec/std/spec_spec.cr index e8502ebe9a21..030e779bdcdc 100644 --- a/spec/std/spec_spec.cr +++ b/spec/std/spec_spec.cr @@ -114,6 +114,18 @@ describe "Spec matchers" do true.should be_truthy end end + + it "detects a nesting `it`" do + ex = expect_raises(Spec::NestingSpecError) { it { } } + ex.message.should eq "can't nest `it` or `pending`" + ex.file.should eq __FILE__ + end + + it "detects a nesting `pending`" do + ex = expect_raises(Spec::NestingSpecError) { pending } + ex.message.should eq "can't nest `it` or `pending`" + ex.file.should eq __FILE__ + end end describe "Spec" do diff --git a/src/spec/context.cr b/src/spec/context.cr index f59a3005bacb..2505574ced7e 100644 --- a/src/spec/context.cr +++ b/src/spec/context.cr @@ -81,7 +81,7 @@ module Spec puts puts "#{(i + 1).to_s.rjust(3, ' ')}) #{fail.description}" - if ex.is_a?(AssertionFailed) + if ex.is_a?(SpecError) source_line = Spec.read_line(ex.file, ex.line) if source_line puts Spec.color(" Failure/Error: #{source_line.strip}", :error) @@ -89,13 +89,13 @@ module Spec end puts - message = ex.is_a?(AssertionFailed) ? ex.to_s : ex.inspect_with_backtrace + message = ex.is_a?(SpecError) ? ex.to_s : ex.inspect_with_backtrace message.split('\n').each do |line| print " " puts Spec.color(line, :error) end - if ex.is_a?(AssertionFailed) + if ex.is_a?(SpecError) puts puts Spec.color(" # #{Spec.relative_file(ex.file)}:#{ex.line}", :comment) end @@ -166,6 +166,19 @@ module Spec def matches?(pattern, line, locations) false end + + @@spec_nesting = false + + def self.check_nesting_spec(file, line, &block) + raise NestingSpecError.new("can't nest `it` or `pending`", file, line) if @@spec_nesting + + @@spec_nesting = true + begin + yield + ensure + @@spec_nesting = false + end + end end # :nodoc: diff --git a/src/spec/dsl.cr b/src/spec/dsl.cr index 9db7eb4095e5..f1c468313a36 100644 --- a/src/spec/dsl.cr +++ b/src/spec/dsl.cr @@ -38,7 +38,7 @@ module Spec end # :nodoc: - class AssertionFailed < Exception + class SpecError < Exception getter file : String getter line : Int32 @@ -47,6 +47,14 @@ module Spec end end + # :nodoc: + class AssertionFailed < SpecError + end + + # :nodoc: + class NestingSpecError < SpecError + end + @@aborted = false # :nodoc: diff --git a/src/spec/expectations.cr b/src/spec/expectations.cr index 7bab35e9b4fa..156945ef26d3 100644 --- a/src/spec/expectations.cr +++ b/src/spec/expectations.cr @@ -385,6 +385,11 @@ module Spec raise ex end + # `NestingSpecError` is treated as the same above. + if ex.is_a?(Spec::NestingSpecError) && klass != Spec::NestingSpecError + raise ex + end + ex_to_s = ex.to_s case message when Regex diff --git a/src/spec/methods.cr b/src/spec/methods.cr index c44199fa9b6b..027b25cf25ed 100644 --- a/src/spec/methods.cr +++ b/src/spec/methods.cr @@ -36,23 +36,25 @@ module Spec::Methods # # It is usually used inside a `#describe` or `#context` section. def it(description = "assert", file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block) - return unless Spec.matches?(description, file, line, end_line) + Spec::RootContext.check_nesting_spec(file, line) do + return unless Spec.matches?(description, file, line, end_line) - Spec.formatters.each(&.before_example(description)) + Spec.formatters.each(&.before_example(description)) - start = Time.monotonic - begin - Spec.run_before_each_hooks - block.call - Spec::RootContext.report(:success, description, file, line, Time.monotonic - start) - rescue ex : Spec::AssertionFailed - Spec::RootContext.report(:fail, description, file, line, Time.monotonic - start, ex) - Spec.abort! if Spec.fail_fast? - rescue ex - Spec::RootContext.report(:error, description, file, line, Time.monotonic - start, ex) - Spec.abort! if Spec.fail_fast? - ensure - Spec.run_after_each_hooks + start = Time.monotonic + begin + Spec.run_before_each_hooks + block.call + Spec::RootContext.report(:success, description, file, line, Time.monotonic - start) + rescue ex : Spec::AssertionFailed + Spec::RootContext.report(:fail, description, file, line, Time.monotonic - start, ex) + Spec.abort! if Spec.fail_fast? + rescue ex + Spec::RootContext.report(:error, description, file, line, Time.monotonic - start, ex) + Spec.abort! if Spec.fail_fast? + ensure + Spec.run_after_each_hooks + end end end @@ -68,11 +70,13 @@ module Spec::Methods # # It is usually used inside a `#describe` or `#context` section. def pending(description = "assert", file = __FILE__, line = __LINE__, end_line = __END_LINE__, &block) - return unless Spec.matches?(description, file, line, end_line) + Spec::RootContext.check_nesting_spec(file, line) do + return unless Spec.matches?(description, file, line, end_line) - Spec.formatters.each(&.before_example(description)) + Spec.formatters.each(&.before_example(description)) - Spec::RootContext.report(:pending, description, file, line) + Spec::RootContext.report(:pending, description, file, line) + end end # Defines a yet-to-be-implemented pending test case