Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/base/test/browser/dom.test.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import assert from 'assert';
7
import { $, h, trackAttributes, copyAttributes, disposableWindowInterval, getWindows, getWindowsCount, getWindowId, getWindowById, hasWindow, getWindow, getDocument, isHTMLElement, SafeTriangle } from '../../browser/dom.js';
8
import { asCssValueWithDefault } from '../../../base/browser/cssValue.js';
9
import { ensureCodeWindow, isAuxiliaryWindow, mainWindow } from '../../browser/window.js';
10
import { DeferredPromise, timeout } from '../../common/async.js';
11
import { runWithFakedTimers } from '../common/timeTravelScheduler.js';
12
import { ensureNoDisposablesAreLeakedInTestSuite } from '../common/utils.js';
13
14
suite('dom', () => {
15
test('hasClass', () => {
16
17
const element = document.createElement('div');
18
element.className = 'foobar boo far';
19
20
assert(element.classList.contains('foobar'));
21
assert(element.classList.contains('boo'));
22
assert(element.classList.contains('far'));
23
assert(!element.classList.contains('bar'));
24
assert(!element.classList.contains('foo'));
25
assert(!element.classList.contains(''));
26
});
27
28
test('removeClass', () => {
29
30
let element = document.createElement('div');
31
element.className = 'foobar boo far';
32
33
element.classList.remove('boo');
34
assert(element.classList.contains('far'));
35
assert(!element.classList.contains('boo'));
36
assert(element.classList.contains('foobar'));
37
assert.strictEqual(element.className, 'foobar far');
38
39
element = document.createElement('div');
40
element.className = 'foobar boo far';
41
42
element.classList.remove('far');
43
assert(!element.classList.contains('far'));
44
assert(element.classList.contains('boo'));
45
assert(element.classList.contains('foobar'));
46
assert.strictEqual(element.className, 'foobar boo');
47
48
element.classList.remove('boo');
49
assert(!element.classList.contains('far'));
50
assert(!element.classList.contains('boo'));
51
assert(element.classList.contains('foobar'));
52
assert.strictEqual(element.className, 'foobar');
53
54
element.classList.remove('foobar');
55
assert(!element.classList.contains('far'));
56
assert(!element.classList.contains('boo'));
57
assert(!element.classList.contains('foobar'));
58
assert.strictEqual(element.className, '');
59
});
60
61
test('removeClass should consider hyphens', function () {
62
const element = document.createElement('div');
63
64
element.classList.add('foo-bar');
65
element.classList.add('bar');
66
67
assert(element.classList.contains('foo-bar'));
68
assert(element.classList.contains('bar'));
69
70
element.classList.remove('bar');
71
assert(element.classList.contains('foo-bar'));
72
assert(!element.classList.contains('bar'));
73
74
element.classList.remove('foo-bar');
75
assert(!element.classList.contains('foo-bar'));
76
assert(!element.classList.contains('bar'));
77
});
78
79
suite('$', () => {
80
test('should build simple nodes', () => {
81
const div = $('div');
82
assert(div);
83
assert(isHTMLElement(div));
84
assert.strictEqual(div.tagName, 'DIV');
85
assert(!div.firstChild);
86
});
87
88
test('should build nodes with id', () => {
89
const div = $('div#foo');
90
assert(div);
91
assert(isHTMLElement(div));
92
assert.strictEqual(div.tagName, 'DIV');
93
assert.strictEqual(div.id, 'foo');
94
});
95
96
test('should build nodes with class-name', () => {
97
const div = $('div.foo');
98
assert(div);
99
assert(isHTMLElement(div));
100
assert.strictEqual(div.tagName, 'DIV');
101
assert.strictEqual(div.className, 'foo');
102
});
103
104
test('should build nodes with attributes', () => {
105
let div = $('div', { class: 'test' });
106
assert.strictEqual(div.className, 'test');
107
108
div = $('div', undefined);
109
assert.strictEqual(div.className, '');
110
});
111
112
test('should build nodes with children', () => {
113
let div = $('div', undefined, $('span', { id: 'demospan' }));
114
const firstChild = div.firstChild as HTMLElement;
115
assert.strictEqual(firstChild.tagName, 'SPAN');
116
assert.strictEqual(firstChild.id, 'demospan');
117
118
div = $('div', undefined, 'hello');
119
120
assert.strictEqual(div.firstChild && div.firstChild.textContent, 'hello');
121
});
122
123
test('should build nodes with text children', () => {
124
const div = $('div', undefined, 'foobar');
125
const firstChild = div.firstChild as HTMLElement;
126
assert.strictEqual(firstChild.tagName, undefined);
127
assert.strictEqual(firstChild.textContent, 'foobar');
128
});
129
});
130
131
suite('h', () => {
132
test('should build simple nodes', () => {
133
const div = h('div');
134
assert(isHTMLElement(div.root));
135
assert.strictEqual(div.root.tagName, 'DIV');
136
137
const span = h('span');
138
assert(isHTMLElement(span.root));
139
assert.strictEqual(span.root.tagName, 'SPAN');
140
141
const img = h('img');
142
assert(isHTMLElement(img.root));
143
assert.strictEqual(img.root.tagName, 'IMG');
144
});
145
146
test('should handle ids and classes', () => {
147
const divId = h('div#myid');
148
assert.strictEqual(divId.root.tagName, 'DIV');
149
assert.strictEqual(divId.root.id, 'myid');
150
151
const divClass = h('div.a');
152
assert.strictEqual(divClass.root.tagName, 'DIV');
153
assert.strictEqual(divClass.root.classList.length, 1);
154
assert(divClass.root.classList.contains('a'));
155
156
const divClasses = h('div.a.b.c');
157
assert.strictEqual(divClasses.root.tagName, 'DIV');
158
assert.strictEqual(divClasses.root.classList.length, 3);
159
assert(divClasses.root.classList.contains('a'));
160
assert(divClasses.root.classList.contains('b'));
161
assert(divClasses.root.classList.contains('c'));
162
163
const divAll = h('div#myid.a.b.c');
164
assert.strictEqual(divAll.root.tagName, 'DIV');
165
assert.strictEqual(divAll.root.id, 'myid');
166
assert.strictEqual(divAll.root.classList.length, 3);
167
assert(divAll.root.classList.contains('a'));
168
assert(divAll.root.classList.contains('b'));
169
assert(divAll.root.classList.contains('c'));
170
171
const spanId = h('span#myid');
172
assert.strictEqual(spanId.root.tagName, 'SPAN');
173
assert.strictEqual(spanId.root.id, 'myid');
174
175
const spanClass = h('span.a');
176
assert.strictEqual(spanClass.root.tagName, 'SPAN');
177
assert.strictEqual(spanClass.root.classList.length, 1);
178
assert(spanClass.root.classList.contains('a'));
179
180
const spanClasses = h('span.a.b.c');
181
assert.strictEqual(spanClasses.root.tagName, 'SPAN');
182
assert.strictEqual(spanClasses.root.classList.length, 3);
183
assert(spanClasses.root.classList.contains('a'));
184
assert(spanClasses.root.classList.contains('b'));
185
assert(spanClasses.root.classList.contains('c'));
186
187
const spanAll = h('span#myid.a.b.c');
188
assert.strictEqual(spanAll.root.tagName, 'SPAN');
189
assert.strictEqual(spanAll.root.id, 'myid');
190
assert.strictEqual(spanAll.root.classList.length, 3);
191
assert(spanAll.root.classList.contains('a'));
192
assert(spanAll.root.classList.contains('b'));
193
assert(spanAll.root.classList.contains('c'));
194
});
195
196
test('should implicitly handle ids and classes', () => {
197
const divId = h('#myid');
198
assert.strictEqual(divId.root.tagName, 'DIV');
199
assert.strictEqual(divId.root.id, 'myid');
200
201
const divClass = h('.a');
202
assert.strictEqual(divClass.root.tagName, 'DIV');
203
assert.strictEqual(divClass.root.classList.length, 1);
204
assert(divClass.root.classList.contains('a'));
205
206
const divClasses = h('.a.b.c');
207
assert.strictEqual(divClasses.root.tagName, 'DIV');
208
assert.strictEqual(divClasses.root.classList.length, 3);
209
assert(divClasses.root.classList.contains('a'));
210
assert(divClasses.root.classList.contains('b'));
211
assert(divClasses.root.classList.contains('c'));
212
213
const divAll = h('#myid.a.b.c');
214
assert.strictEqual(divAll.root.tagName, 'DIV');
215
assert.strictEqual(divAll.root.id, 'myid');
216
assert.strictEqual(divAll.root.classList.length, 3);
217
assert(divAll.root.classList.contains('a'));
218
assert(divAll.root.classList.contains('b'));
219
assert(divAll.root.classList.contains('c'));
220
});
221
222
test('should handle @ identifiers', () => {
223
const implicit = h('@el');
224
assert.strictEqual(implicit.root, implicit.el);
225
assert.strictEqual(implicit.el.tagName, 'DIV');
226
227
const explicit = h('div@el');
228
assert.strictEqual(explicit.root, explicit.el);
229
assert.strictEqual(explicit.el.tagName, 'DIV');
230
231
const implicitId = h('#myid@el');
232
assert.strictEqual(implicitId.root, implicitId.el);
233
assert.strictEqual(implicitId.el.tagName, 'DIV');
234
assert.strictEqual(implicitId.root.id, 'myid');
235
236
const explicitId = h('div#myid@el');
237
assert.strictEqual(explicitId.root, explicitId.el);
238
assert.strictEqual(explicitId.el.tagName, 'DIV');
239
assert.strictEqual(explicitId.root.id, 'myid');
240
241
const implicitClass = h('.a@el');
242
assert.strictEqual(implicitClass.root, implicitClass.el);
243
assert.strictEqual(implicitClass.el.tagName, 'DIV');
244
assert.strictEqual(implicitClass.root.classList.length, 1);
245
assert(implicitClass.root.classList.contains('a'));
246
247
const explicitClass = h('div.a@el');
248
assert.strictEqual(explicitClass.root, explicitClass.el);
249
assert.strictEqual(explicitClass.el.tagName, 'DIV');
250
assert.strictEqual(explicitClass.root.classList.length, 1);
251
assert(explicitClass.root.classList.contains('a'));
252
});
253
});
254
255
test('should recurse', () => {
256
const result = h('div.code-view', [
257
h('div.title@title'),
258
h('div.container', [
259
h('div.gutter@gutterDiv'),
260
h('span@editor'),
261
]),
262
]);
263
264
assert.strictEqual(result.root.tagName, 'DIV');
265
assert.strictEqual(result.root.className, 'code-view');
266
assert.strictEqual(result.root.childElementCount, 2);
267
assert.strictEqual(result.root.firstElementChild, result.title);
268
assert.strictEqual(result.title.tagName, 'DIV');
269
assert.strictEqual(result.title.className, 'title');
270
assert.strictEqual(result.title.childElementCount, 0);
271
assert.strictEqual(result.gutterDiv.tagName, 'DIV');
272
assert.strictEqual(result.gutterDiv.className, 'gutter');
273
assert.strictEqual(result.gutterDiv.childElementCount, 0);
274
assert.strictEqual(result.editor.tagName, 'SPAN');
275
assert.strictEqual(result.editor.className, '');
276
assert.strictEqual(result.editor.childElementCount, 0);
277
});
278
279
test('cssValueWithDefault', () => {
280
assert.strictEqual(asCssValueWithDefault('red', 'blue'), 'red');
281
assert.strictEqual(asCssValueWithDefault(undefined, 'blue'), 'blue');
282
assert.strictEqual(asCssValueWithDefault('var(--my-var)', 'blue'), 'var(--my-var, blue)');
283
assert.strictEqual(asCssValueWithDefault('var(--my-var, red)', 'blue'), 'var(--my-var, red)');
284
assert.strictEqual(asCssValueWithDefault('var(--my-var, var(--my-var2))', 'blue'), 'var(--my-var, var(--my-var2, blue))');
285
});
286
287
test('copyAttributes', () => {
288
const elementSource = document.createElement('div');
289
elementSource.setAttribute('foo', 'bar');
290
elementSource.setAttribute('bar', 'foo');
291
292
const elementTarget = document.createElement('div');
293
copyAttributes(elementSource, elementTarget);
294
295
assert.strictEqual(elementTarget.getAttribute('foo'), 'bar');
296
assert.strictEqual(elementTarget.getAttribute('bar'), 'foo');
297
});
298
299
test('trackAttributes (unfiltered)', async () => {
300
return runWithFakedTimers({ useFakeTimers: true }, async () => {
301
const elementSource = document.createElement('div');
302
const elementTarget = document.createElement('div');
303
304
const disposable = trackAttributes(elementSource, elementTarget);
305
306
elementSource.setAttribute('foo', 'bar');
307
elementSource.setAttribute('bar', 'foo');
308
309
await timeout(1);
310
311
assert.strictEqual(elementTarget.getAttribute('foo'), 'bar');
312
assert.strictEqual(elementTarget.getAttribute('bar'), 'foo');
313
314
disposable.dispose();
315
});
316
});
317
318
test('trackAttributes (filtered)', async () => {
319
return runWithFakedTimers({ useFakeTimers: true }, async () => {
320
const elementSource = document.createElement('div');
321
const elementTarget = document.createElement('div');
322
323
const disposable = trackAttributes(elementSource, elementTarget, ['foo']);
324
325
elementSource.setAttribute('foo', 'bar');
326
elementSource.setAttribute('bar', 'foo');
327
328
await timeout(1);
329
330
assert.strictEqual(elementTarget.getAttribute('foo'), 'bar');
331
assert.strictEqual(elementTarget.getAttribute('bar'), null);
332
333
disposable.dispose();
334
});
335
});
336
337
test('window utilities', () => {
338
const windows = Array.from(getWindows());
339
assert.strictEqual(windows.length, 1);
340
assert.strictEqual(getWindowsCount(), 1);
341
const windowId = getWindowId(mainWindow);
342
assert.ok(typeof windowId === 'number');
343
assert.strictEqual(getWindowById(windowId)?.window, mainWindow);
344
assert.strictEqual(getWindowById(undefined, true).window, mainWindow);
345
assert.strictEqual(hasWindow(windowId), true);
346
assert.strictEqual(isAuxiliaryWindow(mainWindow), false);
347
ensureCodeWindow(mainWindow, 1);
348
assert.ok(typeof mainWindow.vscodeWindowId === 'number');
349
350
const div = document.createElement('div');
351
assert.strictEqual(getWindow(div), mainWindow);
352
assert.strictEqual(getDocument(div), mainWindow.document);
353
354
const event = document.createEvent('MouseEvent');
355
assert.strictEqual(getWindow(event), mainWindow);
356
assert.strictEqual(getDocument(event), mainWindow.document);
357
});
358
359
suite('disposableWindowInterval', () => {
360
test('basics', async () => {
361
let count = 0;
362
const promise = new DeferredPromise<void>();
363
const interval = disposableWindowInterval(mainWindow, () => {
364
count++;
365
if (count === 3) {
366
promise.complete(undefined);
367
return true;
368
} else {
369
return false;
370
}
371
}, 0, 10);
372
373
await promise.p;
374
assert.strictEqual(count, 3);
375
interval.dispose();
376
});
377
378
test('iterations', async () => {
379
let count = 0;
380
const interval = disposableWindowInterval(mainWindow, () => {
381
count++;
382
383
return false;
384
}, 0, 0);
385
386
await timeout(5);
387
assert.strictEqual(count, 0);
388
interval.dispose();
389
});
390
391
test('dispose', async () => {
392
let count = 0;
393
const interval = disposableWindowInterval(mainWindow, () => {
394
count++;
395
396
return false;
397
}, 0, 10);
398
399
interval.dispose();
400
await timeout(5);
401
assert.strictEqual(count, 0);
402
});
403
});
404
405
suite('SafeTriangle', () => {
406
const fakeElement = (left: number, right: number, top: number, bottom: number): HTMLElement => {
407
return { getBoundingClientRect: () => ({ left, right, top, bottom }) } as any;
408
};
409
410
test('works', () => {
411
const safeTriangle = new SafeTriangle(0, 0, fakeElement(10, 20, 10, 20));
412
413
assert.strictEqual(safeTriangle.contains(5, 5), true); // in triangle region
414
assert.strictEqual(safeTriangle.contains(15, 5), false);
415
assert.strictEqual(safeTriangle.contains(25, 5), false);
416
417
assert.strictEqual(safeTriangle.contains(5, 15), false);
418
assert.strictEqual(safeTriangle.contains(15, 15), true);
419
assert.strictEqual(safeTriangle.contains(25, 15), false);
420
421
assert.strictEqual(safeTriangle.contains(5, 25), false);
422
assert.strictEqual(safeTriangle.contains(15, 25), false);
423
assert.strictEqual(safeTriangle.contains(25, 25), false);
424
});
425
426
test('other dirations', () => {
427
const a = new SafeTriangle(30, 30, fakeElement(10, 20, 10, 20));
428
assert.strictEqual(a.contains(25, 25), true);
429
430
const b = new SafeTriangle(0, 30, fakeElement(10, 20, 10, 20));
431
assert.strictEqual(b.contains(5, 25), true);
432
433
const c = new SafeTriangle(30, 0, fakeElement(10, 20, 10, 20));
434
assert.strictEqual(c.contains(25, 5), true);
435
});
436
});
437
438
ensureNoDisposablesAreLeakedInTestSuite();
439
});
440
441