Packages

Wormwood is a tiny library to aid in testing GraphQL queries against an Absinthe schema. It allows you to test your query documents inside ExUnit test modules, and requires no HTTP requests to occur during testing.

Current section

Files

Jump to
wormwood lib gql_case.ex
Raw

lib/gql_case.ex

defmodule Wormwood.GQLCase do
@moduledoc """
This module defines a few helpful macros when testing against an Absinthe GraphQL schema.
It essentially registers an Absinthe schema and a GQL document to the module they're called in.
"""
alias Wormwood.GQLLoader
defmacro __using__(_opts) do
quote do
import Wormwood.GQLCase
end
end
@doc """
Call this macro in the module you wish to load your GQL documents in.
It takes 3 arguments, the first is the query_name that you want to query.
```elixir
defmodule MyCoolApplication.MyModule do
load_gql :my_query, MyCoolApplication.MyAbsintheSchema, "assets/js/queries/MyQuery.gql"
# ...
```
After you define the query name at up code, you can call the query with
```elixir
result = query_gql_by(:my_query, variables: %{}, context: %{})
{:ok, query_data} = result
```
"""
defmacro load_gql(query_name, schema, file_path) when is_atom(query_name) do
quote do
document = GQLLoader.load_file!(unquote(file_path))
Module.put_attribute(unquote(__CALLER__.module), :_wormwood_gql_schema, unquote(schema))
Module.register_attribute(unquote(__CALLER__.module), unquote(query_name), persist: true)
Module.put_attribute(unquote(__CALLER__.module), unquote(query_name), document)
end
end
@doc """
Call this macro in the module you wish to load your GQL document in.
It takes 2 arguments, the first is your Absinthe schema module, the second is a path to a GQL file that contains a GraphQL query or mutation.
For example:
```elixir
defmodule MyCoolApplication.MyModule do
load_gql MyCoolApplication.MyAbsintheSchema, "assets/js/queries/MyQuery.gql"
# ...
```
"""
defmacro load_gql(schema, file_path) do
quote do
if Module.get_attribute(unquote(__CALLER__.module), :_wormwood_gql_query) != nil do
raise WormwoodSetupError, reason: :double_declaration
end
document = GQLLoader.load_file!(unquote(file_path))
Module.put_attribute(unquote(__CALLER__.module), :_wormwood_gql_query, document)
Module.put_attribute(unquote(__CALLER__.module), :_wormwood_gql_schema, unquote(schema))
end
end
@doc """
Call this macro in the module where you want to query using a static string as your query.
It takes 3 arguments, the first is the query_name that you want to query, the second is your Absinthe schema module and
the third is a string of a GQL query or mutation.
```elixir
defmodule MyCoolApplication.MyModule do
set_gql :my_query, MyCoolApplication.MyAbsintheSchema, "query { some { cool { gql { id } }}}"
# ...
```
After you define the query name at up code, you can call the query with
```elixir
result = query_gql_by(:my_query, variables: %{}, context: %{})
{:ok, query_data} = result
```
"""
defmacro set_gql(query_name, schema, query_string) when is_atom(query_name) do
quote do
document = GQLLoader.load_string!(unquote(query_string))
Module.put_attribute(unquote(__CALLER__.module), :_wormwood_gql_schema, unquote(schema))
Module.register_attribute(unquote(__CALLER__.module), unquote(query_name), persist: true)
Module.put_attribute(unquote(__CALLER__.module), unquote(query_name), document)
end
end
@doc """
Call this macro in the module where you want to query using a static string as your query.
It takes 2 arguments, the first is your Absinthe schema module, the second is a string of a GQL query or mutation.
This still supports imports, they will be resolved from the current working directory. (Most likely the top level of your app)
For example:
```elixir
defmodule MyCoolApplication.MyModule do
set_gql MyCoolApplication.MyAbsintheSchema, "query { some { cool { gql { id } }}}"
# ...
```
"""
defmacro set_gql(schema, query_string) do
quote do
if Module.get_attribute(unquote(__CALLER__.module), :_wormwood_gql_query) != nil do
raise WormwoodSetupError, reason: :double_declaration
end
document = GQLLoader.load_string!(unquote(query_string))
Module.put_attribute(unquote(__CALLER__.module), :_wormwood_gql_query, document)
Module.put_attribute(unquote(__CALLER__.module), :_wormwood_gql_schema, unquote(schema))
end
end
@doc """
Call this macro in the module you've loaded a document into using `load_gql/3`.
For example:
```elixir
result = query_gql_by(:my_query, variables: %{}, context: %{})
{:ok, query_data} = result
```
"""
defmacro query_gql_by(query_name, options \\ []) do
quote do
attribute =
unquote(__CALLER__.module).__info__(:attributes)[unquote(query_name)]
|> case do
nil -> nil
list -> List.last(list)
end
if is_nil(attribute) do
raise WormwoodSetupError, reason: :missing_declaration
end
Absinthe.run(
attribute,
@_wormwood_gql_schema,
unquote(options)
)
end
end
@doc """
Call this macro in the module you've loaded a document into using `load_gql` or `set_gql`.
Calling this will execute the document loaded into the module against the schema loaded in the module.
It accepts a keyword list for `options` that are passed into [`Absinthe.run/3`](https://hexdocs.pm/absinthe/Absinthe.html#run/3).
Please see the [Absinthe docs](https://hexdocs.pm/absinthe/Absinthe.html#run/3-options) for more information on the options that can be passed to this macro.
Returns a tuple of the query result from the [`Absinthe.run/3`](https://hexdocs.pm/absinthe/Absinthe.html#run/3) call.
For example:
```elixir
result = query_gql(variables: %{}, context: %{})
{:ok, query_data} = result
```
"""
defmacro query_gql(options \\ []) do
quote do
if is_nil(@_wormwood_gql_query) do
raise WormwoodSetupError, reason: :missing_declaration
end
Absinthe.run(
@_wormwood_gql_query,
@_wormwood_gql_schema,
unquote(options)
)
end
end
@doc """
Call this macro in the module you've loaded a document into using `load_gql` or `set_gql`.
Calling this will execute the document loaded into the module against the schema loaded in the module.
Absinthe will use the phases in the "pipeline_phases" list argument when running.
It also accepts a keyword list for `options` that are passed into [`Absinthe.run/3`](https://hexdocs.pm/absinthe/Absinthe.html#run/3).
Please see the [Absinthe docs](https://hexdocs.pm/absinthe/Absinthe.html#run/3-options) for more information on the options that can be passed to this macro.
Returns a tuple of the query result from the [`Absinthe.Pipeline.run/2`](https://hexdocs.pm/absinthe/Absinthe.Pipeline.html#run/2) call.
For example:
```elixir
pipeline = [Absinthe.Phase.Parse, Absinthe.Phase.Blueprint]
result = query_gql_with_pipeline(pipeline)
assert {:ok, %Absinthe.Blueprint{} = _blueprint, _pipeline} = result
```
"""
defmacro query_gql_with_pipeline(pipeline_phases \\ [], options \\ []) do
quote do
if is_nil(@_wormwood_gql_query) do
raise WormwoodSetupError, reason: :missing_declaration
end
options_list =
unquote(options)
|> Keyword.put(:schema, @_wormwood_gql_schema)
|> Absinthe.Pipeline.options()
pipeline = Enum.map(unquote(pipeline_phases), fn phase -> {phase, options_list} end)
Absinthe.Pipeline.run(@_wormwood_gql_query, pipeline)
end
end
end