mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Cleanup new DSL macros
This commit is contained in:
parent
fbd9713d52
commit
009ca4776a
4 changed files with 64 additions and 212 deletions
|
@ -1,3 +1,4 @@
|
|||
require "../context"
|
||||
require "../source"
|
||||
require "./builder"
|
||||
|
||||
|
@ -8,11 +9,23 @@ module Spectator::DSL
|
|||
# The *name* is the name given to the macro.
|
||||
# TODO: Mark example as pending if block is omitted.
|
||||
macro define_example(name)
|
||||
# Defines an example.
|
||||
#
|
||||
# If a block is given, it is treated as the code to test.
|
||||
# The block is provided the current example instance as an argument.
|
||||
#
|
||||
# The first argument names the example (test).
|
||||
# Typically, this specifies what is being tested.
|
||||
# It has no effect on the test and is purely used for output.
|
||||
# If omitted, a name is generated from the first assertion in the test.
|
||||
#
|
||||
# The example will be marked as pending if the block is omitted.
|
||||
# A block or name must be provided.
|
||||
macro {{name.id}}(what = nil, &block)
|
||||
\{% raise "Cannot use '{{name.id}}' inside of a test block" if @def %}
|
||||
\{% raise "A description or block must be provided. Cannot use '{{name.id}}' alone." unless what || block %}
|
||||
|
||||
def \%test # TODO: Pass example instance.
|
||||
def \%test : Nil # TODO: Pass example instance.
|
||||
\{{block.body}}
|
||||
end
|
||||
|
||||
|
@ -27,7 +40,7 @@ module Spectator::DSL
|
|||
# Inserts the correct representation of a example's name.
|
||||
# If *what* is a string, then it is dropped in as-is.
|
||||
# For anything else, it is stringified.
|
||||
# This is intended to be used to convert a description from the spec DSL to `ExampleNode#name`.
|
||||
# This is intended to be used to convert a description from the spec DSL to `SpecNode#name`.
|
||||
private macro _spectator_example_name(what)
|
||||
{% if what.is_a?(StringLiteral) ||
|
||||
what.is_a?(StringInterpolation) ||
|
||||
|
@ -38,77 +51,12 @@ module Spectator::DSL
|
|||
{% end %}
|
||||
end
|
||||
|
||||
# Defines an example.
|
||||
#
|
||||
# If a block is given, it is treated as the code to test.
|
||||
# The block is provided the current example instance as an argument.
|
||||
#
|
||||
# The first argument names the example (test).
|
||||
# Typically, this specifies what is being tested.
|
||||
# It has no effect on the test and is purely used for output.
|
||||
# If omitted, a name is generated from the first assertion in the test.
|
||||
#
|
||||
# The example will be marked as pending if the block is omitted.
|
||||
# A block or name must be provided.
|
||||
define_example :example
|
||||
|
||||
# Defines an example.
|
||||
#
|
||||
# If a block is given, it is treated as the code to test.
|
||||
# The block is provided the current example instance as an argument.
|
||||
#
|
||||
# The first argument names the example (test).
|
||||
# Typically, this specifies what is being tested.
|
||||
# It has no effect on the test and is purely used for output.
|
||||
# If omitted, a name is generated from the first assertion in the test.
|
||||
#
|
||||
# The example will be marked as pending if the block is omitted.
|
||||
# A block or name must be provided.
|
||||
define_example :it
|
||||
|
||||
# Defines an example.
|
||||
#
|
||||
# If a block is given, it is treated as the code to test.
|
||||
# The block is provided the current example instance as an argument.
|
||||
#
|
||||
# The first argument names the example (test).
|
||||
# Typically, this specifies what is being tested.
|
||||
# It has no effect on the test and is purely used for output.
|
||||
# If omitted, a name is generated from the first assertion in the test.
|
||||
#
|
||||
# The example will be marked as pending if the block is omitted.
|
||||
# A block or name must be provided.
|
||||
define_example :specify
|
||||
end
|
||||
|
||||
macro pending(description = nil, _source_file = __FILE__, _source_line = __LINE__, &block)
|
||||
{% if block.is_a?(Nop) %}
|
||||
{% if description.is_a?(Call) %}
|
||||
def %run
|
||||
{{description}}
|
||||
end
|
||||
{% else %}
|
||||
{% raise "Unrecognized syntax: `pending #{description}` at #{_source_file}:#{_source_line}" %}
|
||||
{% end %}
|
||||
{% else %}
|
||||
def %run
|
||||
{{block.body}}
|
||||
end
|
||||
{% end %}
|
||||
|
||||
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
|
||||
::Spectator::SpecBuilder.add_pending_example(
|
||||
{{description.is_a?(StringLiteral) || description.is_a?(StringInterpolation) || description.is_a?(NilLiteral) ? description : description.stringify}},
|
||||
%source,
|
||||
{{@type.name}}
|
||||
) { |test| test.as({{@type.name}}).%run }
|
||||
end
|
||||
|
||||
macro skip(description = nil, &block)
|
||||
pending({{description}}) {{block}}
|
||||
end
|
||||
|
||||
macro xit(description = nil, &block)
|
||||
pending({{description}}) {{block}}
|
||||
# TODO: pending, skip, and xit
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,7 @@ module Spectator::DSL
|
|||
# If *what* appears to be a type name, it will be symbolized.
|
||||
# If it's a string, then it is dropped in as-is.
|
||||
# For anything else, it is stringified.
|
||||
# This is intended to be used to convert a description from the spec DSL to `ExampleNode#name`.
|
||||
# This is intended to be used to convert a description from the spec DSL to `SpecNode#name`.
|
||||
private macro _spectator_group_name(what)
|
||||
{% if (what.is_a?(Generic) ||
|
||||
what.is_a?(Path) ||
|
||||
|
@ -85,107 +85,7 @@ module Spectator::DSL
|
|||
define_example_group :describe
|
||||
|
||||
define_example_group :context
|
||||
end
|
||||
|
||||
macro sample(collection, count = nil, _source_file = __FILE__, _source_line = __LINE__, &block)
|
||||
{% name = block.args.empty? ? :value.id : block.args.first.id %}
|
||||
|
||||
def %collection
|
||||
{{collection}}
|
||||
end
|
||||
|
||||
def %to_a
|
||||
{% if count %}
|
||||
%collection.first({{count}})
|
||||
{% else %}
|
||||
%collection.to_a
|
||||
{% end %}
|
||||
end
|
||||
|
||||
class Context%sample < {{@type.id}}
|
||||
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
|
||||
::Spectator::SpecBuilder.start_sample_group({{collection.stringify}}, %source, :%sample, {{name.stringify}}) do |values|
|
||||
sample = {{@type.id}}.new(values)
|
||||
sample.%to_a
|
||||
end
|
||||
|
||||
def {{name}}
|
||||
@spectator_test_values.get_value(:%sample, typeof(%to_a.first))
|
||||
end
|
||||
|
||||
{{block.body}}
|
||||
|
||||
::Spectator::SpecBuilder.end_group
|
||||
end
|
||||
end
|
||||
|
||||
macro random_sample(collection, count = nil, _source_file = __FILE__, _source_line = __LINE__, &block)
|
||||
{% name = block.args.empty? ? :value.id : block.args.first.id %}
|
||||
|
||||
def %collection
|
||||
{{collection}}
|
||||
end
|
||||
|
||||
def %to_a
|
||||
{% if count %}
|
||||
%collection.first({{count}})
|
||||
{% else %}
|
||||
%collection.to_a
|
||||
{% end %}
|
||||
end
|
||||
|
||||
class Context%sample < {{@type.id}}
|
||||
%source = ::Spectator::Source.new({{_source_file}}, {{_source_line}})
|
||||
::Spectator::SpecBuilder.start_sample_group({{collection.stringify}}, %source, :%sample, {{name.stringify}}) do |values|
|
||||
sample = {{@type.id}}.new(values)
|
||||
collection = sample.%to_a
|
||||
{% if count %}
|
||||
collection.sample({{count}}, ::Spectator.random)
|
||||
{% else %}
|
||||
collection.shuffle(::Spectator.random)
|
||||
{% end %}
|
||||
end
|
||||
|
||||
def {{name}}
|
||||
@spectator_test_values.get_value(:%sample, typeof(%to_a.first))
|
||||
end
|
||||
|
||||
{{block.body}}
|
||||
|
||||
::Spectator::SpecBuilder.end_group
|
||||
end
|
||||
end
|
||||
|
||||
macro given(*assignments, &block)
|
||||
context({{assignments.splat.stringify}}) do
|
||||
{% for assignment in assignments %}
|
||||
let({{assignment.target}}) { {{assignment.value}} }
|
||||
{% end %}
|
||||
|
||||
{% # Trick to get the contents of the block as an array of nodes.
|
||||
# If there are multiple expressions/statements in the block,
|
||||
# then the body will be a `Expressions` type.
|
||||
# If there's only one expression, then the body is just that.
|
||||
body = if block.is_a?(Nop)
|
||||
raise "Missing block for 'given'"
|
||||
elsif block.body.is_a?(Expressions)
|
||||
# Get the expressions, which is already an array.
|
||||
block.body.expressions
|
||||
else
|
||||
# Wrap the expression in an array.
|
||||
[block.body]
|
||||
end %}
|
||||
|
||||
{% for item in body %}
|
||||
# If the item starts with "it", then leave it as-is.
|
||||
# Otherwise, prefix it with "it"
|
||||
# and treat it as the one-liner "it" syntax.
|
||||
{% if item.is_a?(Call) && item.name == :it.id %}
|
||||
{{item}}
|
||||
{% else %}
|
||||
it {{item}}
|
||||
{% end %}
|
||||
{% end %}
|
||||
end
|
||||
# TODO: sample, random_sample, and given
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,55 +1,61 @@
|
|||
require "../source"
|
||||
require "./builder"
|
||||
|
||||
module Spectator::DSL
|
||||
# DSL methods for adding custom logic to key times of the spec execution.
|
||||
module Hooks
|
||||
# Defines code to run before any and all examples in an example group.
|
||||
macro before_all(&block)
|
||||
{% raise "Cannot use 'before_all' inside of a test block" if @def %}
|
||||
# Defines a macro to create an example group hook.
|
||||
# The *type* indicates when the hook runs and must be a method on `Spectator::DSL::Builder`.
|
||||
macro define_example_group_hook(type)
|
||||
macro {{type.id}}(&block)
|
||||
\{% raise "Missing block for '{{type.id}}' hook" unless block %}
|
||||
\{% raise "Cannot use '{{type.id}}' inside of a test block" if @def %}
|
||||
|
||||
def self.%hook : Nil
|
||||
{{block.body}}
|
||||
def self.\%hook : Nil
|
||||
\{{block.body}}
|
||||
end
|
||||
|
||||
::Spectator::DSL::Builder.before_all(
|
||||
::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
||||
) { {{@type.name}}.%hook }
|
||||
end
|
||||
|
||||
macro before_each(&block)
|
||||
{% raise "Cannot use 'before_each' inside of a test block" if @def %}
|
||||
|
||||
def %hook : Nil
|
||||
{{block.body}}
|
||||
end
|
||||
|
||||
::Spectator::DSL::Builder.before_each(
|
||||
::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
||||
) { |example| example.with_context({{@type.name}}) { %hook } }
|
||||
end
|
||||
|
||||
macro after_all(&block)
|
||||
{% raise "Cannot use 'after_all' inside of a test block" if @def %}
|
||||
|
||||
def self.%hook : Nil
|
||||
{{block.body}}
|
||||
end
|
||||
|
||||
::Spectator::DSL::Builder.after_all(
|
||||
::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
||||
) { {{@type.name}}.%hook }
|
||||
end
|
||||
|
||||
macro after_each(&block)
|
||||
{% raise "Cannot use 'after_each' inside of a test block" if @def %}
|
||||
|
||||
def %hook : Nil
|
||||
{{block.body}}
|
||||
end
|
||||
|
||||
::Spectator::DSL::Builder.after_each(
|
||||
::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
||||
) { |example| example.with_context({{@type.name}}) { %hook } }
|
||||
::Spectator::DSL::Builder.{{type.id}}(
|
||||
::Spectator::Source.new(\{{block.filename}}, \{{block.line_number}})
|
||||
) { \{{@type.name}}.\%hook }
|
||||
end
|
||||
end
|
||||
|
||||
# Defines a macro to create an example hook.
|
||||
# The *type* indicates when the hook runs and must be a method on `Spectator::DSL::Builder`.
|
||||
macro define_example_hook(type)
|
||||
macro {{type.id}}(&block)
|
||||
\{% raise "Missing block for '{{type.id}}' hook" unless block %}
|
||||
\{% raise "Cannot use '{{type.id}}' inside of a test block" if @def %}
|
||||
|
||||
def \%hook : Nil # TODO: Pass example instance.
|
||||
\{{block.body}}
|
||||
end
|
||||
|
||||
::Spectator::DSL::Builder.{{type.id}}(
|
||||
::Spectator::Source.new(\{{block.filename}}, \{{block.line_number}})
|
||||
) { |example| example.with_context(\{{@type.name}}) { \%hook } }
|
||||
end
|
||||
end
|
||||
|
||||
# Defines a block of code that will be invoked once before any examples in the group.
|
||||
# The block will not run in the context of the current running example.
|
||||
# This means that values defined by `let` and `subject` are not available.
|
||||
define_example_group_hook :before_all
|
||||
|
||||
# Defines a block of code that will be invoked once after all examples in the group.
|
||||
# The block will not run in the context of the current running example.
|
||||
# This means that values defined by `let` and `subject` are not available.
|
||||
define_example_group_hook :after_all
|
||||
|
||||
# Defines a block of code that will be invoked before every example in the group.
|
||||
# The block will be run in the context of the current running example.
|
||||
# This means that values defined by `let` and `subject` are available.
|
||||
define_example_hook :before_each
|
||||
|
||||
# Defines a block of code that will be invoked after every example in the group.
|
||||
# The block will be run in the context of the current running example.
|
||||
# This means that values defined by `let` and `subject` are available.
|
||||
define_example_hook :after_each
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,9 +16,7 @@ module Spectator::DSL
|
|||
# # Your examples for `Foo` go here.
|
||||
# end
|
||||
# ```
|
||||
# NOTE: Inside the block, the `Spectator` prefix is no longer needed.
|
||||
# Actually, prefixing methods and macros with `Spectator`
|
||||
# most likely won't work and can cause compiler errors.
|
||||
# NOTE: Inside the block, the `Spectator` prefix _should not_ be used.
|
||||
macro {{method.id}}(description, &block)
|
||||
class ::SpectatorTestContext
|
||||
{{method.id}}(\{{description}}) \{{block}}
|
||||
|
|
Loading…
Reference in a new issue