blob: 607b6e2ca8c8138f87825a0fa508fd2ec7275f8e [file] [log] [blame]
// Copyright 2025 The Dawn & Tint Authors
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
// list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package main
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/net/html"
)
func TestGatherExamples(t *testing.T) {
tests := []struct {
name string
html string
expected []example
}{
{
name: "empty html",
html: "",
expected: []example{},
},
{
name: "no examples",
html: "<html><body><p>Hello</p></body></html>",
expected: []example{},
},
{
name: "single global-scope example",
html: `
<div class="example wgsl global-scope" id="ex1">
<pre>var x : i32;</pre>
</div>
`,
expected: []example{
{
name: "ex1",
code: "var x : i32;",
globalScope: true,
functionScope: false,
expectError: false,
},
},
},
{
name: "single function-scope example",
html: `
<div class="example wgsl function-scope" id="ex2">
<pre>let y = 1;</pre>
</div>
`,
expected: []example{
{
name: "ex2",
code: "let y = 1;",
globalScope: false,
functionScope: true,
expectError: false,
},
},
},
{
name: "expect-error example",
html: `
<div class="example wgsl global-scope expect-error" id="ex3">
<pre>error</pre>
</div>
`,
expected: []example{
{
name: "ex3",
code: "error",
globalScope: true,
functionScope: false,
expectError: true,
},
},
},
{
name: "ignored example (no scope)",
html: `
<div class="example wgsl" id="ignored">
<pre>ignore me</pre>
</div>
`,
expected: []example{},
},
{
name: "ignored example (not wgsl)",
html: `
<div class="example cpp global-scope" id="cpp">
<pre>int main() {}</pre>
</div>
`,
expected: []example{},
},
{
name: "ignored example (not example class)",
html: `
<div class="code wgsl global-scope" id="not-ex">
<pre>code</pre>
</div>
`,
expected: []example{},
},
{
name: "multiple examples",
html: `
<div class="example wgsl global-scope" id="ex1">
<pre>ex1</pre>
</div>
<div>
<div class="example wgsl function-scope" id="ex2">
<pre>ex2</pre>
</div>
</div>
`,
expected: []example{
{
name: "ex1",
code: "ex1",
globalScope: true,
functionScope: false,
expectError: false,
},
{
name: "ex2",
code: "ex2",
globalScope: false,
functionScope: true,
expectError: false,
},
},
},
{
name: "complex content",
html: `
<div class="example wgsl global-scope" id="ex_complex">
<pre>part1 <span>part2</span> part3</pre>
</div>
`,
expected: []example{
{
name: "ex_complex",
code: "part1 part2 part3",
globalScope: true,
functionScope: false,
expectError: false,
},
},
},
{
name: "multiple pre blocks",
html: `
<div class="example wgsl global-scope" id="ex_multi_pre">
<pre>part1</pre>
<pre>part2</pre>
</div>
`,
expected: []example{
{
name: "ex_multi_pre",
code: "part1part2",
globalScope: true,
functionScope: false,
expectError: false,
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
doc, err := html.Parse(strings.NewReader(tc.html))
require.NoError(t, err)
examples := []example{}
err = gatherExamples(doc, &examples)
require.NoError(t, err)
require.Equal(t, tc.expected, examples)
})
}
}
func TestPrintNodeText(t *testing.T) {
tests := []struct {
name string
node *html.Node
expected string
}{
{
name: "single text node",
node: &html.Node{Type: html.TextNode, Data: "Hello"},
expected: "Hello",
},
{
name: "nested text nodes",
node: &html.Node{
Type: html.ElementNode, Data: "div",
FirstChild: &html.Node{Type: html.TextNode, Data: "Part1",
NextSibling: &html.Node{
Type: html.ElementNode, Data: "span",
FirstChild: &html.Node{Type: html.TextNode, Data: "Part2"},
NextSibling: &html.Node{Type: html.TextNode, Data: "Part3"},
},
},
},
expected: "Part1Part2Part3",
},
{
name: "empty node",
node: &html.Node{Type: html.ElementNode, Data: "div"},
expected: "",
},
{
name: "node with no text children",
node: &html.Node{Type: html.ElementNode, Data: "div", FirstChild: &html.Node{Type: html.ElementNode, Data: "span"}},
expected: "",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
sb := &strings.Builder{}
printNodeText(tc.node, sb)
require.Equal(t, tc.expected, sb.String())
})
}
}
func TestHasClass(t *testing.T) {
node := &html.Node{
Attr: []html.Attribute{
{Key: "class", Val: "foo bar baz"},
},
}
require.True(t, hasClass(node, "foo"))
require.True(t, hasClass(node, "bar"))
require.True(t, hasClass(node, "baz"))
require.False(t, hasClass(node, "fo"))
require.False(t, hasClass(node, "ba"))
}
func TestNodeID(t *testing.T) {
nodeWithID := &html.Node{
Attr: []html.Attribute{
{Key: "id", Val: "my-id"},
},
}
require.Equal(t, "my-id", nodeID(nodeWithID))
nodeWithoutID := &html.Node{
Attr: []html.Attribute{
{Key: "class", Val: "foo"},
},
}
require.Equal(t, "", nodeID(nodeWithoutID))
}