module Bezel::Testing
Testing an application through its endpoint, in the test's own task, without sockets: requests built in memory go through every plug, the router and the actions, and the responses come back to be checked.
class TPages < Bezel::Controller
controller
def home(conn: Bezel::Conn) -> Bezel::Conn = render(conn, Bezel.html { |h| h.h1("Hello") })
end
class TRouter < Bezel::Router
routes do
get "/", TPages, :home
end
end
class TApp < Bezel::Endpoint
def self.plugs -> Array[Bezel::Plug] = [TRouter.new]
end
res = Bezel::Testing.get(TApp, "/")
Bezel::Testing.html_response(res, 200).has_text?("Hello") # => true
Testing.get and friends make one request each; a Testing::Client
keeps cookies between requests, as a browser does (sessions, the
flash, a login), and follows redirects. The applications they run in
are test ones whose CSRF check is off (App#skip_csrf, a flag the
program sets and prod refuses), so that a test posts without a
token; Testing.app(skip_csrf: false) tests the check itself.
The response helpers check the status and raise Test::AssertionFailed
otherwise, with the body's start, so that a test fails as an
assertion does.
Classes and modules
Bezel::Testing::ClientA browser for tests: requests to one endpoint (the Host www.example.com), keeping the cookies the responses set (a Max-Age of zero or less deletes one; Expires is not read), sending them with the next.Bezel::Testing::DocA parsed HTML page for tests to query (Testing.html_response): its elements as a tree, found by a small selector language:Bezel::Testing::NodeAn element of a Testing::Doc (or its root, without a tag), or a text.
Class methods
def self.app(name: String = …, *, pool: PG::Pool? = …, skip_csrf: Bool = …) -> Bezel::App
A test application named name, without a database (pool to
give one: PG::Testing's), the CSRF check off unless skip_csrf is
false.
def self.get(endpoint: Type[Bezel::Endpoint], path: String, *, params: Hash[String, String | Array[String]] = …, headers: Hash[String, String] = …, app: Bezel::App = …) -> HTTP::Server::Response
The response of endpoint to a GET of path (a query in it, or
params added to it).
def self.post(endpoint: Type[Bezel::Endpoint], path: String, *, params: Hash[String, String | Array[String]] = …, json: JSON::Value? = …, headers: Hash[String, String] = …, app: Bezel::App = …) -> HTTP::Server::Response
The response of endpoint to a POST of params as a form (or json
as a JSON body) to path.
def self.put(endpoint: Type[Bezel::Endpoint], path: String, *, params: Hash[String, String | Array[String]] = …, json: JSON::Value? = …, headers: Hash[String, String] = …, app: Bezel::App = …) -> HTTP::Server::Response
A PUT: post's arguments.
def self.patch(endpoint: Type[Bezel::Endpoint], path: String, *, params: Hash[String, String | Array[String]] = …, json: JSON::Value? = …, headers: Hash[String, String] = …, app: Bezel::App = …) -> HTTP::Server::Response
A PATCH: post's arguments.
def self.delete(endpoint: Type[Bezel::Endpoint], path: String, *, params: Hash[String, String | Array[String]] = …, json: JSON::Value? = …, headers: Hash[String, String] = …, app: Bezel::App = …) -> HTTP::Server::Response
A DELETE: post's arguments.
def self.html_response(res: HTTP::Server::Response, status: Int = …) -> Bezel::Testing::Doc
The page of res, parsed (Testing::Doc), after checking that its
status is status and that it is HTML.
def self.json_response(res: HTTP::Server::Response, status: Int = …) -> JSON::Value
The JSON of res, after checking that its status is status and
that it is JSON.
def self.text_response(res: HTTP::Server::Response, status: Int = …) -> String
The text of res, after checking that its status is status.
def self.redirected_to(res: HTTP::Server::Response, status: Int? = …) -> String
Where res redirects to, after checking that its status is
status (302 by default; any 3xx with status: nil).
def self.body_of(res: HTTP::Server::Response) -> String
The body of res as text (empty for none).