1import * as assert from 'node:assert';
2import { TAG_NAMES as $, NS, getTagID } from '../common/html.js';
3import { type TagToken, TokenType } from '../common/token.js';
4import { FormattingElementList, EntryType } from './formatting-element-list.js';
5import { generateTestsForEachTreeAdapter } from 'parse5-test-utils/utils/common.js';
6
7function createToken(name: $): TagToken {
8    return {
9        type: TokenType.START_TAG,
10        tagName: name,
11        tagID: getTagID(name),
12        ackSelfClosing: false,
13        selfClosing: false,
14        attrs: [],
15        location: null,
16    };
17}
18
19generateTestsForEachTreeAdapter('FormattingElementList', (treeAdapter) => {
20    test('Insert marker', () => {
21        const list = new FormattingElementList(treeAdapter);
22
23        list.insertMarker();
24        assert.strictEqual(list.entries.length, 1);
25        assert.strictEqual(list.entries[0].type, EntryType.Marker);
26
27        list.insertMarker();
28        assert.strictEqual(list.entries.length, 2);
29        assert.strictEqual(list.entries[0].type, EntryType.Marker);
30    });
31
32    test('Push element', () => {
33        const list = new FormattingElementList(treeAdapter);
34        const element1Token = createToken($.DIV);
35        const element2Token = createToken($.P);
36        const element1 = treeAdapter.createElement($.DIV, NS.HTML, []);
37        const element2 = treeAdapter.createElement($.P, NS.HTML, []);
38
39        list.pushElement(element1, element1Token);
40        assert.strictEqual(list.entries.length, 1);
41        assert.strictEqual(list.entries[0].type, EntryType.Element as const);
42        assert.strictEqual(list.entries[0].element, element1);
43        assert.strictEqual(list.entries[0].token, element1Token);
44
45        list.pushElement(element2, element2Token);
46        assert.strictEqual(list.entries.length, 2);
47        assert.strictEqual(list.entries[0].type, EntryType.Element);
48        assert.strictEqual(list.entries[0].element, element2);
49        assert.strictEqual(list.entries[0].token, element2Token);
50    });
51
52    test('Insert element after bookmark', () => {
53        const list = new FormattingElementList(treeAdapter);
54        const element1 = treeAdapter.createElement($.DIV, NS.HTML, []);
55        const element2 = treeAdapter.createElement($.P, NS.HTML, []);
56        const element3 = treeAdapter.createElement($.SPAN, NS.HTML, []);
57        const element4 = treeAdapter.createElement($.TITLE, NS.HTML, []);
58
59        list.pushElement(element1, createToken($.DIV));
60        list.bookmark = list.entries[0];
61
62        list.pushElement(element2, createToken($.P));
63        list.pushElement(element3, createToken($.SPAN));
64
65        list.insertElementAfterBookmark(element4, createToken($.TITLE));
66
67        assert.strictEqual(list.entries.length, 4);
68        expect(list.entries[2]).toHaveProperty('element', element4);
69    });
70
71    test('Push element - Noah Ark condition', () => {
72        const list = new FormattingElementList(treeAdapter);
73        const token1 = createToken($.DIV);
74        const token2 = createToken($.DIV);
75        const token3 = createToken($.DIV);
76        const token4 = createToken($.DIV);
77        const token5 = createToken($.DIV);
78        const token6 = createToken($.DIV);
79
80        const element1 = treeAdapter.createElement($.DIV, NS.HTML, [
81            { name: 'attr1', value: 'val1' },
82            { name: 'attr2', value: 'val2' },
83        ]);
84
85        const element2 = treeAdapter.createElement($.DIV, NS.HTML, [
86            { name: 'attr1', value: 'val1' },
87            { name: 'attr2', value: 'someOtherValue' },
88        ]);
89
90        list.pushElement(element1, token1);
91        list.pushElement(element1, token2);
92        list.pushElement(element2, token3);
93        list.pushElement(element1, token4);
94
95        assert.strictEqual(list.entries.length, 4);
96        expect(list.entries[3]).toHaveProperty('token', token1);
97        expect(list.entries[2]).toHaveProperty('token', token2);
98        expect(list.entries[1]).toHaveProperty('token', token3);
99        expect(list.entries[0]).toHaveProperty('token', token4);
100
101        list.pushElement(element1, token5);
102
103        assert.strictEqual(list.entries.length, 4);
104        expect(list.entries[3]).toHaveProperty('token', token2);
105        expect(list.entries[2]).toHaveProperty('token', token3);
106        expect(list.entries[1]).toHaveProperty('token', token4);
107        expect(list.entries[0]).toHaveProperty('token', token5);
108
109        list.insertMarker();
110        list.pushElement(element1, token6);
111
112        assert.strictEqual(list.entries.length, 6);
113        expect(list.entries[5]).toHaveProperty('token', token2);
114        expect(list.entries[4]).toHaveProperty('token', token3);
115        expect(list.entries[3]).toHaveProperty('token', token4);
116        expect(list.entries[2]).toHaveProperty('token', token5);
117        expect(list.entries[1]).toHaveProperty('type', EntryType.Marker);
118        expect(list.entries[0]).toHaveProperty('token', token6);
119    });
120
121    test('Clear to the last marker', () => {
122        const list = new FormattingElementList(treeAdapter);
123        const token = createToken($.DIV);
124
125        const element1 = treeAdapter.createElement($.DIV, NS.HTML, [
126            { name: 'attr1', value: 'val1' },
127            { name: 'attr2', value: 'val2' },
128        ]);
129
130        const element2 = treeAdapter.createElement($.DIV, NS.HTML, [
131            { name: 'attr1', value: 'val1' },
132            { name: 'attr2', value: 'someOtherValue' },
133        ]);
134
135        list.pushElement(element1, token);
136        list.pushElement(element2, token);
137        list.insertMarker();
138        list.pushElement(element1, token);
139        list.pushElement(element1, token);
140        list.pushElement(element2, token);
141
142        list.clearToLastMarker();
143
144        assert.strictEqual(list.entries.length, 2);
145
146        list.clearToLastMarker();
147
148        assert.strictEqual(list.entries.length, 0);
149    });
150
151    test('Remove entry', () => {
152        const list = new FormattingElementList(treeAdapter);
153        const token = createToken($.DIV);
154
155        const element1 = treeAdapter.createElement($.DIV, NS.HTML, [
156            { name: 'attr1', value: 'val1' },
157            { name: 'attr2', value: 'val2' },
158        ]);
159
160        const element2 = treeAdapter.createElement($.DIV, NS.HTML, [
161            { name: 'attr1', value: 'val1' },
162            { name: 'attr2', value: 'someOtherValue' },
163        ]);
164
165        list.pushElement(element1, token);
166        list.pushElement(element2, token);
167        list.pushElement(element2, token);
168
169        list.removeEntry(list.entries[2]);
170
171        assert.strictEqual(list.entries.length, 2);
172
173        for (let i = 0; i < list.entries.length; i++) {
174            expect(list.entries[i]).not.toHaveProperty('element', element1);
175        }
176    });
177
178    test('Get entry in scope with given tag name', () => {
179        const list = new FormattingElementList(treeAdapter);
180        const token = createToken($.DIV);
181        const element = treeAdapter.createElement($.DIV, NS.HTML, []);
182
183        assert.ok(!list.getElementEntryInScopeWithTagName($.DIV));
184
185        list.pushElement(element, token);
186        list.pushElement(element, token);
187        assert.strictEqual(list.getElementEntryInScopeWithTagName($.DIV), list.entries[0]);
188
189        list.insertMarker();
190        assert.ok(!list.getElementEntryInScopeWithTagName($.DIV));
191
192        list.pushElement(element, token);
193        assert.strictEqual(list.getElementEntryInScopeWithTagName($.DIV), list.entries[0]);
194    });
195
196    test('Get element entry', () => {
197        const list = new FormattingElementList(treeAdapter);
198        const token = createToken($.DIV);
199        const element1 = treeAdapter.createElement($.DIV, NS.HTML, []);
200        const element2 = treeAdapter.createElement($.A, NS.HTML, []);
201
202        list.pushElement(element2, token);
203        list.pushElement(element1, token);
204        list.pushElement(element2, token);
205        list.insertMarker();
206
207        const entry = list.getElementEntry(element1);
208
209        assert.ok(entry);
210        assert.strictEqual(entry.type, EntryType.Element);
211        assert.strictEqual(entry.token, token);
212        assert.strictEqual(entry.element, element1);
213    });
214});
215