Skip to content

Commit 108239d

Browse files
author
samuelhulla
committed
chore: printer and parser tests
1 parent 025677e commit 108239d

26 files changed

Lines changed: 1166 additions & 1 deletion

internal/parser_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,3 +126,129 @@ func findTargetNode(doc *Node) *Node {
126126
})
127127
return target
128128
}
129+
130+
type DuplicateAttributeTest struct {
131+
name string
132+
input string
133+
targetId string
134+
expectedAttrs map[string]string // key -> value
135+
expectedLength int // expected number of attributes
136+
}
137+
138+
func TestDuplicateAttributes(t *testing.T) {
139+
cases := []DuplicateAttributeTest{
140+
{
141+
name: "duplicate class attributes - last wins",
142+
input: `<div id="target" class="first" class="second" class="third"></div>`,
143+
targetId: "target",
144+
expectedAttrs: map[string]string{
145+
"id": "target",
146+
"class": "third",
147+
},
148+
expectedLength: 2,
149+
},
150+
{
151+
name: "duplicate data attributes - last wins",
152+
input: `<div id="target" data-value="1" data-value="2"></div>`,
153+
targetId: "target",
154+
expectedAttrs: map[string]string{
155+
"id": "target",
156+
"data-value": "2",
157+
},
158+
expectedLength: 2,
159+
},
160+
{
161+
name: "multiple different duplicates",
162+
input: `<div id="target" class="a" title="first" class="b" title="second"></div>`,
163+
targetId: "target",
164+
expectedAttrs: map[string]string{
165+
"id": "target",
166+
"class": "b",
167+
"title": "second",
168+
},
169+
expectedLength: 3,
170+
},
171+
{
172+
name: "no duplicates",
173+
input: `<div id="target" class="test" title="hello"></div>`,
174+
targetId: "target",
175+
expectedAttrs: map[string]string{
176+
"id": "target",
177+
"class": "test",
178+
"title": "hello",
179+
},
180+
expectedLength: 3,
181+
},
182+
{
183+
name: "duplicate with namespace - last wins",
184+
input: `<svg id="target" xmlns:xlink="http://first" xmlns:xlink="http://second"></svg>`,
185+
targetId: "target",
186+
expectedAttrs: map[string]string{
187+
"id": "target",
188+
},
189+
expectedLength: 2, // id + xlink namespace
190+
},
191+
{
192+
name: "three duplicates of same attribute",
193+
input: `<div id="target" data-test="one" data-test="two" data-test="three"></div>`,
194+
targetId: "target",
195+
expectedAttrs: map[string]string{
196+
"id": "target",
197+
"data-test": "three",
198+
},
199+
expectedLength: 2,
200+
},
201+
{
202+
name: "duplicate aria attributes",
203+
input: `<button id="target" aria-label="first" aria-label="second"></button>`,
204+
targetId: "target",
205+
expectedAttrs: map[string]string{
206+
"id": "target",
207+
"aria-label": "second",
208+
},
209+
expectedLength: 2,
210+
},
211+
}
212+
213+
for _, tt := range cases {
214+
t.Run(tt.name, func(t *testing.T) {
215+
code := test_utils.Dedent(tt.input)
216+
doc, err := Parse(strings.NewReader(code))
217+
218+
if err != nil {
219+
t.Errorf("Parse error: %v", err)
220+
return
221+
}
222+
223+
target := findTargetNode(doc)
224+
if target == nil {
225+
t.Error("Target node not found")
226+
return
227+
}
228+
229+
if len(target.Attr) != tt.expectedLength {
230+
t.Errorf("Expected %d attributes, got %d", tt.expectedLength, len(target.Attr))
231+
}
232+
233+
for key, expectedVal := range tt.expectedAttrs {
234+
found := false
235+
for _, attr := range target.Attr {
236+
attrKey := attr.Key
237+
if attr.Namespace != "" {
238+
attrKey = attr.Namespace + ":" + attr.Key
239+
}
240+
if attrKey == key {
241+
found = true
242+
if attr.Val != expectedVal {
243+
t.Errorf("For attribute %s: expected value %q, got %q", key, expectedVal, attr.Val)
244+
}
245+
break
246+
}
247+
}
248+
if !found {
249+
t.Errorf("Expected attribute %s not found", key)
250+
}
251+
}
252+
})
253+
}
254+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
[TestPrinter/complex_duplicate_scenario - 1]
3+
## Input
4+
5+
```
6+
<div class="a" id="1" class="b" title="x" id="2" class="c"></div>
7+
```
8+
9+
## Output
10+
11+
```js
12+
import {
13+
Fragment,
14+
render as $$render,
15+
createAstro as $$createAstro,
16+
createComponent as $$createComponent,
17+
renderComponent as $$renderComponent,
18+
renderHead as $$renderHead,
19+
maybeRenderHead as $$maybeRenderHead,
20+
unescapeHTML as $$unescapeHTML,
21+
renderSlot as $$renderSlot,
22+
mergeSlots as $$mergeSlots,
23+
addAttribute as $$addAttribute,
24+
spreadAttributes as $$spreadAttributes,
25+
defineStyleVars as $$defineStyleVars,
26+
defineScriptVars as $$defineScriptVars,
27+
renderTransition as $$renderTransition,
28+
createTransitionScope as $$createTransitionScope,
29+
renderScript as $$renderScript,
30+
createMetadata as $$createMetadata
31+
} from "http://localhost:3000/";
32+
33+
export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });
34+
35+
const $$Component = $$createComponent(($$result, $$props, $$slots) => {
36+
37+
return $$render`${$$maybeRenderHead($$result)}<div title="x" id="2" class="c"></div>`;
38+
}, undefined, undefined);
39+
export default $$Component;
40+
```
41+
---
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
[TestPrinter/duplicate_aria_attributes - 1]
3+
## Input
4+
5+
```
6+
<button aria-label="first" aria-label="second"></button>
7+
```
8+
9+
## Output
10+
11+
```js
12+
import {
13+
Fragment,
14+
render as $$render,
15+
createAstro as $$createAstro,
16+
createComponent as $$createComponent,
17+
renderComponent as $$renderComponent,
18+
renderHead as $$renderHead,
19+
maybeRenderHead as $$maybeRenderHead,
20+
unescapeHTML as $$unescapeHTML,
21+
renderSlot as $$renderSlot,
22+
mergeSlots as $$mergeSlots,
23+
addAttribute as $$addAttribute,
24+
spreadAttributes as $$spreadAttributes,
25+
defineStyleVars as $$defineStyleVars,
26+
defineScriptVars as $$defineScriptVars,
27+
renderTransition as $$renderTransition,
28+
createTransitionScope as $$createTransitionScope,
29+
renderScript as $$renderScript,
30+
createMetadata as $$createMetadata
31+
} from "http://localhost:3000/";
32+
33+
export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });
34+
35+
const $$Component = $$createComponent(($$result, $$props, $$slots) => {
36+
37+
return $$render`${$$maybeRenderHead($$result)}<button aria-label="second"></button>`;
38+
}, undefined, undefined);
39+
export default $$Component;
40+
```
41+
---
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
[TestPrinter/duplicate_attributes_on_components - 1]
3+
## Input
4+
5+
```
6+
<Component prop="a" prop="b"></Component>
7+
```
8+
9+
## Output
10+
11+
```js
12+
import {
13+
Fragment,
14+
render as $$render,
15+
createAstro as $$createAstro,
16+
createComponent as $$createComponent,
17+
renderComponent as $$renderComponent,
18+
renderHead as $$renderHead,
19+
maybeRenderHead as $$maybeRenderHead,
20+
unescapeHTML as $$unescapeHTML,
21+
renderSlot as $$renderSlot,
22+
mergeSlots as $$mergeSlots,
23+
addAttribute as $$addAttribute,
24+
spreadAttributes as $$spreadAttributes,
25+
defineStyleVars as $$defineStyleVars,
26+
defineScriptVars as $$defineScriptVars,
27+
renderTransition as $$renderTransition,
28+
createTransitionScope as $$createTransitionScope,
29+
renderScript as $$renderScript,
30+
createMetadata as $$createMetadata
31+
} from "http://localhost:3000/";
32+
33+
export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });
34+
35+
const $$Component = $$createComponent(($$result, $$props, $$slots) => {
36+
37+
return $$render`${$$renderComponent($$result,'Component',Component,{"prop":"b"})}`;
38+
}, undefined, undefined);
39+
export default $$Component;
40+
```
41+
---
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
[TestPrinter/duplicate_attributes_with_expressions_and_strings - 1]
3+
## Input
4+
5+
```
6+
<div data-value="static" data-value={dynamic}></div>
7+
```
8+
9+
## Output
10+
11+
```js
12+
import {
13+
Fragment,
14+
render as $$render,
15+
createAstro as $$createAstro,
16+
createComponent as $$createComponent,
17+
renderComponent as $$renderComponent,
18+
renderHead as $$renderHead,
19+
maybeRenderHead as $$maybeRenderHead,
20+
unescapeHTML as $$unescapeHTML,
21+
renderSlot as $$renderSlot,
22+
mergeSlots as $$mergeSlots,
23+
addAttribute as $$addAttribute,
24+
spreadAttributes as $$spreadAttributes,
25+
defineStyleVars as $$defineStyleVars,
26+
defineScriptVars as $$defineScriptVars,
27+
renderTransition as $$renderTransition,
28+
createTransitionScope as $$createTransitionScope,
29+
renderScript as $$renderScript,
30+
createMetadata as $$createMetadata
31+
} from "http://localhost:3000/";
32+
33+
export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });
34+
35+
const $$Component = $$createComponent(($$result, $$props, $$slots) => {
36+
37+
return $$render`${$$maybeRenderHead($$result)}<div${$$addAttribute(dynamic, "data-value")}></div>`;
38+
}, undefined, undefined);
39+
export default $$Component;
40+
```
41+
---
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
[TestPrinter/duplicate_data_attributes - 1]
3+
## Input
4+
5+
```
6+
<div data-test="a" data-test="b"></div>
7+
```
8+
9+
## Output
10+
11+
```js
12+
import {
13+
Fragment,
14+
render as $$render,
15+
createAstro as $$createAstro,
16+
createComponent as $$createComponent,
17+
renderComponent as $$renderComponent,
18+
renderHead as $$renderHead,
19+
maybeRenderHead as $$maybeRenderHead,
20+
unescapeHTML as $$unescapeHTML,
21+
renderSlot as $$renderSlot,
22+
mergeSlots as $$mergeSlots,
23+
addAttribute as $$addAttribute,
24+
spreadAttributes as $$spreadAttributes,
25+
defineStyleVars as $$defineStyleVars,
26+
defineScriptVars as $$defineScriptVars,
27+
renderTransition as $$renderTransition,
28+
createTransitionScope as $$createTransitionScope,
29+
renderScript as $$renderScript,
30+
createMetadata as $$createMetadata
31+
} from "http://localhost:3000/";
32+
33+
export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });
34+
35+
const $$Component = $$createComponent(($$result, $$props, $$slots) => {
36+
37+
return $$render`${$$maybeRenderHead($$result)}<div data-test="b"></div>`;
38+
}, undefined, undefined);
39+
export default $$Component;
40+
```
41+
---
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
2+
[TestPrinter/duplicate_empty_attributes - 1]
3+
## Input
4+
5+
```
6+
<div disabled disabled></div>
7+
```
8+
9+
## Output
10+
11+
```js
12+
import {
13+
Fragment,
14+
render as $$render,
15+
createAstro as $$createAstro,
16+
createComponent as $$createComponent,
17+
renderComponent as $$renderComponent,
18+
renderHead as $$renderHead,
19+
maybeRenderHead as $$maybeRenderHead,
20+
unescapeHTML as $$unescapeHTML,
21+
renderSlot as $$renderSlot,
22+
mergeSlots as $$mergeSlots,
23+
addAttribute as $$addAttribute,
24+
spreadAttributes as $$spreadAttributes,
25+
defineStyleVars as $$defineStyleVars,
26+
defineScriptVars as $$defineScriptVars,
27+
renderTransition as $$renderTransition,
28+
createTransitionScope as $$createTransitionScope,
29+
renderScript as $$renderScript,
30+
createMetadata as $$createMetadata
31+
} from "http://localhost:3000/";
32+
33+
export const $$metadata = $$createMetadata(import.meta.url, { modules: [], hydratedComponents: [], clientOnlyComponents: [], hydrationDirectives: new Set([]), hoisted: [] });
34+
35+
const $$Component = $$createComponent(($$result, $$props, $$slots) => {
36+
37+
return $$render`${$$maybeRenderHead($$result)}<div disabled></div>`;
38+
}, undefined, undefined);
39+
export default $$Component;
40+
```
41+
---

0 commit comments

Comments
 (0)