Let’s have a look at how one can do it in phases: We begin with the next take a look at that tries to compile the template. In Go we use the usual html/template
bundle.
Go
func Test_wellFormedHtml(t *testing.T) { templ := template.Should(template.ParseFiles("index.tmpl")) _ = templ }
In Java we use moustache
as a result of it is rather straightforward to make use of; free marker both
Pace are different frequent choices.
Java
@Check void indexIsSoundHtml() { var template = Mustache.compiler().compile( new InputStreamReader( getClass().getResourceAsStream("/index.tmpl"))); }
If we run this take a look at, it can fail, as a result of the index.tmpl
the file doesn’t exist. So we create it, with the damaged HTML above. Now the take a look at ought to cross.
Then we create a mannequin for the template to make use of. The app manages a to-do checklist and we will create a minimal mannequin for demonstration functions.
Go
func Test_wellFormedHtml(t *testing.T) {
templ := template.Should(template.ParseFiles("index.tmpl"))
mannequin := todo.NewList()
_ = templ
_ = mannequin
}
Java
@Check
void indexIsSoundHtml() {
var template = Mustache.compiler().compile(
new InputStreamReader(
getClass().getResourceAsStream("/index.tmpl")));
var mannequin = new TodoList();
}
Now we render the template, saving the ends in a byte buffer (Go) or as a String
(Java).
Go
func Test_wellFormedHtml(t *testing.T) {
templ := template.Should(template.ParseFiles("index.tmpl"))
mannequin := todo.NewList()
var buf bytes.Buffer
err := templ.Execute(&buf, mannequin)
if err != nil {
panic(err)
}
}
Java
@Check
void indexIsSoundHtml() {
var template = Mustache.compiler().compile(
new InputStreamReader(
getClass().getResourceAsStream("/index.tmpl")));
var mannequin = new TodoList();
var html = template.execute(mannequin);
}
At this level we wish parse the HTML and we anticipate to see an error, as a result of in our damaged HTML there’s a div
component that’s closed by a p
component. There’s an HTML parser within the Go customary library, but it surely’s too forgiving: if we run it on our damaged HTML, we do not get an error. Fortuitously, the Go customary library additionally has an XML parser that may be configured to parse HTML (because of this stack overflow reply)
Go
func Test_wellFormedHtml(t *testing.T) {
templ := template.Should(template.ParseFiles("index.tmpl"))
mannequin := todo.NewList()
// render the template right into a buffer
var buf bytes.Buffer
err := templ.Execute(&buf, mannequin)
if err != nil {
panic(err)
}
// verify that the template will be parsed as (lenient) XML
decoder := xml.NewDecoder(bytes.NewReader(buf.Bytes()))
decoder.Strict = false
decoder.AutoClose = xml.HTMLAutoClose
decoder.Entity = xml.HTMLEntity
for {
_, err := decoder.Token()
swap err {
case io.EOF:
return // We're accomplished, it is legitimate!
case nil:
// do nothing
default:
t.Fatalf("Error parsing html: %s", err)
}
}
}
This code configures the HTML parser to have the right degree of leniency for HTML after which parses the HTML token by token. In reality, we see the error message we needed:
--- FAIL: Test_wellFormedHtml (0.00s) index_template_test.go:61: Error parsing html: XML syntax error on line 4: surprising finish component
In Java, a flexible library to make use of is jsopa:
Java
@Check
void indexIsSoundHtml() {
var template = Mustache.compiler().compile(
new InputStreamReader(
getClass().getResourceAsStream("/index.tmpl")));
var mannequin = new TodoList();
var html = template.execute(mannequin);
var parser = Parser.htmlParser().setTrackErrors(10);
Jsoup.parse(html, "", parser);
assertThat(parser.getErrors()).isEmpty();
}
And we see it fail:
java.lang.AssertionError: Anticipating empty however was:<(<1:13>: Sudden EndTag token () when in state (InBody),
Success! Now if we copy TodoMVC template content material to our index.tmpl
file, the take a look at passes.
The proof, nonetheless, is just too detailed: we extract two auxiliary features to make clear the intent of the proof and procure
Go
func Test_wellFormedHtml(t *testing.T) { mannequin := todo.NewList() buf := renderTemplate("index.tmpl", mannequin) assertWellFormedHtml(t, buf) }
Java
@Check void indexIsSoundHtml() { var mannequin = new TodoList(); var html = renderTemplate("/index.tmpl", mannequin); assertSoundHtml(html); }
Stage 2: Check the HTML construction
What else ought to we attempt?
We all know that in the end solely a human can take a look at the look of a web page and see the way it renders in a browser. Nonetheless, there’s usually logic in templates and we wish to have the ability to take a look at that logic.
One could be tempted to attempt rendered HTML with string equality, however this system fails in follow, as a result of templates include many particulars that make string equality assertions impractical. The claims change into very detailed and when studying them it’s obscure what we are attempting to show.
What we want is a method to claim that some elements of the rendered HTML correspond to what we anticipate, since ignore all the small print that do not matter to us. A technique to do that is by operating queries with the CSS selector language: is a robust language that enables us to pick the weather that curiosity us from the whole HTML doc. As soon as we’ve chosen these components, we (1) depend that the variety of components returned is what we anticipate and (2) that they include the textual content or different content material we anticipate.
The UI we’re speculated to generate appears to be like like this:
There are a number of particulars which are rendered dynamically:
- The variety of components and their textual content material change, clearly.
- The model of the duty merchandise adjustments when it’s accomplished (for instance, the second)
- The “2 objects left” textual content will change based mostly on the variety of uncompleted objects
- One of many three buttons “All”, “Lively”, “Accomplished” will probably be highlighted, relying on the present URL; for instance, if we resolve that the URL that exhibits solely “lively” objects is
/lively
then when the present url is/lively
the “Lively” button ought to be surrounded by a skinny pink rectangle - The “Clear Accomplished” button ought to solely be seen if any merchandise is accomplished
Every of those considerations will be examined with the assistance of CSS selectors.
It is a snippet of the TodoMVC template (barely simplified). I have not added the dynamic bits but, so what we see right here is static content material, offered for example:
index.tmpl