domtest

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Sep 9, 2026 License: MIT Imports: 8 Imported by: 0

README

domtest Go Reference

domtest parses the HTML a handler wrote into spec types, so a test can query it with CSS selectors instead of matching substrings. Nothing here needs a browser.

import "github.com/typelate/dom/domtest"

A runnable example lives in ./example, and its README maps each idea below to the test that demonstrates it.

Parsers

Six functions cover three input sources and two parse modes.

*http.Response string io.Reader
Document ParseResponseDocument ParseStringDocument ParseReaderDocument
Fragment ParseResponseDocumentFragment ParseStringDocumentFragment ParseReaderDocumentFragment

Document parsers return spec.Document, fragment parsers return spec.DocumentFragment. Both response parsers close res.Body, on the success path and on failure.

What to assert on

The markup a hypermedia server returns is its interface. Forms, links, and fields carry what a user can do next, so assert on those. A form that posts to a route the server serves, and a field that round-trips its value under the name the handler reads, are claims that survive a rewording of the page.

A strings.Contains check over the response body keeps passing after a field escapes its form or loses its name. The selector form input[name="name"] stops matching the moment either happens. Name the container and the element, then assert on the attribute the next request depends on.

Marker classes work the same way. The example puts default-value or custom-value on the heading depending on which branch produced it, so a test selects the state directly and survives a copy edit.

Attribute selectors accept hyphenated names, so hx-*, fx-*, and data-* are reachable like any other attribute. Passing a control's target attribute back into QuerySelector checks in one line that what it points at exists.

Whole-page golden comparisons fail on every unrelated edit and say nothing about which affordance broke. Keep assertions narrow and let the selector carry the structural claim.

Reusable assertions

A check worth making twice belongs in a helper, and spec already has the interface to write it against. Take a spec.ElementQueries and the same helper runs over a document, a fragment, or a single element.

// assertLinksResolve fails for every anchor whose href is missing, empty, or
// not something net/url will parse.
func assertLinksResolve(t *testing.T, root spec.ElementQueries) {
	t.Helper()
	for a := range root.QuerySelectorSequence("a") {
		href := a.GetAttribute("href")
		if !assert.NotEmptyf(t, href, "anchor %q has no href", a.TextContent()) {
			continue
		}
		_, err := url.Parse(href)
		assert.NoErrorf(t, err, "anchor %q has an unparsable href %q", a.TextContent(), href)
	}
}

Pass a document to sweep a page, a fragment to sweep a partial response, or an element to narrow the sweep further, as in assertLinksResolve(t, doc.QuerySelector("nav")).

url.Parse rejects less than you would hope. It turns down bad escapes like %zz, spaces in a host, and control characters, but accepts the empty string and javascript:alert(1), which is why the emptiness check earns its keep. A helper is where project rules go, such as requiring a leading slash on internal links.

Fragments and the parent element

A handler answering a partial request returns a fragment, and it has to be parsed in the context of the element it will land in. The parent argument names that element and decides the result. HTML parsing rules discard a <tr> outside a table, so the wrong parent produces an empty fragment:

parent result for <tr><td>a</td></tr><tr><td>b</td></tr>
atom.Body 0 elements
atom.Tbody 2 elements

html.ParseFragment runs the same algorithm a browser runs for innerHTML, so a fragment that vanishes here would not have landed in the page either. Pass the element the response gets swapped into and the test checks the swap contract for free.

Use atom.Tbody for table rows, atom.Select for options, and atom.Body or atom.Div for ordinary flow content.

Failure behavior

A helper that cannot read or parse its input reports it with t.Error and returns nil. Error does not stop the test, so the assertions run on with a nil document and the first method call panics. Check the result, or call t.Failed. The nil is a true nil interface value, so comparing against nil is reliable.

Queries behave the same way, and a miss is nil rather than a zero value. Checking for it is the one piece of ceremony this package asks for.

With testify

Nil checks read better as require, which stops the test, and comparisons read better as assert, which lets one run report every mismatch. Anything you then dereference needs require, because a nil Element panics on the next method call. Anything only compared can be assert, so a broken page reports its heading, its class, and its fields together instead of one at a time.

The helpers follow the same convention, reporting with t.Error and returning nil, which pairs with require.NotNil.

Documentation

Overview

Package domtest provides test helpers that parse HTML into spec types.

Every helper reports parse and read failures with TestingT.Error and returns nil. Error does not stop the test, so check the result or call t.Failed before dereferencing it.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ParseReaderDocument

func ParseReaderDocument(t TestingT, r io.Reader) spec.Document

ParseReaderDocument parses a whole HTML document from r. Missing html, head, and body elements are inserted, as by html.Parse.

func ParseReaderDocumentFragment

func ParseReaderDocumentFragment(t TestingT, r io.Reader, parent atom.Atom) spec.DocumentFragment

ParseReaderDocumentFragment parses an HTML fragment from r as if it were inside a parent element, which decides how the markup is interpreted: use atom.Tbody for rows, atom.Select for options, and atom.Body or atom.Div for ordinary flow content.

func ParseResponseDocument

func ParseResponseDocument(t TestingT, res *http.Response) spec.Document

ParseResponseDocument parses a whole HTML document from res and closes its body.

func ParseResponseDocumentFragment

func ParseResponseDocumentFragment(t TestingT, res *http.Response, parent atom.Atom) spec.DocumentFragment

ParseResponseDocumentFragment parses an HTML fragment from res and closes its body. See ParseReaderDocumentFragment for the meaning of parent.

func ParseStringDocument

func ParseStringDocument(t TestingT, s string) spec.Document

ParseStringDocument parses a whole HTML document from s.

func ParseStringDocumentFragment added in v0.7.0

func ParseStringDocumentFragment(t TestingT, in string, parent atom.Atom) spec.DocumentFragment

ParseStringDocumentFragment parses an HTML fragment from in. See ParseReaderDocumentFragment for the meaning of parent.

Types

type TestingT

type TestingT interface {
	Helper()
	Error(...any)
	Log(...any)
	Errorf(format string, args ...any)
	FailNow()
	Failed() bool
	SkipNow()
}

TestingT is the subset of *testing.T the helpers use.

Directories

Path Synopsis
Command example serves a greeting that one form field changes.
Command example serves a greeting that one form field changes.

Jump to

Keyboard shortcuts

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