webtest

package
v0.0.3-alpha Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 12, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package webtest is a supertest-flavoured HTTP client + a react-testing-library-flavoured DOM query surface for testing hex web apps end-to-end.

Two use modes:

  1. Direct Go tests — build a Client, chain request/assert calls:

    client := webtest.New(t, app) client.Get("/dashboard"). StatusIs(200). See("Welcome, Alice"). Find(".user-card").HasClass("active"). Find("button").Count(3)

  2. BDD/Gherkin — wire the standard step vocabulary via hex/webtest/bdd.Register, then write scenarios in .feature files.

The client boots the app once (via httptest.NewServer wrapping the Echo instance the web provider registers under "http"), then runs real requests through the real network stack. Fast enough for a tight feedback loop; realistic enough that middleware, routing, cookies, and redirects all behave the same as production.

Server-side rendering (Go templates, Jade, Markdown) is fully covered. JavaScript behaviour (Alpine, HTMX transitions on the browser side) is NOT executed — for that, a follow-up will add chromedp/rod bindings. HTMX-triggered server routes DO get exercised, since HTMX is just a normal HTTP request with special headers; use Client.Header("HX-Request", "true") to simulate the header the client would send.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a stateful HTTP client bound to an in-process hex app. After every request the response body is parsed as HTML (via goquery) so assertions like Find(".selector") work without a second network round-trip.

Client is NOT goroutine-safe — one Client per test scenario.

func New

func New(t testing.TB, app *hex.App) *Client

New starts a test HTTP server wrapping the app's Echo instance (resolved from the container under "http") and returns a Client bound to it. The server is torn down via t.Cleanup.

func (*Client) Body

func (c *Client) Body() string

Body returns the last response body as a string.

func (*Client) BodyContains

func (c *Client) BodyContains(substring string) *Client

BodyContains asserts the response body contains substring.

func (*Client) Delete

func (c *Client) Delete(path string) *Client

Delete performs a DELETE.

func (*Client) Doc

func (c *Client) Doc() *goquery.Document

Doc lazily parses the last body as HTML and returns the *goquery document. Escape hatch for callers who want the full goquery API.

func (*Client) Find

func (c *Client) Find(sel string) *Selection

Find selects elements matching sel. See Selection for chainable assertions.

func (*Client) Get

func (c *Client) Get(path string) *Client

Get performs a GET at path (relative to the test server root). Returns c for chaining.

func (*Client) Header

func (c *Client) Header(name, value string) *Client

Header sets a default header applied to every subsequent request. Multiple calls with the same name append values.

func (*Client) HeaderIs

func (c *Client) HeaderIs(name, value string) *Client

HeaderIs asserts the response has the given header name/value.

func (*Client) LocationIs

func (c *Client) LocationIs(path string) *Client

LocationIs asserts the Location header equals path (for redirects).

func (*Client) Post

func (c *Client) Post(path string, body io.Reader, contentType string) *Client

Post performs a POST with a raw body. contentType is stored in the Content-Type header; pass an empty string to skip.

func (*Client) PostForm

func (c *Client) PostForm(path string, form url.Values) *Client

PostForm posts a url-encoded form.

func (*Client) PostJSON

func (c *Client) PostJSON(path string, body any) *Client

PostJSON posts a JSON-encoded body.

func (*Client) Put

func (c *Client) Put(path string, body io.Reader, contentType string) *Client

Put performs a PUT with the given body. Empty contentType is treated as text/plain.

func (*Client) See

func (c *Client) See(text string) *Client

See asserts text appears anywhere in the response body.

func (*Client) Status

func (c *Client) Status() int

Status returns the last response's HTTP status code. Fails the test if no request has been sent yet.

func (*Client) StatusIs

func (c *Client) StatusIs(expected int) *Client

StatusIs asserts the last response status equals expected.

func (*Client) URL

func (c *Client) URL() string

URL returns the base URL of the test server. Handy for building absolute URLs (redirect targets, etc.).

type Selection

type Selection struct {
	// contains filtered or unexported fields
}

Selection is a chainable assertion wrapper around a goquery selection. Modeled after react-testing-library's queries and Laravel's TestResponse assertions.

Every assertion method returns the same Selection so calls chain:

client.Find(".user-card").
    HasClass("active").
    HasText("Alice").
    Count(3)

func (*Selection) Attr

func (s *Selection) Attr(name string) string

Attr returns the value of attribute name on the first matched element. If missing, returns "" and reports the miss (does not fail).

func (*Selection) Count

func (s *Selection) Count(n int) *Selection

Count asserts the selection contains exactly n elements. Zero asserts absence; use Exists / DoesNotExist for readability.

func (*Selection) DoesNotExist

func (s *Selection) DoesNotExist() *Selection

DoesNotExist asserts no elements matched the selector.

func (*Selection) Exists

func (s *Selection) Exists() *Selection

Exists asserts at least one element matched the selector.

func (*Selection) Find

func (s *Selection) Find(sel string) *Selection

Find scopes a further selection inside the currently selected elements — same semantics as CSS descendant combinator.

func (*Selection) First

func (s *Selection) First() *Selection

First returns a Selection for the first matched element.

func (*Selection) HasAttribute

func (s *Selection) HasAttribute(name string) *Selection

HasAttribute asserts the selected element has attribute name (any value).

func (*Selection) HasAttributeValue

func (s *Selection) HasAttributeValue(name, want string) *Selection

HasAttributeValue asserts the attribute name equals want.

func (*Selection) HasClass

func (s *Selection) HasClass(class string) *Selection

HasClass asserts the selection has the given CSS class.

func (*Selection) HasExactText

func (s *Selection) HasExactText(want string) *Selection

HasExactText asserts the selected element's text (trimmed) is exactly equal to want.

func (*Selection) HasText

func (s *Selection) HasText(substring string) *Selection

HasText asserts the selected elements' combined text content contains substring. Whitespace is not normalised — pass exactly what you expect (with mindful spacing).

func (*Selection) Nth

func (s *Selection) Nth(n int) *Selection

Nth returns a Selection for the nth (0-indexed) matched element.

func (*Selection) Text

func (s *Selection) Text() string

Text returns the combined text content of all selected elements.

func (*Selection) Underlying

func (s *Selection) Underlying() *goquery.Selection

Underlying returns the wrapped *goquery.Selection. Escape hatch when callers need the full goquery API.

Directories

Path Synopsis
Package bdd wires the hex/webtest client into a hex/bdd suite as a set of standard Gherkin step definitions, giving apps a react-testing-library-flavoured browser-test vocabulary in .feature files:
Package bdd wires the hex/webtest client into a hex/bdd suite as a set of standard Gherkin step definitions, giving apps a react-testing-library-flavoured browser-test vocabulary in .feature files:

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL