-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhtml.go
More file actions
135 lines (102 loc) · 3.42 KB
/
Copy pathhtml.go
File metadata and controls
135 lines (102 loc) · 3.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package testastic
import (
"errors"
"fmt"
"strings"
"testing"
)
// errUnsupportedHTMLType is returned when an unsupported type is passed to AssertHTML.
var errUnsupportedHTMLType = errors.New("unsupported type for HTML comparison")
// AssertHTML compares actual HTML against an expected HTML file.
// T can be: []byte, string, io.Reader, or any type implementing fmt.Stringer.
//
// Example:
//
// testastic.AssertHTML(t, "testdata/user.expected.html", resp.Body)
// testastic.AssertHTML(t, "testdata/user.expected.html", htmlBytes)
// testastic.AssertHTML(t, "testdata/user.expected.html", htmlString)
func AssertHTML[T any](tb testing.TB, expectedFile string, actual T, opts ...Option) {
tb.Helper()
actualBytes, err := toHTMLBytes(actual)
if err != nil {
tb.Fatalf("testastic: failed to convert actual to bytes: %v", err)
return
}
cfg := newConfig(opts)
if unsupported := cfg.validateOptions(assertHTML); len(unsupported) > 0 {
tb.Fatalf("testastic: unsupported options for AssertHTML: %s", strings.Join(unsupported, ", "))
return
}
if handleMissingExpectedFile(tb, expectedFile, actualBytes, cfg.Update, createExpectedHTMLFile) {
return
}
expected, err := parseExpectedHTMLFile(expectedFile)
if err != nil {
tb.Fatalf("testastic: %v", err)
return
}
actualNode, err := parseActualHTMLBytes(actualBytes)
if err != nil {
tb.Fatalf("testastic: %v", err)
return
}
diffs := compareHTML(expected.Root, actualNode, cfg)
if handleHTMLDiffs(tb, expectedFile, actualBytes, expected.Root, actualNode, diffs, cfg) {
return
}
}
// handleHTMLDiffs handles update mode and error reporting for HTML.
// Returns true if the assertion should stop.
func handleHTMLDiffs(
tb testing.TB, path string, actualBytes []byte, expectedRoot, actualNode *htmlNode,
diffs []htmlDifference, cfg *config,
) bool {
tb.Helper()
if len(diffs) == 0 {
return false
}
if cfg.Update {
updateErr := updateExpectedHTMLFile(path, actualBytes)
if updateErr != nil {
tb.Fatalf("testastic: failed to update expected HTML file: %v", updateErr)
}
tb.Logf("testastic: updated expected HTML file %s", path)
return true
}
sortHTMLDiffs(diffs)
msg := formatAssertionMessage("AssertHTML", path, cfg.Message)
tb.Errorf("testastic: assertion failed\n\n %s\n%s", msg, formatHTMLDiffInline(expectedRoot, actualNode, cfg))
return false
}
func toHTMLBytes[T any](v T) ([]byte, error) {
if data, handled, err := bytesFromCommonInput(v); handled {
return data, err
}
if stringer, ok := any(v).(fmt.Stringer); ok {
return []byte(stringer.String()), nil
}
return nil, fmt.Errorf("unsupported HTML value of type %T: %w", v, errUnsupportedHTMLType)
}
func createExpectedHTMLFile(path string, actual []byte) error {
// Parse and re-render for consistent formatting
node, err := parseActualHTMLBytes(actual)
if err != nil {
// If parsing fails, just write the raw content
return writeHTMLFile(path, actual)
}
formatted := renderPrettyHTML(node, 0)
return writeHTMLFile(path, []byte(formatted))
}
func updateExpectedHTMLFile(path string, actual []byte) error {
// Parse and re-render for consistent formatting
node, err := parseActualHTMLBytes(actual)
if err != nil {
// If parsing fails, just write the raw content
return writeHTMLFile(path, actual)
}
formatted := renderPrettyHTML(node, 0)
return writeHTMLFile(path, []byte(formatted))
}
func writeHTMLFile(path string, data []byte) error {
return writeFileAtomic(path, data)
}