@tkeron/html-parser 0.1.5 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -7
- package/bun.lock +8 -3
- package/index.ts +4 -0
- package/package.json +13 -6
- package/src/css-selector.ts +45 -27
- package/src/dom-simulator.ts +162 -20
- package/src/encoding.ts +39 -0
- package/src/index.ts +9 -0
- package/src/parser.ts +478 -183
- package/src/serializer.ts +450 -0
- package/src/tokenizer.ts +59 -139
- package/tests/advanced.test.ts +119 -106
- package/tests/custom-elements.test.ts +172 -162
- package/tests/dom-extended.test.ts +12 -12
- package/tests/dom-manipulation.test.ts +637 -0
- package/tests/dom.test.ts +32 -27
- package/tests/helpers/tokenizer-adapter.test.ts +70 -0
- package/tests/helpers/tokenizer-adapter.ts +65 -0
- package/tests/helpers/tree-adapter.test.ts +39 -0
- package/tests/helpers/tree-adapter.ts +43 -0
- package/tests/html5lib-data/tokenizer/namedEntities.test +42422 -0
- package/tests/html5lib-data/tokenizer/pendingSpecChanges.test +9 -0
- package/tests/html5lib-data/tree-construction/adoption01.dat +354 -0
- package/tests/html5lib-data/tree-construction/adoption02.dat +39 -0
- package/tests/html5lib-data/tree-construction/domjs-unsafe.dat +0 -0
- package/tests/html5lib-data/tree-construction/entities02.dat +309 -0
- package/tests/html5lib-data/tree-construction/html5test-com.dat +301 -0
- package/tests/html5lib-data/tree-construction/math.dat +104 -0
- package/tests/html5lib-data/tree-construction/namespace-sensitivity.dat +22 -0
- package/tests/html5lib-data/tree-construction/noscript01.dat +237 -0
- package/tests/html5lib-data/tree-construction/ruby.dat +302 -0
- package/tests/html5lib-data/tree-construction/scriptdata01.dat +372 -0
- package/tests/html5lib-data/tree-construction/svg.dat +104 -0
- package/tests/html5lib-data/tree-construction/template.dat +1673 -0
- package/tests/html5lib-data/tree-construction/tests10.dat +853 -0
- package/tests/html5lib-data/tree-construction/tests11.dat +523 -0
- package/tests/html5lib-data/tree-construction/tests20.dat +842 -0
- package/tests/html5lib-data/tree-construction/tests21.dat +306 -0
- package/tests/html5lib-data/tree-construction/tests23.dat +168 -0
- package/tests/html5lib-data/tree-construction/tests24.dat +79 -0
- package/tests/html5lib-data/tree-construction/tests5.dat +210 -0
- package/tests/html5lib-data/tree-construction/tests6.dat +663 -0
- package/tests/html5lib-data/tree-construction/tests_innerHTML_1.dat +844 -0
- package/tests/parser.test.ts +172 -193
- package/tests/selectors.test.ts +64 -1
- package/tests/serializer-core.test.ts +16 -0
- package/tests/serializer-data/core.test +125 -0
- package/tests/serializer-data/injectmeta.test +66 -0
- package/tests/serializer-data/optionaltags.test +965 -0
- package/tests/serializer-data/options.test +60 -0
- package/tests/serializer-data/whitespace.test +51 -0
- package/tests/serializer-injectmeta.test.ts +16 -0
- package/tests/serializer-optionaltags.test.ts +16 -0
- package/tests/serializer-options.test.ts +16 -0
- package/tests/serializer-whitespace.test.ts +16 -0
- package/tests/tokenizer-namedEntities.test.ts +20 -0
- package/tests/tokenizer-pendingSpecChanges.test.ts +20 -0
- package/tests/tokenizer.test.ts +83 -0
- package/tests/tree-construction-adoption01.test.ts +37 -0
- package/tests/tree-construction-adoption02.test.ts +34 -0
- package/tests/tree-construction-domjs-unsafe.test.ts +24 -0
- package/tests/tree-construction-entities02.test.ts +33 -0
- package/tests/tree-construction-html5test-com.test.ts +24 -0
- package/tests/tree-construction-math.test.ts +18 -0
- package/tests/tree-construction-namespace-sensitivity.test.ts +18 -0
- package/tests/tree-construction-noscript01.test.ts +18 -0
- package/tests/tree-construction-ruby.test.ts +21 -0
- package/tests/tree-construction-scriptdata01.test.ts +21 -0
- package/tests/tree-construction-svg.test.ts +21 -0
- package/tests/tree-construction-template.test.ts +21 -0
- package/tests/tree-construction-tests10.test.ts +21 -0
- package/tests/tree-construction-tests11.test.ts +21 -0
- package/tests/tree-construction-tests20.test.ts +18 -0
- package/tests/tree-construction-tests21.test.ts +18 -0
- package/tests/tree-construction-tests23.test.ts +18 -0
- package/tests/tree-construction-tests24.test.ts +18 -0
- package/tests/tree-construction-tests5.test.ts +21 -0
- package/tests/tree-construction-tests6.test.ts +21 -0
- package/tests/tree-construction-tests_innerHTML_1.test.ts +21 -0
- package/tests/void-elements.test.ts +471 -0
- package/tests/official/README.md +0 -87
- package/tests/official/acid/acid-tests.test.ts +0 -309
- package/tests/official/final-output/final-output.test.ts +0 -361
- package/tests/official/html5lib/tokenizer-utils.ts +0 -192
- package/tests/official/html5lib/tokenizer.test.ts +0 -171
- package/tests/official/html5lib/tree-construction-utils.ts +0 -194
- package/tests/official/html5lib/tree-construction.test.ts +0 -250
- package/tests/official/validator/validator-tests.test.ts +0 -237
- package/tests/official/validator-nu/validator-nu.test.ts +0 -335
- package/tests/official/whatwg/whatwg-tests.test.ts +0 -205
- package/tests/official/wpt/wpt-tests.test.ts +0 -409
|
@@ -1,94 +1,108 @@
|
|
|
1
|
-
import { expect,
|
|
1
|
+
import { expect, it, describe } from 'bun:test';
|
|
2
2
|
import { tokenize } from '../src/tokenizer';
|
|
3
|
-
import { parse, ASTNodeType, type ASTNode } from '../src/parser';
|
|
3
|
+
import { parse, domToAST, ASTNodeType, type ASTNode } from '../src/parser';
|
|
4
|
+
|
|
5
|
+
function parseToAST(tokens: any[]): any {
|
|
6
|
+
const dom = parse(tokens);
|
|
7
|
+
const ast = domToAST(dom);
|
|
8
|
+
|
|
9
|
+
const htmlEl = ast.children?.find((c: any) => c.tagName === 'html');
|
|
10
|
+
if (htmlEl) {
|
|
11
|
+
const bodyEl = htmlEl.children?.find((c: any) => c.tagName === 'body');
|
|
12
|
+
if (bodyEl && bodyEl.children) {
|
|
13
|
+
return { type: ASTNodeType.Document, children: bodyEl.children };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return ast;
|
|
17
|
+
}
|
|
4
18
|
|
|
5
19
|
describe('Custom Elements Support', () => {
|
|
6
20
|
|
|
7
21
|
describe('Basic Custom Elements', () => {
|
|
8
|
-
|
|
22
|
+
it('should parse simple custom element with single hyphen', () => {
|
|
9
23
|
const tokens = tokenize('<my-component></my-component>');
|
|
10
|
-
const ast =
|
|
24
|
+
const ast = parseToAST(tokens);
|
|
11
25
|
|
|
12
|
-
expect(ast.type).toBe(ASTNodeType.
|
|
26
|
+
expect(ast.type).toBe(ASTNodeType.Document);
|
|
13
27
|
expect(ast.children).toHaveLength(1);
|
|
14
28
|
|
|
15
29
|
const element = ast.children![0]!;
|
|
16
|
-
expect(element.type).toBe(ASTNodeType.
|
|
30
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
17
31
|
expect(element.tagName).toBe('my-component');
|
|
18
32
|
});
|
|
19
33
|
|
|
20
|
-
|
|
34
|
+
it('should parse custom element with numbers', () => {
|
|
21
35
|
const tokens = tokenize('<my-component-123></my-component-123>');
|
|
22
|
-
const ast =
|
|
36
|
+
const ast = parseToAST(tokens);
|
|
23
37
|
|
|
24
38
|
const element = ast.children![0]!;
|
|
25
|
-
expect(element.type).toBe(ASTNodeType.
|
|
39
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
26
40
|
expect(element.tagName).toBe('my-component-123');
|
|
27
41
|
});
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
it('should parse short custom element', () => {
|
|
30
44
|
const tokens = tokenize('<x-button></x-button>');
|
|
31
|
-
const ast =
|
|
45
|
+
const ast = parseToAST(tokens);
|
|
32
46
|
|
|
33
47
|
const element = ast.children![0]!;
|
|
34
|
-
expect(element.type).toBe(ASTNodeType.
|
|
48
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
35
49
|
expect(element.tagName).toBe('x-button');
|
|
36
50
|
});
|
|
37
51
|
|
|
38
|
-
|
|
52
|
+
it('should parse custom element with multiple hyphens', () => {
|
|
39
53
|
const tokens = tokenize('<app-header-nav></app-header-nav>');
|
|
40
|
-
const ast =
|
|
54
|
+
const ast = parseToAST(tokens);
|
|
41
55
|
|
|
42
56
|
const element = ast.children![0]!;
|
|
43
|
-
expect(element.type).toBe(ASTNodeType.
|
|
57
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
44
58
|
expect(element.tagName).toBe('app-header-nav');
|
|
45
59
|
});
|
|
46
60
|
|
|
47
|
-
|
|
61
|
+
it('should parse custom element with many hyphens', () => {
|
|
48
62
|
const tokens = tokenize('<my-custom-super-component></my-custom-super-component>');
|
|
49
|
-
const ast =
|
|
63
|
+
const ast = parseToAST(tokens);
|
|
50
64
|
|
|
51
65
|
const element = ast.children![0]!;
|
|
52
|
-
expect(element.type).toBe(ASTNodeType.
|
|
66
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
53
67
|
expect(element.tagName).toBe('my-custom-super-component');
|
|
54
68
|
});
|
|
55
69
|
|
|
56
|
-
|
|
70
|
+
it('should parse custom element with dots', () => {
|
|
57
71
|
const tokens = tokenize('<my-comp.v2></my-comp.v2>');
|
|
58
|
-
const ast =
|
|
72
|
+
const ast = parseToAST(tokens);
|
|
59
73
|
|
|
60
74
|
const element = ast.children![0]!;
|
|
61
|
-
expect(element.type).toBe(ASTNodeType.
|
|
75
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
62
76
|
expect(element.tagName).toBe('my-comp.v2');
|
|
63
77
|
});
|
|
64
78
|
|
|
65
|
-
|
|
79
|
+
it('should parse custom element with underscores', () => {
|
|
66
80
|
const tokens = tokenize('<my-comp_beta></my-comp_beta>');
|
|
67
|
-
const ast =
|
|
81
|
+
const ast = parseToAST(tokens);
|
|
68
82
|
|
|
69
83
|
const element = ast.children![0]!;
|
|
70
|
-
expect(element.type).toBe(ASTNodeType.
|
|
84
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
71
85
|
expect(element.tagName).toBe('my-comp_beta');
|
|
72
86
|
});
|
|
73
87
|
});
|
|
74
88
|
|
|
75
89
|
describe('Custom Elements with Attributes', () => {
|
|
76
|
-
|
|
90
|
+
it('should parse custom element with class attribute', () => {
|
|
77
91
|
const tokens = tokenize('<my-comp class="test"></my-comp>');
|
|
78
|
-
const ast =
|
|
92
|
+
const ast = parseToAST(tokens);
|
|
79
93
|
|
|
80
94
|
const element = ast.children![0]!;
|
|
81
|
-
expect(element.type).toBe(ASTNodeType.
|
|
95
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
82
96
|
expect(element.tagName).toBe('my-comp');
|
|
83
97
|
expect(element.attributes).toEqual({ class: 'test' });
|
|
84
98
|
});
|
|
85
99
|
|
|
86
|
-
|
|
100
|
+
it('should parse custom element with multiple attributes', () => {
|
|
87
101
|
const tokens = tokenize('<my-comp class="test" id="main" data-value="123"></my-comp>');
|
|
88
|
-
const ast =
|
|
102
|
+
const ast = parseToAST(tokens);
|
|
89
103
|
|
|
90
104
|
const element = ast.children![0]!;
|
|
91
|
-
expect(element.type).toBe(ASTNodeType.
|
|
105
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
92
106
|
expect(element.tagName).toBe('my-comp');
|
|
93
107
|
expect(element.attributes).toEqual({
|
|
94
108
|
class: 'test',
|
|
@@ -97,12 +111,12 @@ describe('Custom Elements Support', () => {
|
|
|
97
111
|
});
|
|
98
112
|
});
|
|
99
113
|
|
|
100
|
-
|
|
114
|
+
it('should parse custom element with custom attributes', () => {
|
|
101
115
|
const tokens = tokenize('<user-card name="John" age="30"></user-card>');
|
|
102
|
-
const ast =
|
|
116
|
+
const ast = parseToAST(tokens);
|
|
103
117
|
|
|
104
118
|
const element = ast.children![0]!;
|
|
105
|
-
expect(element.type).toBe(ASTNodeType.
|
|
119
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
106
120
|
expect(element.tagName).toBe('user-card');
|
|
107
121
|
expect(element.attributes).toEqual({
|
|
108
122
|
name: 'John',
|
|
@@ -112,34 +126,31 @@ describe('Custom Elements Support', () => {
|
|
|
112
126
|
});
|
|
113
127
|
|
|
114
128
|
describe('Self-Closing Custom Elements', () => {
|
|
115
|
-
|
|
129
|
+
it('should parse self-closing custom element with space', () => {
|
|
116
130
|
const tokens = tokenize('<self-closing />');
|
|
117
|
-
const ast =
|
|
131
|
+
const ast = parseToAST(tokens);
|
|
118
132
|
|
|
119
133
|
const element = ast.children![0]!;
|
|
120
|
-
expect(element.type).toBe(ASTNodeType.
|
|
134
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
121
135
|
expect(element.tagName).toBe('self-closing');
|
|
122
|
-
expect(element.isSelfClosing).toBe(true);
|
|
123
136
|
});
|
|
124
137
|
|
|
125
|
-
|
|
138
|
+
it('should parse self-closing custom element without space', () => {
|
|
126
139
|
const tokens = tokenize('<my-comp/>');
|
|
127
|
-
const ast =
|
|
140
|
+
const ast = parseToAST(tokens);
|
|
128
141
|
|
|
129
142
|
const element = ast.children![0]!;
|
|
130
|
-
expect(element.type).toBe(ASTNodeType.
|
|
143
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
131
144
|
expect(element.tagName).toBe('my-comp');
|
|
132
|
-
expect(element.isSelfClosing).toBe(true);
|
|
133
145
|
});
|
|
134
146
|
|
|
135
|
-
|
|
147
|
+
it('should parse self-closing custom element with attributes', () => {
|
|
136
148
|
const tokens = tokenize('<icon-button type="primary" size="lg" />');
|
|
137
|
-
const ast =
|
|
149
|
+
const ast = parseToAST(tokens);
|
|
138
150
|
|
|
139
151
|
const element = ast.children![0]!;
|
|
140
|
-
expect(element.type).toBe(ASTNodeType.
|
|
152
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
141
153
|
expect(element.tagName).toBe('icon-button');
|
|
142
|
-
expect(element.isSelfClosing).toBe(true);
|
|
143
154
|
expect(element.attributes).toEqual({
|
|
144
155
|
type: 'primary',
|
|
145
156
|
size: 'lg'
|
|
@@ -148,28 +159,28 @@ describe('Custom Elements Support', () => {
|
|
|
148
159
|
});
|
|
149
160
|
|
|
150
161
|
describe('Nested Custom Elements', () => {
|
|
151
|
-
|
|
162
|
+
it('should parse nested custom elements', () => {
|
|
152
163
|
const tokens = tokenize('<outer-comp><inner-comp>text</inner-comp></outer-comp>');
|
|
153
|
-
const ast =
|
|
164
|
+
const ast = parseToAST(tokens);
|
|
154
165
|
|
|
155
166
|
const outer = ast.children![0]!;
|
|
156
|
-
expect(outer.type).toBe(ASTNodeType.
|
|
167
|
+
expect(outer.type).toBe(ASTNodeType.Element);
|
|
157
168
|
expect(outer.tagName).toBe('outer-comp');
|
|
158
169
|
expect(outer.children).toHaveLength(1);
|
|
159
170
|
|
|
160
171
|
const inner = outer.children![0]!;
|
|
161
|
-
expect(inner.type).toBe(ASTNodeType.
|
|
172
|
+
expect(inner.type).toBe(ASTNodeType.Element);
|
|
162
173
|
expect(inner.tagName).toBe('inner-comp');
|
|
163
174
|
expect(inner.children).toHaveLength(1);
|
|
164
175
|
|
|
165
176
|
const text = inner.children![0]!;
|
|
166
|
-
expect(text.type).toBe(ASTNodeType.
|
|
177
|
+
expect(text.type).toBe(ASTNodeType.Text);
|
|
167
178
|
expect(text.content).toBe('text');
|
|
168
179
|
});
|
|
169
180
|
|
|
170
|
-
|
|
181
|
+
it('should parse deeply nested custom elements', () => {
|
|
171
182
|
const tokens = tokenize('<level-1><level-2><level-3>content</level-3></level-2></level-1>');
|
|
172
|
-
const ast =
|
|
183
|
+
const ast = parseToAST(tokens);
|
|
173
184
|
|
|
174
185
|
const level1 = ast.children![0]!;
|
|
175
186
|
expect(level1.tagName).toBe('level-1');
|
|
@@ -181,9 +192,9 @@ describe('Custom Elements Support', () => {
|
|
|
181
192
|
expect(level3.tagName).toBe('level-3');
|
|
182
193
|
});
|
|
183
194
|
|
|
184
|
-
|
|
195
|
+
it('should parse custom elements mixed with standard elements', () => {
|
|
185
196
|
const tokens = tokenize('<div><my-comp><span>text</span></my-comp></div>');
|
|
186
|
-
const ast =
|
|
197
|
+
const ast = parseToAST(tokens);
|
|
187
198
|
|
|
188
199
|
const div = ast.children![0]!;
|
|
189
200
|
expect(div.tagName).toBe('div');
|
|
@@ -197,57 +208,57 @@ describe('Custom Elements Support', () => {
|
|
|
197
208
|
});
|
|
198
209
|
|
|
199
210
|
describe('Tag Name Normalization', () => {
|
|
200
|
-
|
|
211
|
+
it('should normalize custom element tagName to UPPERCASE', () => {
|
|
201
212
|
const tokens = tokenize('<my-comp></my-comp>');
|
|
202
|
-
const ast =
|
|
213
|
+
const ast = parseToAST(tokens);
|
|
203
214
|
|
|
204
215
|
const element = ast.children![0]!;
|
|
205
|
-
expect(element.tagName
|
|
216
|
+
expect(element.tagName).toBe('my-comp');
|
|
206
217
|
});
|
|
207
218
|
|
|
208
|
-
|
|
219
|
+
it('should normalize nodeName to UPPERCASE', () => {
|
|
209
220
|
const tokens = tokenize('<my-comp></my-comp>');
|
|
210
|
-
const ast =
|
|
221
|
+
const ast = parseToAST(tokens);
|
|
211
222
|
|
|
212
223
|
const element = ast.children![0]!;
|
|
213
224
|
|
|
214
225
|
if (element.nodeName) {
|
|
215
|
-
expect(element.nodeName
|
|
226
|
+
expect(element.nodeName).toBe('my-comp');
|
|
216
227
|
}
|
|
217
228
|
});
|
|
218
229
|
});
|
|
219
230
|
|
|
220
231
|
describe('Regression Tests - Standard Elements', () => {
|
|
221
|
-
|
|
232
|
+
it('should still parse standard div element', () => {
|
|
222
233
|
const tokens = tokenize('<div></div>');
|
|
223
|
-
const ast =
|
|
234
|
+
const ast = parseToAST(tokens);
|
|
224
235
|
|
|
225
236
|
const element = ast.children![0]!;
|
|
226
|
-
expect(element.type).toBe(ASTNodeType.
|
|
237
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
227
238
|
expect(element.tagName).toBe('div');
|
|
228
239
|
});
|
|
229
240
|
|
|
230
|
-
|
|
241
|
+
it('should still parse standard header element', () => {
|
|
231
242
|
const tokens = tokenize('<header></header>');
|
|
232
|
-
const ast =
|
|
243
|
+
const ast = parseToAST(tokens);
|
|
233
244
|
|
|
234
245
|
const element = ast.children![0]!;
|
|
235
|
-
expect(element.type).toBe(ASTNodeType.
|
|
246
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
236
247
|
expect(element.tagName).toBe('header');
|
|
237
248
|
});
|
|
238
249
|
|
|
239
|
-
|
|
250
|
+
it('should still parse standard section element', () => {
|
|
240
251
|
const tokens = tokenize('<section></section>');
|
|
241
|
-
const ast =
|
|
252
|
+
const ast = parseToAST(tokens);
|
|
242
253
|
|
|
243
254
|
const element = ast.children![0]!;
|
|
244
|
-
expect(element.type).toBe(ASTNodeType.
|
|
255
|
+
expect(element.type).toBe(ASTNodeType.Element);
|
|
245
256
|
expect(element.tagName).toBe('section');
|
|
246
257
|
});
|
|
247
258
|
|
|
248
|
-
|
|
259
|
+
it('should distinguish between header tag and header-comp custom element', () => {
|
|
249
260
|
const tokens = tokenize('<header></header><header-comp></header-comp>');
|
|
250
|
-
const ast =
|
|
261
|
+
const ast = parseToAST(tokens);
|
|
251
262
|
|
|
252
263
|
expect(ast.children).toHaveLength(2);
|
|
253
264
|
|
|
@@ -260,41 +271,41 @@ describe('Custom Elements Support', () => {
|
|
|
260
271
|
});
|
|
261
272
|
|
|
262
273
|
describe('Custom Elements with Different Formats', () => {
|
|
263
|
-
|
|
274
|
+
it('should parse custom element: my-comp', () => {
|
|
264
275
|
const tokens = tokenize('<my-comp></my-comp>');
|
|
265
|
-
const ast =
|
|
276
|
+
const ast = parseToAST(tokens);
|
|
266
277
|
|
|
267
278
|
const element = ast.children![0]!;
|
|
268
279
|
expect(element.tagName).toBe('my-comp');
|
|
269
280
|
});
|
|
270
281
|
|
|
271
|
-
|
|
282
|
+
it('should parse custom element: comp-v2', () => {
|
|
272
283
|
const tokens = tokenize('<comp-v2></comp-v2>');
|
|
273
|
-
const ast =
|
|
284
|
+
const ast = parseToAST(tokens);
|
|
274
285
|
|
|
275
286
|
const element = ast.children![0]!;
|
|
276
287
|
expect(element.tagName).toBe('comp-v2');
|
|
277
288
|
});
|
|
278
289
|
|
|
279
|
-
|
|
290
|
+
it('should parse custom element: my-comp-123', () => {
|
|
280
291
|
const tokens = tokenize('<my-comp-123></my-comp-123>');
|
|
281
|
-
const ast =
|
|
292
|
+
const ast = parseToAST(tokens);
|
|
282
293
|
|
|
283
294
|
const element = ast.children![0]!;
|
|
284
295
|
expect(element.tagName).toBe('my-comp-123');
|
|
285
296
|
});
|
|
286
297
|
|
|
287
|
-
|
|
298
|
+
it('should parse custom element: x-foo', () => {
|
|
288
299
|
const tokens = tokenize('<x-foo></x-foo>');
|
|
289
|
-
const ast =
|
|
300
|
+
const ast = parseToAST(tokens);
|
|
290
301
|
|
|
291
302
|
const element = ast.children![0]!;
|
|
292
303
|
expect(element.tagName).toBe('x-foo');
|
|
293
304
|
});
|
|
294
305
|
|
|
295
|
-
|
|
306
|
+
it('should parse custom element with numbers: comp-123-test', () => {
|
|
296
307
|
const tokens = tokenize('<comp-123-test></comp-123-test>');
|
|
297
|
-
const ast =
|
|
308
|
+
const ast = parseToAST(tokens);
|
|
298
309
|
|
|
299
310
|
const element = ast.children![0]!;
|
|
300
311
|
expect(element.tagName).toBe('comp-123-test');
|
|
@@ -302,17 +313,17 @@ describe('Custom Elements Support', () => {
|
|
|
302
313
|
});
|
|
303
314
|
|
|
304
315
|
describe('Edge Cases', () => {
|
|
305
|
-
|
|
316
|
+
it('should parse custom element with whitespace before closing bracket', () => {
|
|
306
317
|
const tokens = tokenize('<my-comp ></my-comp>');
|
|
307
|
-
const ast =
|
|
318
|
+
const ast = parseToAST(tokens);
|
|
308
319
|
|
|
309
320
|
const element = ast.children![0]!;
|
|
310
321
|
expect(element.tagName).toBe('my-comp');
|
|
311
322
|
});
|
|
312
323
|
|
|
313
|
-
|
|
324
|
+
it('should parse multiple custom elements in sequence', () => {
|
|
314
325
|
const tokens = tokenize('<first-comp></first-comp><second-comp></second-comp><third-comp></third-comp>');
|
|
315
|
-
const ast =
|
|
326
|
+
const ast = parseToAST(tokens);
|
|
316
327
|
|
|
317
328
|
expect(ast.children).toHaveLength(3);
|
|
318
329
|
expect(ast.children![0]!.tagName).toBe('first-comp');
|
|
@@ -320,20 +331,20 @@ describe('Custom Elements Support', () => {
|
|
|
320
331
|
expect(ast.children![2]!.tagName).toBe('third-comp');
|
|
321
332
|
});
|
|
322
333
|
|
|
323
|
-
|
|
334
|
+
it('should parse custom element with text content', () => {
|
|
324
335
|
const tokens = tokenize('<user-name>John Doe</user-name>');
|
|
325
|
-
const ast =
|
|
336
|
+
const ast = parseToAST(tokens);
|
|
326
337
|
|
|
327
338
|
const element = ast.children![0]!;
|
|
328
339
|
expect(element.tagName).toBe('user-name');
|
|
329
340
|
expect(element.children).toHaveLength(1);
|
|
330
|
-
expect(element.children![0]!.type).toBe(ASTNodeType.
|
|
341
|
+
expect(element.children![0]!.type).toBe(ASTNodeType.Text);
|
|
331
342
|
expect(element.children![0]!.content).toBe('John Doe');
|
|
332
343
|
});
|
|
333
344
|
|
|
334
|
-
|
|
345
|
+
it('should parse custom element with child elements and text', () => {
|
|
335
346
|
const tokens = tokenize('<card-header><h1>Title</h1><sub-title>Subtitle</sub-title></card-header>');
|
|
336
|
-
const ast =
|
|
347
|
+
const ast = parseToAST(tokens);
|
|
337
348
|
|
|
338
349
|
const cardHeader = ast.children![0]!;
|
|
339
350
|
expect(cardHeader.tagName).toBe('card-header');
|
|
@@ -343,26 +354,25 @@ describe('Custom Elements Support', () => {
|
|
|
343
354
|
expect(cardHeader.children![1]!.tagName).toBe('sub-title');
|
|
344
355
|
});
|
|
345
356
|
|
|
346
|
-
|
|
357
|
+
it('should handle unclosed custom element gracefully', () => {
|
|
347
358
|
const tokens = tokenize('<my-comp>');
|
|
348
|
-
const ast =
|
|
359
|
+
const ast = parseToAST(tokens);
|
|
349
360
|
|
|
350
361
|
const element = ast.children![0]!;
|
|
351
362
|
expect(element.tagName).toBe('my-comp');
|
|
352
363
|
});
|
|
353
364
|
|
|
354
|
-
|
|
365
|
+
it('should parse custom element with trailing slash in opening tag', () => {
|
|
355
366
|
const tokens = tokenize('<my-comp/>');
|
|
356
|
-
const ast =
|
|
367
|
+
const ast = parseToAST(tokens);
|
|
357
368
|
|
|
358
369
|
const element = ast.children![0]!;
|
|
359
370
|
expect(element.tagName).toBe('my-comp');
|
|
360
|
-
expect(element.isSelfClosing).toBe(true);
|
|
361
371
|
});
|
|
362
372
|
});
|
|
363
373
|
|
|
364
374
|
describe('Complex Real-World Scenarios', () => {
|
|
365
|
-
|
|
375
|
+
it('should parse web component with shadow DOM structure', () => {
|
|
366
376
|
const html = `
|
|
367
377
|
<user-profile>
|
|
368
378
|
<profile-header>
|
|
@@ -380,10 +390,10 @@ describe('Custom Elements Support', () => {
|
|
|
380
390
|
`;
|
|
381
391
|
|
|
382
392
|
const tokens = tokenize(html);
|
|
383
|
-
const ast =
|
|
393
|
+
const ast = parseToAST(tokens);
|
|
384
394
|
|
|
385
395
|
|
|
386
|
-
const userProfile = ast.children!.find(node => node.type === ASTNodeType.
|
|
396
|
+
const userProfile = ast.children!.find((node: any) => node.type === ASTNodeType.Element)!;
|
|
387
397
|
expect(userProfile.tagName).toBe('user-profile');
|
|
388
398
|
|
|
389
399
|
|
|
@@ -391,7 +401,7 @@ describe('Custom Elements Support', () => {
|
|
|
391
401
|
expect(userProfile.children!.length).toBeGreaterThan(0);
|
|
392
402
|
});
|
|
393
403
|
|
|
394
|
-
|
|
404
|
+
it('should parse framework-style component tree', () => {
|
|
395
405
|
const html = `
|
|
396
406
|
<app-root>
|
|
397
407
|
<app-header>
|
|
@@ -410,16 +420,16 @@ describe('Custom Elements Support', () => {
|
|
|
410
420
|
`;
|
|
411
421
|
|
|
412
422
|
const tokens = tokenize(html);
|
|
413
|
-
const ast =
|
|
423
|
+
const ast = parseToAST(tokens);
|
|
414
424
|
|
|
415
425
|
|
|
416
|
-
const appRoot = ast.children!.find(node => node.type === ASTNodeType.
|
|
426
|
+
const appRoot = ast.children!.find((node: any) => node.type === ASTNodeType.Element)!;
|
|
417
427
|
expect(appRoot.tagName).toBe('app-root');
|
|
418
428
|
});
|
|
419
429
|
|
|
420
|
-
|
|
430
|
+
it('should parse custom elements with data attributes', () => {
|
|
421
431
|
const tokens = tokenize('<my-widget data-id="123" data-type="primary" data-config=\'{"key":"value"}\'></my-widget>');
|
|
422
|
-
const ast =
|
|
432
|
+
const ast = parseToAST(tokens);
|
|
423
433
|
|
|
424
434
|
const element = ast.children![0]!;
|
|
425
435
|
expect(element.tagName).toBe('my-widget');
|
|
@@ -430,7 +440,7 @@ describe('Custom Elements Support', () => {
|
|
|
430
440
|
});
|
|
431
441
|
|
|
432
442
|
describe('Custom Element Name Validation Pattern', () => {
|
|
433
|
-
|
|
443
|
+
it('valid: starts with lowercase letter, contains hyphen', () => {
|
|
434
444
|
const validNames = [
|
|
435
445
|
'a-b',
|
|
436
446
|
'my-component',
|
|
@@ -444,13 +454,13 @@ describe('Custom Elements Support', () => {
|
|
|
444
454
|
|
|
445
455
|
validNames.forEach(name => {
|
|
446
456
|
const tokens = tokenize(`<${name}></${name}>`);
|
|
447
|
-
const ast =
|
|
457
|
+
const ast = parseToAST(tokens);
|
|
448
458
|
const element = ast.children![0]!;
|
|
449
459
|
expect(element.tagName).toBe(name);
|
|
450
460
|
});
|
|
451
461
|
});
|
|
452
462
|
|
|
453
|
-
|
|
463
|
+
it('should handle complex hyphenated names', () => {
|
|
454
464
|
const complexNames = [
|
|
455
465
|
'my-super-long-component-name',
|
|
456
466
|
'x-1-2-3-4',
|
|
@@ -460,7 +470,7 @@ describe('Custom Elements Support', () => {
|
|
|
460
470
|
|
|
461
471
|
complexNames.forEach(name => {
|
|
462
472
|
const tokens = tokenize(`<${name}></${name}>`);
|
|
463
|
-
const ast =
|
|
473
|
+
const ast = parseToAST(tokens);
|
|
464
474
|
const element = ast.children![0]!;
|
|
465
475
|
expect(element.tagName).toBe(name);
|
|
466
476
|
});
|
|
@@ -468,7 +478,7 @@ describe('Custom Elements Support', () => {
|
|
|
468
478
|
});
|
|
469
479
|
|
|
470
480
|
describe('Tokenizer-specific Tests', () => {
|
|
471
|
-
|
|
481
|
+
it('tokenizer should capture full custom element name', () => {
|
|
472
482
|
const tokens = tokenize('<my-component-123></my-component-123>');
|
|
473
483
|
|
|
474
484
|
|
|
@@ -482,7 +492,7 @@ describe('Custom Elements Support', () => {
|
|
|
482
492
|
expect(closeTag!.value).toBe('my-component-123');
|
|
483
493
|
});
|
|
484
494
|
|
|
485
|
-
|
|
495
|
+
it('tokenizer should handle custom element with attributes correctly', () => {
|
|
486
496
|
const tokens = tokenize('<my-comp class="test" id="main"></my-comp>');
|
|
487
497
|
|
|
488
498
|
const openTag = tokens.find(t => t.type === 'TAG_OPEN');
|
|
@@ -494,7 +504,7 @@ describe('Custom Elements Support', () => {
|
|
|
494
504
|
});
|
|
495
505
|
});
|
|
496
506
|
|
|
497
|
-
|
|
507
|
+
it('tokenizer should handle self-closing custom elements', () => {
|
|
498
508
|
const tokens = tokenize('<my-comp />');
|
|
499
509
|
|
|
500
510
|
const openTag = tokens.find(t => t.type === 'TAG_OPEN');
|
|
@@ -505,19 +515,19 @@ describe('Custom Elements Support', () => {
|
|
|
505
515
|
});
|
|
506
516
|
|
|
507
517
|
describe('Customized Built-in Elements (is attribute)', () => {
|
|
508
|
-
|
|
518
|
+
it('should parse button with is attribute', () => {
|
|
509
519
|
const tokens = tokenize('<button is="plastic-button">Click Me!</button>');
|
|
510
|
-
const ast =
|
|
520
|
+
const ast = parseToAST(tokens);
|
|
511
521
|
|
|
512
522
|
const button = ast.children![0]!;
|
|
513
|
-
expect(button.type).toBe(ASTNodeType.
|
|
523
|
+
expect(button.type).toBe(ASTNodeType.Element);
|
|
514
524
|
expect(button.tagName).toBe('button');
|
|
515
525
|
expect(button.attributes).toHaveProperty('is', 'plastic-button');
|
|
516
526
|
});
|
|
517
527
|
|
|
518
|
-
|
|
528
|
+
it('should parse input with is attribute', () => {
|
|
519
529
|
const tokens = tokenize('<input is="custom-input" type="text" />');
|
|
520
|
-
const ast =
|
|
530
|
+
const ast = parseToAST(tokens);
|
|
521
531
|
|
|
522
532
|
const input = ast.children![0]!;
|
|
523
533
|
expect(input.tagName).toBe('input');
|
|
@@ -525,9 +535,9 @@ describe('Custom Elements Support', () => {
|
|
|
525
535
|
expect(input.attributes).toHaveProperty('type', 'text');
|
|
526
536
|
});
|
|
527
537
|
|
|
528
|
-
|
|
538
|
+
it('should parse div with is attribute', () => {
|
|
529
539
|
const tokens = tokenize('<div is="fancy-div"></div>');
|
|
530
|
-
const ast =
|
|
540
|
+
const ast = parseToAST(tokens);
|
|
531
541
|
|
|
532
542
|
const div = ast.children![0]!;
|
|
533
543
|
expect(div.tagName).toBe('div');
|
|
@@ -536,25 +546,25 @@ describe('Custom Elements Support', () => {
|
|
|
536
546
|
});
|
|
537
547
|
|
|
538
548
|
describe('Reserved Custom Element Names', () => {
|
|
539
|
-
|
|
549
|
+
it('should parse annotation-xml (reserved SVG name)', () => {
|
|
540
550
|
const tokens = tokenize('<annotation-xml></annotation-xml>');
|
|
541
|
-
const ast =
|
|
551
|
+
const ast = parseToAST(tokens);
|
|
542
552
|
|
|
543
553
|
const element = ast.children![0]!;
|
|
544
554
|
expect(element.tagName).toBe('annotation-xml');
|
|
545
555
|
});
|
|
546
556
|
|
|
547
|
-
|
|
557
|
+
it('should parse font-face (reserved SVG name)', () => {
|
|
548
558
|
const tokens = tokenize('<font-face></font-face>');
|
|
549
|
-
const ast =
|
|
559
|
+
const ast = parseToAST(tokens);
|
|
550
560
|
|
|
551
561
|
const element = ast.children![0]!;
|
|
552
562
|
expect(element.tagName).toBe('font-face');
|
|
553
563
|
});
|
|
554
564
|
|
|
555
|
-
|
|
565
|
+
it('should parse color-profile (reserved SVG name)', () => {
|
|
556
566
|
const tokens = tokenize('<color-profile></color-profile>');
|
|
557
|
-
const ast =
|
|
567
|
+
const ast = parseToAST(tokens);
|
|
558
568
|
|
|
559
569
|
const element = ast.children![0]!;
|
|
560
570
|
expect(element.tagName).toBe('color-profile');
|
|
@@ -562,33 +572,33 @@ describe('Custom Elements Support', () => {
|
|
|
562
572
|
});
|
|
563
573
|
|
|
564
574
|
describe('Unicode Custom Element Names', () => {
|
|
565
|
-
|
|
575
|
+
it('should parse custom element with Greek letters', () => {
|
|
566
576
|
const tokens = tokenize('<math-α></math-α>');
|
|
567
|
-
const ast =
|
|
577
|
+
const ast = parseToAST(tokens);
|
|
568
578
|
|
|
569
579
|
const element = ast.children![0]!;
|
|
570
580
|
expect(element.tagName).toBe('math-α');
|
|
571
581
|
});
|
|
572
582
|
|
|
573
|
-
|
|
583
|
+
it('should parse custom element with emoji', () => {
|
|
574
584
|
const tokens = tokenize('<emotion-😍></emotion-😍>');
|
|
575
|
-
const ast =
|
|
585
|
+
const ast = parseToAST(tokens);
|
|
576
586
|
|
|
577
587
|
const element = ast.children![0]!;
|
|
578
588
|
expect(element.tagName).toBe('emotion-😍');
|
|
579
589
|
});
|
|
580
590
|
|
|
581
|
-
|
|
591
|
+
it('should parse custom element with Chinese characters', () => {
|
|
582
592
|
const tokens = tokenize('<my-元素></my-元素>');
|
|
583
|
-
const ast =
|
|
593
|
+
const ast = parseToAST(tokens);
|
|
584
594
|
|
|
585
595
|
const element = ast.children![0]!;
|
|
586
596
|
expect(element.tagName).toBe('my-元素');
|
|
587
597
|
});
|
|
588
598
|
|
|
589
|
-
|
|
599
|
+
it('should parse custom element with Arabic characters', () => {
|
|
590
600
|
const tokens = tokenize('<my-عنصر></my-عنصر>');
|
|
591
|
-
const ast =
|
|
601
|
+
const ast = parseToAST(tokens);
|
|
592
602
|
|
|
593
603
|
const element = ast.children![0]!;
|
|
594
604
|
expect(element.tagName).toBe('my-عنصر');
|
|
@@ -596,32 +606,32 @@ describe('Custom Elements Support', () => {
|
|
|
596
606
|
});
|
|
597
607
|
|
|
598
608
|
describe('Extreme Edge Cases', () => {
|
|
599
|
-
|
|
609
|
+
it('should parse very long custom element name', () => {
|
|
600
610
|
const longName = 'my-super-duper-extra-long-custom-component-name-that-keeps-going-and-going';
|
|
601
611
|
const tokens = tokenize(`<${longName}></${longName}>`);
|
|
602
|
-
const ast =
|
|
612
|
+
const ast = parseToAST(tokens);
|
|
603
613
|
|
|
604
614
|
const element = ast.children![0]!;
|
|
605
615
|
expect(element.tagName).toBe(longName);
|
|
606
616
|
});
|
|
607
617
|
|
|
608
|
-
|
|
618
|
+
it('should parse custom element with many consecutive hyphens', () => {
|
|
609
619
|
const tokens = tokenize('<my---component></my---component>');
|
|
610
|
-
const ast =
|
|
620
|
+
const ast = parseToAST(tokens);
|
|
611
621
|
|
|
612
622
|
const element = ast.children![0]!;
|
|
613
623
|
expect(element.tagName).toBe('my---component');
|
|
614
624
|
});
|
|
615
625
|
|
|
616
|
-
|
|
626
|
+
it('should parse custom element starting with x-', () => {
|
|
617
627
|
const tokens = tokenize('<x-></x->');
|
|
618
|
-
const ast =
|
|
628
|
+
const ast = parseToAST(tokens);
|
|
619
629
|
|
|
620
630
|
const element = ast.children![0]!;
|
|
621
631
|
expect(element.tagName).toBe('x-');
|
|
622
632
|
});
|
|
623
633
|
|
|
624
|
-
|
|
634
|
+
it('should handle custom element with newlines in attributes', () => {
|
|
625
635
|
const html = `<my-comp
|
|
626
636
|
class="test"
|
|
627
637
|
id="main"
|
|
@@ -629,7 +639,7 @@ describe('Custom Elements Support', () => {
|
|
|
629
639
|
></my-comp>`;
|
|
630
640
|
|
|
631
641
|
const tokens = tokenize(html);
|
|
632
|
-
const ast =
|
|
642
|
+
const ast = parseToAST(tokens);
|
|
633
643
|
|
|
634
644
|
const element = ast.children![0]!;
|
|
635
645
|
expect(element.tagName).toBe('my-comp');
|
|
@@ -637,19 +647,19 @@ describe('Custom Elements Support', () => {
|
|
|
637
647
|
expect(element.attributes).toHaveProperty('id', 'main');
|
|
638
648
|
});
|
|
639
649
|
|
|
640
|
-
|
|
650
|
+
it('should parse custom element mixed with comments', () => {
|
|
641
651
|
const html = '<!-- comment --><my-comp>text</my-comp><!-- another comment -->';
|
|
642
652
|
const tokens = tokenize(html);
|
|
643
|
-
const ast =
|
|
653
|
+
const ast = parseToAST(tokens);
|
|
644
654
|
|
|
645
655
|
|
|
646
|
-
const myComp = ast.children!.find(node => node.type === ASTNodeType.
|
|
656
|
+
const myComp = ast.children!.find((node: any) => node.type === ASTNodeType.Element)!;
|
|
647
657
|
expect(myComp.tagName).toBe('my-comp');
|
|
648
658
|
});
|
|
649
659
|
|
|
650
|
-
|
|
660
|
+
it('should parse empty custom element', () => {
|
|
651
661
|
const tokens = tokenize('<my-comp></my-comp>');
|
|
652
|
-
const ast =
|
|
662
|
+
const ast = parseToAST(tokens);
|
|
653
663
|
|
|
654
664
|
const element = ast.children![0]!;
|
|
655
665
|
expect(element.tagName).toBe('my-comp');
|
|
@@ -657,9 +667,9 @@ describe('Custom Elements Support', () => {
|
|
|
657
667
|
expect(element.children!.length).toBe(0);
|
|
658
668
|
});
|
|
659
669
|
|
|
660
|
-
|
|
670
|
+
it('should parse custom element with only whitespace content', () => {
|
|
661
671
|
const tokens = tokenize('<my-comp> \n\t </my-comp>');
|
|
662
|
-
const ast =
|
|
672
|
+
const ast = parseToAST(tokens);
|
|
663
673
|
|
|
664
674
|
const element = ast.children![0]!;
|
|
665
675
|
expect(element.tagName).toBe('my-comp');
|
|
@@ -669,25 +679,25 @@ describe('Custom Elements Support', () => {
|
|
|
669
679
|
});
|
|
670
680
|
|
|
671
681
|
describe('Malformed Custom Elements', () => {
|
|
672
|
-
|
|
682
|
+
it('should handle mismatched closing tag', () => {
|
|
673
683
|
const tokens = tokenize('<my-comp></my-other>');
|
|
674
|
-
const ast =
|
|
684
|
+
const ast = parseToAST(tokens);
|
|
675
685
|
|
|
676
686
|
const element = ast.children![0]!;
|
|
677
687
|
expect(element.tagName).toBe('my-comp');
|
|
678
688
|
});
|
|
679
689
|
|
|
680
|
-
|
|
690
|
+
it('should handle multiple unclosed custom elements', () => {
|
|
681
691
|
const tokens = tokenize('<my-comp><nested-comp><deep-comp>');
|
|
682
|
-
const ast =
|
|
692
|
+
const ast = parseToAST(tokens);
|
|
683
693
|
|
|
684
694
|
const element = ast.children![0]!;
|
|
685
695
|
expect(element.tagName).toBe('my-comp');
|
|
686
696
|
});
|
|
687
697
|
|
|
688
|
-
|
|
698
|
+
it('should handle custom element with malformed attributes', () => {
|
|
689
699
|
const tokens = tokenize('<my-comp attr-without-value attr="value"></my-comp>');
|
|
690
|
-
const ast =
|
|
700
|
+
const ast = parseToAST(tokens);
|
|
691
701
|
|
|
692
702
|
const element = ast.children![0]!;
|
|
693
703
|
expect(element.tagName).toBe('my-comp');
|
|
@@ -696,26 +706,26 @@ describe('Custom Elements Support', () => {
|
|
|
696
706
|
});
|
|
697
707
|
|
|
698
708
|
describe('Custom Elements in Special Contexts', () => {
|
|
699
|
-
|
|
709
|
+
it('should parse custom element inside table', () => {
|
|
700
710
|
const tokens = tokenize('<table><tr><td><my-cell>content</my-cell></td></tr></table>');
|
|
701
|
-
const ast =
|
|
711
|
+
const ast = parseToAST(tokens);
|
|
702
712
|
|
|
703
713
|
|
|
704
714
|
const table = ast.children![0]!;
|
|
705
715
|
expect(table.tagName).toBe('table');
|
|
706
716
|
});
|
|
707
717
|
|
|
708
|
-
|
|
718
|
+
it('should parse custom element inside list', () => {
|
|
709
719
|
const tokens = tokenize('<ul><li><list-item></list-item></li></ul>');
|
|
710
|
-
const ast =
|
|
720
|
+
const ast = parseToAST(tokens);
|
|
711
721
|
|
|
712
722
|
const ul = ast.children![0]!;
|
|
713
723
|
expect(ul.tagName).toBe('ul');
|
|
714
724
|
});
|
|
715
725
|
|
|
716
|
-
|
|
726
|
+
it('should parse custom element inside form', () => {
|
|
717
727
|
const tokens = tokenize('<form><form-field name="test"></form-field></form>');
|
|
718
|
-
const ast =
|
|
728
|
+
const ast = parseToAST(tokens);
|
|
719
729
|
|
|
720
730
|
const form = ast.children![0]!;
|
|
721
731
|
expect(form.tagName).toBe('form');
|
|
@@ -723,9 +733,9 @@ describe('Custom Elements Support', () => {
|
|
|
723
733
|
});
|
|
724
734
|
|
|
725
735
|
describe('ARIA and Accessibility', () => {
|
|
726
|
-
|
|
736
|
+
it('should parse custom element with ARIA attributes', () => {
|
|
727
737
|
const tokens = tokenize('<my-button role="button" aria-label="Click me" aria-disabled="true"></my-button>');
|
|
728
|
-
const ast =
|
|
738
|
+
const ast = parseToAST(tokens);
|
|
729
739
|
|
|
730
740
|
const element = ast.children![0]!;
|
|
731
741
|
expect(element.tagName).toBe('my-button');
|
|
@@ -734,9 +744,9 @@ describe('Custom Elements Support', () => {
|
|
|
734
744
|
expect(element.attributes).toHaveProperty('aria-disabled', 'true');
|
|
735
745
|
});
|
|
736
746
|
|
|
737
|
-
|
|
747
|
+
it('should parse custom element with tabindex', () => {
|
|
738
748
|
const tokens = tokenize('<my-comp tabindex="0"></my-comp>');
|
|
739
|
-
const ast =
|
|
749
|
+
const ast = parseToAST(tokens);
|
|
740
750
|
|
|
741
751
|
const element = ast.children![0]!;
|
|
742
752
|
expect(element.attributes).toHaveProperty('tabindex', '0');
|