You've already forked tribes-plugin-aether
forked from tribes/tribes-plugin-template
skeleton
This commit is contained in:
86
test/contract_test.exs
Normal file
86
test/contract_test.exs
Normal file
@@ -0,0 +1,86 @@
|
||||
defmodule MyPlugin.ContractTest do
|
||||
@moduledoc """
|
||||
Contract compliance tests.
|
||||
|
||||
These verify that the plugin conforms to the Tribes plugin contract.
|
||||
When Tribes.Plugin.ContractTest is available (host dep loaded),
|
||||
replace this file with:
|
||||
|
||||
defmodule MyPlugin.ContractTest do
|
||||
use Tribes.Plugin.ContractTest,
|
||||
plugin: MyPlugin.Plugin,
|
||||
otp_app: :my_plugin
|
||||
end
|
||||
|
||||
Until then, this file provides a standalone equivalent.
|
||||
"""
|
||||
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
@plugin MyPlugin.Plugin
|
||||
@manifest_path Path.join(__DIR__, "../manifest.json") |> Path.expand()
|
||||
|
||||
setup_all do
|
||||
manifest = @manifest_path |> File.read!() |> Jason.decode!()
|
||||
spec = @plugin.register(%{pubsub: nil, repo: nil})
|
||||
%{manifest: manifest, spec: spec}
|
||||
end
|
||||
|
||||
test "runtime provides matches manifest provides", %{manifest: manifest, spec: spec} do
|
||||
normalise = fn cap ->
|
||||
if String.contains?(cap, "@"), do: cap, else: "#{cap}@1"
|
||||
end
|
||||
|
||||
manifest_caps = manifest["provides"] |> Enum.map(normalise) |> Enum.sort()
|
||||
runtime_caps = spec.provides |> Enum.map(normalise) |> Enum.sort()
|
||||
|
||||
assert manifest_caps == runtime_caps,
|
||||
"manifest provides #{inspect(manifest_caps)} != runtime provides #{inspect(runtime_caps)}"
|
||||
end
|
||||
|
||||
test "runtime requires matches manifest requires", %{manifest: manifest, spec: spec} do
|
||||
normalise = fn cap ->
|
||||
if String.contains?(cap, "@"), do: cap, else: "#{cap}@1"
|
||||
end
|
||||
|
||||
manifest_caps = manifest["requires"] |> Enum.map(normalise) |> Enum.sort()
|
||||
runtime_caps = spec.requires |> Enum.map(normalise) |> Enum.sort()
|
||||
|
||||
assert manifest_caps == runtime_caps,
|
||||
"manifest requires #{inspect(manifest_caps)} != runtime requires #{inspect(runtime_caps)}"
|
||||
end
|
||||
|
||||
test "spec has all required keys", %{spec: spec} do
|
||||
required_keys = [
|
||||
:name,
|
||||
:version,
|
||||
:provides,
|
||||
:requires,
|
||||
:nav_items,
|
||||
:pages,
|
||||
:children,
|
||||
:global_js,
|
||||
:global_css,
|
||||
:migrations_path,
|
||||
:hooks
|
||||
]
|
||||
|
||||
for key <- required_keys do
|
||||
assert Map.has_key?(spec, key), "spec must contain #{inspect(key)}"
|
||||
end
|
||||
end
|
||||
|
||||
test "name in spec matches manifest", %{manifest: manifest, spec: spec} do
|
||||
assert spec.name == manifest["name"]
|
||||
end
|
||||
|
||||
test "migrations_path exists if manifest declares migrations", %{manifest: manifest, spec: spec} do
|
||||
if manifest["migrations"] do
|
||||
assert is_binary(spec.migrations_path),
|
||||
"migrations_path must be set when manifest declares migrations: true"
|
||||
|
||||
assert File.dir?(spec.migrations_path),
|
||||
"migrations_path #{inspect(spec.migrations_path)} must be an existing directory"
|
||||
end
|
||||
end
|
||||
end
|
||||
59
test/my_plugin/manifest_test.exs
Normal file
59
test/my_plugin/manifest_test.exs
Normal file
@@ -0,0 +1,59 @@
|
||||
defmodule MyPlugin.ManifestTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
@manifest_path Path.join(__DIR__, "../../manifest.json") |> Path.expand()
|
||||
|
||||
setup_all do
|
||||
content = File.read!(@manifest_path)
|
||||
manifest = Jason.decode!(content)
|
||||
%{manifest: manifest}
|
||||
end
|
||||
|
||||
describe "manifest.json" do
|
||||
test "is valid JSON", %{manifest: manifest} do
|
||||
assert is_map(manifest)
|
||||
end
|
||||
|
||||
test "has required fields", %{manifest: manifest} do
|
||||
required = ["name", "version", "entry_module", "host_api", "provides", "requires"]
|
||||
|
||||
for field <- required do
|
||||
assert Map.has_key?(manifest, field),
|
||||
"manifest.json must contain #{inspect(field)}"
|
||||
end
|
||||
end
|
||||
|
||||
test "name matches OTP app name", %{manifest: manifest} do
|
||||
assert manifest["name"] == "my_plugin"
|
||||
end
|
||||
|
||||
test "entry_module is a valid Elixir module name", %{manifest: manifest} do
|
||||
module_name = manifest["entry_module"]
|
||||
assert is_binary(module_name)
|
||||
assert String.starts_with?(module_name, "Elixir.") or not String.contains?(module_name, " ")
|
||||
end
|
||||
|
||||
test "provides contains valid capability identifiers", %{manifest: manifest} do
|
||||
for cap <- manifest["provides"] do
|
||||
assert Regex.match?(~r/^[a-z][a-z0-9_]*(@\d+)?$/, cap),
|
||||
"invalid capability identifier: #{inspect(cap)}"
|
||||
end
|
||||
end
|
||||
|
||||
test "requires contains valid capability identifiers", %{manifest: manifest} do
|
||||
for cap <- manifest["requires"] do
|
||||
assert Regex.match?(~r/^[a-z][a-z0-9_]*(@\d+)?$/, cap),
|
||||
"invalid capability identifier: #{inspect(cap)}"
|
||||
end
|
||||
end
|
||||
|
||||
test "host_api is a string version", %{manifest: manifest} do
|
||||
assert is_binary(manifest["host_api"])
|
||||
end
|
||||
|
||||
test "entry_module matches actual plugin module", %{manifest: manifest} do
|
||||
module = String.to_atom("Elixir.#{manifest["entry_module"]}")
|
||||
assert Code.ensure_loaded?(module), "entry_module #{manifest["entry_module"]} must be loadable"
|
||||
end
|
||||
end
|
||||
end
|
||||
63
test/my_plugin/plugin_test.exs
Normal file
63
test/my_plugin/plugin_test.exs
Normal file
@@ -0,0 +1,63 @@
|
||||
defmodule MyPlugin.PluginTest do
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
describe "register/1" do
|
||||
setup do
|
||||
context = %{pubsub: nil, repo: nil}
|
||||
%{spec: MyPlugin.Plugin.register(context)}
|
||||
end
|
||||
|
||||
test "returns plugin name and version", %{spec: spec} do
|
||||
assert spec.name == "my_plugin"
|
||||
assert is_binary(spec.version)
|
||||
end
|
||||
|
||||
test "provides is a list of capability strings", %{spec: spec} do
|
||||
assert is_list(spec.provides)
|
||||
|
||||
for cap <- spec.provides do
|
||||
assert is_binary(cap), "capability must be a string, got: #{inspect(cap)}"
|
||||
end
|
||||
end
|
||||
|
||||
test "requires is a list of capability strings", %{spec: spec} do
|
||||
assert is_list(spec.requires)
|
||||
|
||||
for cap <- spec.requires do
|
||||
assert is_binary(cap), "capability must be a string, got: #{inspect(cap)}"
|
||||
end
|
||||
end
|
||||
|
||||
test "nav items have required fields", %{spec: spec} do
|
||||
assert is_list(spec.nav_items)
|
||||
|
||||
for item <- spec.nav_items do
|
||||
assert is_binary(item.label), "nav item must have a label"
|
||||
assert is_binary(item.path), "nav item must have a path"
|
||||
assert is_integer(item.order), "nav item must have an integer order"
|
||||
end
|
||||
end
|
||||
|
||||
test "pages reference existing modules", %{spec: spec} do
|
||||
assert is_list(spec.pages)
|
||||
|
||||
for page <- spec.pages do
|
||||
assert is_binary(page.path), "page must have a path"
|
||||
assert is_atom(page.live_view), "page must have a live_view module"
|
||||
assert Code.ensure_loaded?(page.live_view), "module #{page.live_view} must be loadable"
|
||||
end
|
||||
end
|
||||
|
||||
test "children are valid child specs", %{spec: spec} do
|
||||
assert is_list(spec.children)
|
||||
end
|
||||
|
||||
test "asset lists are string lists", %{spec: spec} do
|
||||
assert is_list(spec.global_js)
|
||||
assert is_list(spec.global_css)
|
||||
|
||||
for js <- spec.global_js, do: assert(is_binary(js))
|
||||
for css <- spec.global_css, do: assert(is_binary(css))
|
||||
end
|
||||
end
|
||||
end
|
||||
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
@@ -0,0 +1 @@
|
||||
ExUnit.start()
|
||||
Reference in New Issue
Block a user