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 "../source"
|
||||||
require "./builder"
|
require "./builder"
|
||||||
|
|
||||||
|
@ -8,11 +9,23 @@ module Spectator::DSL
|
||||||
# The *name* is the name given to the macro.
|
# The *name* is the name given to the macro.
|
||||||
# TODO: Mark example as pending if block is omitted.
|
# TODO: Mark example as pending if block is omitted.
|
||||||
macro define_example(name)
|
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)
|
macro {{name.id}}(what = nil, &block)
|
||||||
\{% raise "Cannot use '{{name.id}}' inside of a test block" if @def %}
|
\{% 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 %}
|
\{% 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}}
|
\{{block.body}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -27,7 +40,7 @@ module Spectator::DSL
|
||||||
# Inserts the correct representation of a example's name.
|
# Inserts the correct representation of a example's name.
|
||||||
# If *what* is a string, then it is dropped in as-is.
|
# If *what* is a string, then it is dropped in as-is.
|
||||||
# For anything else, it is stringified.
|
# 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)
|
private macro _spectator_example_name(what)
|
||||||
{% if what.is_a?(StringLiteral) ||
|
{% if what.is_a?(StringLiteral) ||
|
||||||
what.is_a?(StringInterpolation) ||
|
what.is_a?(StringInterpolation) ||
|
||||||
|
@ -38,77 +51,12 @@ module Spectator::DSL
|
||||||
{% end %}
|
{% end %}
|
||||||
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
|
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
|
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
|
define_example :specify
|
||||||
|
|
||||||
|
# TODO: pending, skip, and xit
|
||||||
end
|
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}}
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -32,7 +32,7 @@ module Spectator::DSL
|
||||||
# If *what* appears to be a type name, it will be symbolized.
|
# If *what* appears to be a type name, it will be symbolized.
|
||||||
# If it's a string, then it is dropped in as-is.
|
# If it's a string, then it is dropped in as-is.
|
||||||
# For anything else, it is stringified.
|
# 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)
|
private macro _spectator_group_name(what)
|
||||||
{% if (what.is_a?(Generic) ||
|
{% if (what.is_a?(Generic) ||
|
||||||
what.is_a?(Path) ||
|
what.is_a?(Path) ||
|
||||||
|
@ -85,107 +85,7 @@ module Spectator::DSL
|
||||||
define_example_group :describe
|
define_example_group :describe
|
||||||
|
|
||||||
define_example_group :context
|
define_example_group :context
|
||||||
|
|
||||||
|
# TODO: sample, random_sample, and given
|
||||||
end
|
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
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,55 +1,61 @@
|
||||||
|
require "../source"
|
||||||
require "./builder"
|
require "./builder"
|
||||||
|
|
||||||
module Spectator::DSL
|
module Spectator::DSL
|
||||||
# DSL methods for adding custom logic to key times of the spec execution.
|
# DSL methods for adding custom logic to key times of the spec execution.
|
||||||
module Hooks
|
module Hooks
|
||||||
# Defines code to run before any and all examples in an example group.
|
# Defines a macro to create an example group hook.
|
||||||
macro before_all(&block)
|
# The *type* indicates when the hook runs and must be a method on `Spectator::DSL::Builder`.
|
||||||
{% raise "Cannot use 'before_all' inside of a test block" if @def %}
|
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
|
def self.\%hook : Nil
|
||||||
{{block.body}}
|
\{{block.body}}
|
||||||
|
end
|
||||||
|
|
||||||
|
::Spectator::DSL::Builder.{{type.id}}(
|
||||||
|
::Spectator::Source.new(\{{block.filename}}, \{{block.line_number}})
|
||||||
|
) { \{{@type.name}}.\%hook }
|
||||||
end
|
end
|
||||||
|
|
||||||
::Spectator::DSL::Builder.before_all(
|
|
||||||
::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
|
||||||
) { {{@type.name}}.%hook }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
macro before_each(&block)
|
# Defines a macro to create an example hook.
|
||||||
{% raise "Cannot use 'before_each' inside of a test block" if @def %}
|
# 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
|
def \%hook : Nil # TODO: Pass example instance.
|
||||||
{{block.body}}
|
\{{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
|
||||||
|
|
||||||
::Spectator::DSL::Builder.before_each(
|
|
||||||
::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
|
||||||
) { |example| example.with_context({{@type.name}}) { %hook } }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
macro after_all(&block)
|
# Defines a block of code that will be invoked once before any examples in the group.
|
||||||
{% raise "Cannot use 'after_all' inside of a test block" if @def %}
|
# 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
|
||||||
|
|
||||||
def self.%hook : Nil
|
# Defines a block of code that will be invoked once after all examples in the group.
|
||||||
{{block.body}}
|
# The block will not run in the context of the current running example.
|
||||||
end
|
# This means that values defined by `let` and `subject` are not available.
|
||||||
|
define_example_group_hook :after_all
|
||||||
|
|
||||||
::Spectator::DSL::Builder.after_all(
|
# Defines a block of code that will be invoked before every example in the group.
|
||||||
::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
# The block will be run in the context of the current running example.
|
||||||
) { {{@type.name}}.%hook }
|
# This means that values defined by `let` and `subject` are available.
|
||||||
end
|
define_example_hook :before_each
|
||||||
|
|
||||||
macro after_each(&block)
|
# Defines a block of code that will be invoked after every example in the group.
|
||||||
{% raise "Cannot use 'after_each' inside of a test block" if @def %}
|
# The block will be run in the context of the current running example.
|
||||||
|
# This means that values defined by `let` and `subject` are available.
|
||||||
def %hook : Nil
|
define_example_hook :after_each
|
||||||
{{block.body}}
|
|
||||||
end
|
|
||||||
|
|
||||||
::Spectator::DSL::Builder.after_each(
|
|
||||||
::Spectator::Source.new({{block.filename}}, {{block.line_number}})
|
|
||||||
) { |example| example.with_context({{@type.name}}) { %hook } }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,9 +16,7 @@ module Spectator::DSL
|
||||||
# # Your examples for `Foo` go here.
|
# # Your examples for `Foo` go here.
|
||||||
# end
|
# end
|
||||||
# ```
|
# ```
|
||||||
# NOTE: Inside the block, the `Spectator` prefix is no longer needed.
|
# NOTE: Inside the block, the `Spectator` prefix _should not_ be used.
|
||||||
# Actually, prefixing methods and macros with `Spectator`
|
|
||||||
# most likely won't work and can cause compiler errors.
|
|
||||||
macro {{method.id}}(description, &block)
|
macro {{method.id}}(description, &block)
|
||||||
class ::SpectatorTestContext
|
class ::SpectatorTestContext
|
||||||
{{method.id}}(\{{description}}) \{{block}}
|
{{method.id}}(\{{description}}) \{{block}}
|
||||||
|
|
Loading…
Reference in a new issue