Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts
5241 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 { isHTMLSpanElement } from '../../../../../base/browser/dom.js';
8
import { Color, RGBA } from '../../../../../base/common/color.js';
9
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
10
import { generateUuid } from '../../../../../base/common/uuid.js';
11
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
12
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
13
import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';
14
import { registerColors } from '../../../terminal/common/terminalColorRegistry.js';
15
import { appendStylizedStringToContainer, calcANSI8bitColor, handleANSIOutput } from '../../browser/debugANSIHandling.js';
16
import { DebugSession } from '../../browser/debugSession.js';
17
import { DebugLinkHoverBehavior, LinkDetector } from '../../browser/linkDetector.js';
18
import { DebugModel } from '../../common/debugModel.js';
19
import { createTestSession } from './callStack.test.js';
20
import { createMockDebugModel } from './mockDebugModel.js';
21
22
suite('Debug - ANSI Handling', () => {
23
24
let disposables: DisposableStore;
25
let model: DebugModel;
26
let session: DebugSession;
27
let linkDetector: LinkDetector;
28
29
/**
30
* Instantiate services for use by the functions being tested.
31
*/
32
setup(() => {
33
disposables = new DisposableStore();
34
model = createMockDebugModel(disposables);
35
session = createTestSession(model);
36
37
const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService(undefined, disposables);
38
linkDetector = instantiationService.createInstance(LinkDetector);
39
registerColors();
40
});
41
42
teardown(() => {
43
disposables.dispose();
44
});
45
46
ensureNoDisposablesAreLeakedInTestSuite();
47
48
test('appendStylizedStringToContainer', () => {
49
const root: HTMLSpanElement = document.createElement('span');
50
let child: Node;
51
52
assert.strictEqual(0, root.children.length);
53
54
const hoverBehavior = { type: DebugLinkHoverBehavior.None, store: new DisposableStore() };
55
appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session.root, undefined, undefined, undefined, undefined, 0, hoverBehavior);
56
appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session.root, undefined, undefined, undefined, undefined, 0, hoverBehavior);
57
58
assert.strictEqual(2, root.children.length);
59
60
child = root.firstChild!;
61
if (isHTMLSpanElement(child)) {
62
assert.strictEqual('content1', child.textContent);
63
assert(child.classList.contains('class1'));
64
assert(child.classList.contains('class2'));
65
} else {
66
assert.fail('Unexpected assertion error');
67
}
68
69
child = root.lastChild!;
70
if (isHTMLSpanElement(child)) {
71
assert.strictEqual('content2', child.textContent);
72
assert(child.classList.contains('class2'));
73
assert(child.classList.contains('class3'));
74
} else {
75
assert.fail('Unexpected assertion error');
76
}
77
hoverBehavior.store.dispose();
78
});
79
80
/**
81
* Apply an ANSI sequence to {@link #getSequenceOutput}.
82
*
83
* @param sequence The ANSI sequence to stylize.
84
* @returns An {@link HTMLSpanElement} that contains the stylized text.
85
*/
86
function getSequenceOutput(sequence: string): HTMLSpanElement {
87
const hoverBehavior = { type: DebugLinkHoverBehavior.None, store: new DisposableStore() };
88
const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, session.root, [], hoverBehavior);
89
assert.strictEqual(1, root.children.length);
90
const child: Node = root.lastChild!;
91
hoverBehavior.store.dispose();
92
if (isHTMLSpanElement(child)) {
93
return child;
94
} else {
95
assert.fail('Unexpected assertion error');
96
}
97
}
98
99
/**
100
* Assert that a given ANSI sequence maintains added content following the ANSI code, and that
101
* the provided {@param assertion} passes.
102
*
103
* @param sequence The ANSI sequence to verify. The provided sequence should contain ANSI codes
104
* only, and should not include actual text content as it is provided by this function.
105
* @param assertion The function used to verify the output.
106
*/
107
function assertSingleSequenceElement(sequence: string, assertion: (child: HTMLSpanElement) => void): void {
108
const child: HTMLSpanElement = getSequenceOutput(sequence + 'content');
109
assert.strictEqual('content', child.textContent);
110
assertion(child);
111
}
112
113
/**
114
* Assert that a given DOM element has the custom inline CSS style matching
115
* the color value provided.
116
* @param element The HTML span element to look at.
117
* @param colorType If `foreground`, will check the element's css `color`;
118
* if `background`, will check the element's css `backgroundColor`.
119
* if `underline`, will check the elements css `textDecorationColor`.
120
* @param color RGBA object to compare color to. If `undefined` or not provided,
121
* will assert that no value is set.
122
* @param message Optional custom message to pass to assertion.
123
* @param colorShouldMatch Optional flag (defaults TO true) which allows caller to indicate that the color SHOULD NOT MATCH
124
* (for testing changes to theme colors where we need color to have changed but we don't know exact color it should have
125
* changed to (but we do know the color it should NO LONGER BE))
126
*/
127
function assertInlineColor(element: HTMLSpanElement, colorType: 'background' | 'foreground' | 'underline', color?: RGBA | undefined, message?: string, colorShouldMatch: boolean = true): void {
128
if (color !== undefined) {
129
const cssColor = Color.Format.CSS.formatRGB(
130
new Color(color)
131
);
132
if (colorType === 'background') {
133
const styleBefore = element.style.backgroundColor;
134
element.style.backgroundColor = cssColor;
135
assert((styleBefore === element.style.backgroundColor) === colorShouldMatch, message || `Incorrect ${colorType} color style found (found color: ${styleBefore}, expected ${cssColor}).`);
136
} else if (colorType === 'foreground') {
137
const styleBefore = element.style.color;
138
element.style.color = cssColor;
139
assert((styleBefore === element.style.color) === colorShouldMatch, message || `Incorrect ${colorType} color style found (found color: ${styleBefore}, expected ${cssColor}).`);
140
} else {
141
const styleBefore = element.style.textDecorationColor;
142
element.style.textDecorationColor = cssColor;
143
assert((styleBefore === element.style.textDecorationColor) === colorShouldMatch, message || `Incorrect ${colorType} color style found (found color: ${styleBefore}, expected ${cssColor}).`);
144
}
145
} else {
146
if (colorType === 'background') {
147
assert(!element.style.backgroundColor, message || `Defined ${colorType} color style found when it should not have been defined`);
148
} else if (colorType === 'foreground') {
149
assert(!element.style.color, message || `Defined ${colorType} color style found when it should not have been defined`);
150
} else {
151
assert(!element.style.textDecorationColor, message || `Defined ${colorType} color style found when it should not have been defined`);
152
}
153
}
154
155
}
156
157
test('Expected single sequence operation', () => {
158
159
// Bold code
160
assertSingleSequenceElement('\x1b[1m', (child) => {
161
assert(child.classList.contains('code-bold'), 'Bold formatting not detected after bold ANSI code.');
162
});
163
164
// Italic code
165
assertSingleSequenceElement('\x1b[3m', (child) => {
166
assert(child.classList.contains('code-italic'), 'Italic formatting not detected after italic ANSI code.');
167
});
168
169
// Underline code
170
assertSingleSequenceElement('\x1b[4m', (child) => {
171
assert(child.classList.contains('code-underline'), 'Underline formatting not detected after underline ANSI code.');
172
});
173
174
for (let i = 30; i <= 37; i++) {
175
const customClassName: string = 'code-foreground-colored';
176
177
// Foreground colour class
178
assertSingleSequenceElement('\x1b[' + i + 'm', (child) => {
179
assert(child.classList.contains(customClassName), `Custom foreground class not found on element after foreground ANSI code #${i}.`);
180
});
181
182
// Cancellation code removes colour class
183
assertSingleSequenceElement('\x1b[' + i + ';39m', (child) => {
184
assert(child.classList.contains(customClassName) === false, 'Custom foreground class still found after foreground cancellation code.');
185
assertInlineColor(child, 'foreground', undefined, 'Custom color style still found after foreground cancellation code.');
186
});
187
}
188
189
for (let i = 40; i <= 47; i++) {
190
const customClassName: string = 'code-background-colored';
191
192
// Foreground colour class
193
assertSingleSequenceElement('\x1b[' + i + 'm', (child) => {
194
assert(child.classList.contains(customClassName), `Custom background class not found on element after background ANSI code #${i}.`);
195
});
196
197
// Cancellation code removes colour class
198
assertSingleSequenceElement('\x1b[' + i + ';49m', (child) => {
199
assert(child.classList.contains(customClassName) === false, 'Custom background class still found after background cancellation code.');
200
assertInlineColor(child, 'foreground', undefined, 'Custom color style still found after background cancellation code.');
201
});
202
}
203
204
// check all basic colors for underlines (full range is checked elsewhere, here we check cancelation)
205
for (let i = 0; i <= 255; i++) {
206
const customClassName: string = 'code-underline-colored';
207
208
// Underline colour class
209
assertSingleSequenceElement('\x1b[58;5;' + i + 'm', (child) => {
210
assert(child.classList.contains(customClassName), `Custom underline color class not found on element after underline color ANSI code 58;5;${i}m.`);
211
});
212
213
// Cancellation underline color code removes colour class
214
assertSingleSequenceElement('\x1b[58;5;' + i + 'm\x1b[59m', (child) => {
215
assert(child.classList.contains(customClassName) === false, 'Custom underline color class still found after underline color cancellation code 59m.');
216
assertInlineColor(child, 'underline', undefined, 'Custom underline color style still found after underline color cancellation code 59m.');
217
});
218
}
219
220
// Different codes do not cancel each other
221
assertSingleSequenceElement('\x1b[1;3;4;30;41m', (child) => {
222
assert.strictEqual(5, child.classList.length, 'Incorrect number of classes found for different ANSI codes.');
223
224
assert(child.classList.contains('code-bold'));
225
assert(child.classList.contains('code-italic'), 'Different ANSI codes should not cancel each other.');
226
assert(child.classList.contains('code-underline'), 'Different ANSI codes should not cancel each other.');
227
assert(child.classList.contains('code-foreground-colored'), 'Different ANSI codes should not cancel each other.');
228
assert(child.classList.contains('code-background-colored'), 'Different ANSI codes should not cancel each other.');
229
});
230
231
// Different codes do not ACCUMULATE more than one copy of each class
232
assertSingleSequenceElement('\x1b[1;1;2;2;3;3;4;4;5;5;6;6;8;8;9;9;21;21;53;53;73;73;74;74m', (child) => {
233
assert(child.classList.contains('code-bold'));
234
assert(child.classList.contains('code-italic'), 'italic missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
235
assert(child.classList.contains('code-underline') === false, 'underline PRESENT and double underline should have removed it- Doubles of each Different ANSI codes should not cancel each other or accumulate.');
236
assert(child.classList.contains('code-dim'), 'dim missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
237
assert(child.classList.contains('code-blink'), 'blink missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
238
assert(child.classList.contains('code-rapid-blink'), 'rapid blink mkssing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
239
assert(child.classList.contains('code-double-underline'), 'double underline missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
240
assert(child.classList.contains('code-hidden'), 'hidden missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
241
assert(child.classList.contains('code-strike-through'), 'strike-through missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
242
assert(child.classList.contains('code-overline'), 'overline missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
243
assert(child.classList.contains('code-superscript') === false, 'superscript PRESENT and subscript should have removed it- Doubles of each Different ANSI codes should not cancel each other or accumulate.');
244
assert(child.classList.contains('code-subscript'), 'subscript missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');
245
246
assert.strictEqual(10, child.classList.length, 'Incorrect number of classes found for each style code sent twice ANSI codes.');
247
});
248
249
250
251
// More Different codes do not cancel each other
252
assertSingleSequenceElement('\x1b[1;2;5;6;21;8;9m', (child) => {
253
assert.strictEqual(7, child.classList.length, 'Incorrect number of classes found for different ANSI codes.');
254
255
assert(child.classList.contains('code-bold'));
256
assert(child.classList.contains('code-dim'), 'Different ANSI codes should not cancel each other.');
257
assert(child.classList.contains('code-blink'), 'Different ANSI codes should not cancel each other.');
258
assert(child.classList.contains('code-rapid-blink'), 'Different ANSI codes should not cancel each other.');
259
assert(child.classList.contains('code-double-underline'), 'Different ANSI codes should not cancel each other.');
260
assert(child.classList.contains('code-hidden'), 'Different ANSI codes should not cancel each other.');
261
assert(child.classList.contains('code-strike-through'), 'Different ANSI codes should not cancel each other.');
262
});
263
264
265
266
// New foreground codes don't remove old background codes and vice versa
267
assertSingleSequenceElement('\x1b[40;31;42;33m', (child) => {
268
assert.strictEqual(2, child.classList.length);
269
270
assert(child.classList.contains('code-background-colored'), 'New foreground ANSI code should not cancel existing background formatting.');
271
assert(child.classList.contains('code-foreground-colored'), 'New background ANSI code should not cancel existing foreground formatting.');
272
});
273
274
// Duplicate codes do not change output
275
assertSingleSequenceElement('\x1b[1;1;4;1;4;4;1;4m', (child) => {
276
assert(child.classList.contains('code-bold'), 'Duplicate formatting codes should have no effect.');
277
assert(child.classList.contains('code-underline'), 'Duplicate formatting codes should have no effect.');
278
});
279
280
// Extra terminating semicolon does not change output
281
assertSingleSequenceElement('\x1b[1;4;m', (child) => {
282
assert(child.classList.contains('code-bold'), 'Extra semicolon after ANSI codes should have no effect.');
283
assert(child.classList.contains('code-underline'), 'Extra semicolon after ANSI codes should have no effect.');
284
});
285
286
// Cancellation code removes multiple codes
287
assertSingleSequenceElement('\x1b[1;4;30;41;32;43;34;45;36;47;0m', (child) => {
288
assert.strictEqual(0, child.classList.length, 'Cancellation ANSI code should clear ALL formatting.');
289
assertInlineColor(child, 'background', undefined, 'Cancellation ANSI code should clear ALL formatting.');
290
assertInlineColor(child, 'foreground', undefined, 'Cancellation ANSI code should clear ALL formatting.');
291
});
292
293
});
294
295
test('Expected single 8-bit color sequence operation', () => {
296
// Basic and bright color codes specified with 8-bit color code format
297
for (let i = 0; i <= 15; i++) {
298
// As these are controlled by theme, difficult to check actual color value
299
// Foreground codes should add standard classes
300
assertSingleSequenceElement('\x1b[38;5;' + i + 'm', (child) => {
301
assert(child.classList.contains('code-foreground-colored'), `Custom color class not found after foreground 8-bit color code 38;5;${i}`);
302
});
303
304
// Background codes should add standard classes
305
assertSingleSequenceElement('\x1b[48;5;' + i + 'm', (child) => {
306
assert(child.classList.contains('code-background-colored'), `Custom color class not found after background 8-bit color code 48;5;${i}`);
307
});
308
}
309
310
// 8-bit advanced colors
311
for (let i = 16; i <= 255; i++) {
312
// Foreground codes should add custom class and inline style
313
assertSingleSequenceElement('\x1b[38;5;' + i + 'm', (child) => {
314
assert(child.classList.contains('code-foreground-colored'), `Custom color class not found after foreground 8-bit color code 38;5;${i}`);
315
assertInlineColor(child, 'foreground', (calcANSI8bitColor(i) as RGBA), `Incorrect or no color styling found after foreground 8-bit color code 38;5;${i}`);
316
});
317
318
// Background codes should add custom class and inline style
319
assertSingleSequenceElement('\x1b[48;5;' + i + 'm', (child) => {
320
assert(child.classList.contains('code-background-colored'), `Custom color class not found after background 8-bit color code 48;5;${i}`);
321
assertInlineColor(child, 'background', (calcANSI8bitColor(i) as RGBA), `Incorrect or no color styling found after background 8-bit color code 48;5;${i}`);
322
});
323
324
// Color underline codes should add custom class and inline style
325
assertSingleSequenceElement('\x1b[58;5;' + i + 'm', (child) => {
326
assert(child.classList.contains('code-underline-colored'), `Custom color class not found after underline 8-bit color code 58;5;${i}`);
327
assertInlineColor(child, 'underline', (calcANSI8bitColor(i) as RGBA), `Incorrect or no color styling found after underline 8-bit color code 58;5;${i}`);
328
});
329
}
330
331
// Bad (nonexistent) color should not render
332
assertSingleSequenceElement('\x1b[48;5;300m', (child) => {
333
assert.strictEqual(0, child.classList.length, 'Bad ANSI color codes should have no effect.');
334
});
335
336
// Should ignore any codes after the ones needed to determine color
337
assertSingleSequenceElement('\x1b[48;5;100;42;77;99;4;24m', (child) => {
338
assert(child.classList.contains('code-background-colored'));
339
assert.strictEqual(1, child.classList.length);
340
assertInlineColor(child, 'background', (calcANSI8bitColor(100) as RGBA));
341
});
342
});
343
344
test('Expected single 24-bit color sequence operation', () => {
345
// 24-bit advanced colors
346
for (let r = 0; r <= 255; r += 64) {
347
for (let g = 0; g <= 255; g += 64) {
348
for (let b = 0; b <= 255; b += 64) {
349
const color = new RGBA(r, g, b);
350
// Foreground codes should add class and inline style
351
assertSingleSequenceElement(`\x1b[38;2;${r};${g};${b}m`, (child) => {
352
assert(child.classList.contains('code-foreground-colored'), 'DOM should have "code-foreground-colored" class for advanced ANSI colors.');
353
assertInlineColor(child, 'foreground', color);
354
});
355
356
// Background codes should add class and inline style
357
assertSingleSequenceElement(`\x1b[48;2;${r};${g};${b}m`, (child) => {
358
assert(child.classList.contains('code-background-colored'), 'DOM should have "code-foreground-colored" class for advanced ANSI colors.');
359
assertInlineColor(child, 'background', color);
360
});
361
362
// Underline color codes should add class and inline style
363
assertSingleSequenceElement(`\x1b[58;2;${r};${g};${b}m`, (child) => {
364
assert(child.classList.contains('code-underline-colored'), 'DOM should have "code-underline-colored" class for advanced ANSI colors.');
365
assertInlineColor(child, 'underline', color);
366
});
367
}
368
}
369
}
370
371
// Invalid color should not render
372
assertSingleSequenceElement('\x1b[38;2;4;4m', (child) => {
373
assert.strictEqual(0, child.classList.length, `Invalid color code "38;2;4;4" should not add a class (classes found: ${child.classList}).`);
374
assert(!child.style.color, `Invalid color code "38;2;4;4" should not add a custom color CSS (found color: ${child.style.color}).`);
375
});
376
377
// Bad (nonexistent) color should not render
378
assertSingleSequenceElement('\x1b[48;2;150;300;5m', (child) => {
379
assert.strictEqual(0, child.classList.length, `Nonexistent color code "48;2;150;300;5" should not add a class (classes found: ${child.classList}).`);
380
});
381
382
// Should ignore any codes after the ones needed to determine color
383
assertSingleSequenceElement('\x1b[48;2;100;42;77;99;200;75m', (child) => {
384
assert(child.classList.contains('code-background-colored'), `Color code with extra (valid) items "48;2;100;42;77;99;200;75" should still treat initial part as valid code and add class "code-background-custom".`);
385
assert.strictEqual(1, child.classList.length, `Color code with extra items "48;2;100;42;77;99;200;75" should add one and only one class. (classes found: ${child.classList}).`);
386
assertInlineColor(child, 'background', new RGBA(100, 42, 77), `Color code "48;2;100;42;77;99;200;75" should style background-color as rgb(100,42,77).`);
387
});
388
});
389
390
391
/**
392
* Assert that a given ANSI sequence produces the expected number of {@link HTMLSpanElement} children. For
393
* each child, run the provided assertion.
394
*
395
* @param sequence The ANSI sequence to verify.
396
* @param assertions A set of assertions to run on the resulting children.
397
*/
398
function assertMultipleSequenceElements(sequence: string, assertions: Array<(child: HTMLSpanElement) => void>, elementsExpected?: number): void {
399
if (elementsExpected === undefined) {
400
elementsExpected = assertions.length;
401
}
402
const hoverBehavior = { type: DebugLinkHoverBehavior.None, store: new DisposableStore() };
403
const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, session.root, [], hoverBehavior);
404
assert.strictEqual(elementsExpected, root.children.length);
405
for (let i = 0; i < elementsExpected; i++) {
406
const child: Node = root.children[i];
407
if (isHTMLSpanElement(child)) {
408
assertions[i](child);
409
} else {
410
assert.fail('Unexpected assertion error');
411
}
412
}
413
hoverBehavior.store.dispose();
414
}
415
416
test('Expected multiple sequence operation', () => {
417
418
// Multiple codes affect the same text
419
assertSingleSequenceElement('\x1b[1m\x1b[3m\x1b[4m\x1b[32m', (child) => {
420
assert(child.classList.contains('code-bold'), 'Bold class not found after multiple different ANSI codes.');
421
assert(child.classList.contains('code-italic'), 'Italic class not found after multiple different ANSI codes.');
422
assert(child.classList.contains('code-underline'), 'Underline class not found after multiple different ANSI codes.');
423
assert(child.classList.contains('code-foreground-colored'), 'Foreground color class not found after multiple different ANSI codes.');
424
});
425
426
// Consecutive codes do not affect previous ones
427
assertMultipleSequenceElements('\x1b[1mbold\x1b[32mgreen\x1b[4munderline\x1b[3mitalic\x1b[0mnothing', [
428
(bold) => {
429
assert.strictEqual(1, bold.classList.length);
430
assert(bold.classList.contains('code-bold'), 'Bold class not found after bold ANSI code.');
431
},
432
(green) => {
433
assert.strictEqual(2, green.classList.length);
434
assert(green.classList.contains('code-bold'), 'Bold class not found after both bold and color ANSI codes.');
435
assert(green.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');
436
},
437
(underline) => {
438
assert.strictEqual(3, underline.classList.length);
439
assert(underline.classList.contains('code-bold'), 'Bold class not found after bold, color, and underline ANSI codes.');
440
assert(underline.classList.contains('code-foreground-colored'), 'Color class not found after color and underline ANSI codes.');
441
assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code.');
442
},
443
(italic) => {
444
assert.strictEqual(4, italic.classList.length);
445
assert(italic.classList.contains('code-bold'), 'Bold class not found after bold, color, underline, and italic ANSI codes.');
446
assert(italic.classList.contains('code-foreground-colored'), 'Color class not found after color, underline, and italic ANSI codes.');
447
assert(italic.classList.contains('code-underline'), 'Underline class not found after underline and italic ANSI codes.');
448
assert(italic.classList.contains('code-italic'), 'Italic class not found after italic ANSI code.');
449
},
450
(nothing) => {
451
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after reset ANSI code.');
452
},
453
], 5);
454
455
// Consecutive codes with ENDING/OFF codes do not LEAVE affect previous ones
456
assertMultipleSequenceElements('\x1b[1mbold\x1b[22m\x1b[32mgreen\x1b[4munderline\x1b[24m\x1b[3mitalic\x1b[23mjustgreen\x1b[0mnothing', [
457
(bold) => {
458
assert.strictEqual(1, bold.classList.length);
459
assert(bold.classList.contains('code-bold'), 'Bold class not found after bold ANSI code.');
460
},
461
(green) => {
462
assert.strictEqual(1, green.classList.length);
463
assert(green.classList.contains('code-bold') === false, 'Bold class found after both bold WAS TURNED OFF with 22m');
464
assert(green.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');
465
},
466
(underline) => {
467
assert.strictEqual(2, underline.classList.length);
468
assert(underline.classList.contains('code-foreground-colored'), 'Color class not found after color and underline ANSI codes.');
469
assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code.');
470
},
471
(italic) => {
472
assert.strictEqual(2, italic.classList.length);
473
assert(italic.classList.contains('code-foreground-colored'), 'Color class not found after color, underline, and italic ANSI codes.');
474
assert(italic.classList.contains('code-underline') === false, 'Underline class found after underline WAS TURNED OFF with 24m');
475
assert(italic.classList.contains('code-italic'), 'Italic class not found after italic ANSI code.');
476
},
477
(justgreen) => {
478
assert.strictEqual(1, justgreen.classList.length);
479
assert(justgreen.classList.contains('code-italic') === false, 'Italic class found after italic WAS TURNED OFF with 23m');
480
assert(justgreen.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');
481
},
482
(nothing) => {
483
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after reset ANSI code.');
484
},
485
], 6);
486
487
// more Consecutive codes with ENDING/OFF codes do not LEAVE affect previous ones
488
assertMultipleSequenceElements('\x1b[2mdim\x1b[22m\x1b[32mgreen\x1b[5mslowblink\x1b[25m\x1b[6mrapidblink\x1b[25mjustgreen\x1b[0mnothing', [
489
(dim) => {
490
assert.strictEqual(1, dim.classList.length);
491
assert(dim.classList.contains('code-dim'), 'Dim class not found after dim ANSI code 2m.');
492
},
493
(green) => {
494
assert.strictEqual(1, green.classList.length);
495
assert(green.classList.contains('code-dim') === false, 'Dim class found after dim WAS TURNED OFF with 22m');
496
assert(green.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');
497
},
498
(slowblink) => {
499
assert.strictEqual(2, slowblink.classList.length);
500
assert(slowblink.classList.contains('code-foreground-colored'), 'Color class not found after color and blink ANSI codes.');
501
assert(slowblink.classList.contains('code-blink'), 'Blink class not found after underline ANSI code 5m.');
502
},
503
(rapidblink) => {
504
assert.strictEqual(2, rapidblink.classList.length);
505
assert(rapidblink.classList.contains('code-foreground-colored'), 'Color class not found after color, blink, and rapid blink ANSI codes.');
506
assert(rapidblink.classList.contains('code-blink') === false, 'blink class found after underline WAS TURNED OFF with 25m');
507
assert(rapidblink.classList.contains('code-rapid-blink'), 'Rapid blink class not found after rapid blink ANSI code 6m.');
508
},
509
(justgreen) => {
510
assert.strictEqual(1, justgreen.classList.length);
511
assert(justgreen.classList.contains('code-rapid-blink') === false, 'Rapid blink class found after rapid blink WAS TURNED OFF with 25m');
512
assert(justgreen.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');
513
},
514
(nothing) => {
515
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after reset ANSI code.');
516
},
517
], 6);
518
519
// more Consecutive codes with ENDING/OFF codes do not LEAVE affect previous ones
520
assertMultipleSequenceElements('\x1b[8mhidden\x1b[28m\x1b[32mgreen\x1b[9mcrossedout\x1b[29m\x1b[21mdoubleunderline\x1b[24mjustgreen\x1b[0mnothing', [
521
(hidden) => {
522
assert.strictEqual(1, hidden.classList.length);
523
assert(hidden.classList.contains('code-hidden'), 'Hidden class not found after dim ANSI code 8m.');
524
},
525
(green) => {
526
assert.strictEqual(1, green.classList.length);
527
assert(green.classList.contains('code-hidden') === false, 'Hidden class found after Hidden WAS TURNED OFF with 28m');
528
assert(green.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');
529
},
530
(crossedout) => {
531
assert.strictEqual(2, crossedout.classList.length);
532
assert(crossedout.classList.contains('code-foreground-colored'), 'Color class not found after color and hidden ANSI codes.');
533
assert(crossedout.classList.contains('code-strike-through'), 'strike-through class not found after crossout/strikethrough ANSI code 9m.');
534
},
535
(doubleunderline) => {
536
assert.strictEqual(2, doubleunderline.classList.length);
537
assert(doubleunderline.classList.contains('code-foreground-colored'), 'Color class not found after color, hidden, and crossedout ANSI codes.');
538
assert(doubleunderline.classList.contains('code-strike-through') === false, 'strike-through class found after strike-through WAS TURNED OFF with 29m');
539
assert(doubleunderline.classList.contains('code-double-underline'), 'Double underline class not found after double underline ANSI code 21m.');
540
},
541
(justgreen) => {
542
assert.strictEqual(1, justgreen.classList.length);
543
assert(justgreen.classList.contains('code-double-underline') === false, 'Double underline class found after double underline WAS TURNED OFF with 24m');
544
assert(justgreen.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');
545
},
546
(nothing) => {
547
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after reset ANSI code.');
548
},
549
], 6);
550
551
// underline, double underline are mutually exclusive, test underline->double underline->off and double underline->underline->off
552
assertMultipleSequenceElements('\x1b[4munderline\x1b[21mdouble underline\x1b[24munderlineOff\x1b[21mdouble underline\x1b[4munderline\x1b[24munderlineOff', [
553
(underline) => {
554
assert.strictEqual(1, underline.classList.length);
555
assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code 4m.');
556
},
557
(doubleunderline) => {
558
assert(doubleunderline.classList.contains('code-underline') === false, 'Underline class found after double underline code 21m');
559
assert(doubleunderline.classList.contains('code-double-underline'), 'Double underline class not found after double underline code 21m');
560
assert.strictEqual(1, doubleunderline.classList.length, 'should have found only double underline');
561
},
562
(nothing) => {
563
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after underline off code 4m.');
564
},
565
(doubleunderline) => {
566
assert(doubleunderline.classList.contains('code-double-underline'), 'Double underline class not found after double underline code 21m');
567
assert.strictEqual(1, doubleunderline.classList.length, 'should have found only double underline');
568
},
569
(underline) => {
570
assert(underline.classList.contains('code-double-underline') === false, 'Double underline class found after underline code 4m');
571
assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code 4m.');
572
assert.strictEqual(1, underline.classList.length, 'should have found only underline');
573
},
574
(nothing) => {
575
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after underline off code 4m.');
576
},
577
], 6);
578
579
// underline and strike-through and overline can exist at the same time and
580
// in any combination
581
assertMultipleSequenceElements('\x1b[4munderline\x1b[9mand strikethough\x1b[53mand overline\x1b[24munderlineOff\x1b[55moverlineOff\x1b[29mstriklethoughOff', [
582
(underline) => {
583
assert.strictEqual(1, underline.classList.length, 'should have found only underline');
584
assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code 4m.');
585
},
586
(strikethrough) => {
587
assert(strikethrough.classList.contains('code-underline'), 'Underline class NOT found after strikethrough code 9m');
588
assert(strikethrough.classList.contains('code-strike-through'), 'Strike through class not found after strikethrough code 9m');
589
assert.strictEqual(2, strikethrough.classList.length, 'should have found underline and strikethrough');
590
},
591
(overline) => {
592
assert(overline.classList.contains('code-underline'), 'Underline class NOT found after overline code 53m');
593
assert(overline.classList.contains('code-strike-through'), 'Strike through class not found after overline code 53m');
594
assert(overline.classList.contains('code-overline'), 'Overline class not found after overline code 53m');
595
assert.strictEqual(3, overline.classList.length, 'should have found underline,strikethrough and overline');
596
},
597
(underlineoff) => {
598
assert(underlineoff.classList.contains('code-underline') === false, 'Underline class found after underline off code 24m');
599
assert(underlineoff.classList.contains('code-strike-through'), 'Strike through class not found after underline off code 24m');
600
assert(underlineoff.classList.contains('code-overline'), 'Overline class not found after underline off code 24m');
601
assert.strictEqual(2, underlineoff.classList.length, 'should have found strikethrough and overline');
602
},
603
(overlineoff) => {
604
assert(overlineoff.classList.contains('code-underline') === false, 'Underline class found after overline off code 55m');
605
assert(overlineoff.classList.contains('code-overline') === false, 'Overline class found after overline off code 55m');
606
assert(overlineoff.classList.contains('code-strike-through'), 'Strike through class not found after overline off code 55m');
607
assert.strictEqual(1, overlineoff.classList.length, 'should have found only strikethrough');
608
},
609
(nothing) => {
610
assert(nothing.classList.contains('code-strike-through') === false, 'Strike through class found after strikethrough off code 29m');
611
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after strikethough OFF code 29m');
612
},
613
], 6);
614
615
// double underline and strike-through and overline can exist at the same time and
616
// in any combination
617
assertMultipleSequenceElements('\x1b[21mdoubleunderline\x1b[9mand strikethough\x1b[53mand overline\x1b[29mstriklethoughOff\x1b[55moverlineOff\x1b[24munderlineOff', [
618
(doubleunderline) => {
619
assert.strictEqual(1, doubleunderline.classList.length, 'should have found only doubleunderline');
620
assert(doubleunderline.classList.contains('code-double-underline'), 'Double underline class not found after double underline ANSI code 21m.');
621
},
622
(strikethrough) => {
623
assert(strikethrough.classList.contains('code-double-underline'), 'Double nderline class NOT found after strikethrough code 9m');
624
assert(strikethrough.classList.contains('code-strike-through'), 'Strike through class not found after strikethrough code 9m');
625
assert.strictEqual(2, strikethrough.classList.length, 'should have found doubleunderline and strikethrough');
626
},
627
(overline) => {
628
assert(overline.classList.contains('code-double-underline'), 'Double underline class NOT found after overline code 53m');
629
assert(overline.classList.contains('code-strike-through'), 'Strike through class not found after overline code 53m');
630
assert(overline.classList.contains('code-overline'), 'Overline class not found after overline code 53m');
631
assert.strictEqual(3, overline.classList.length, 'should have found doubleunderline,overline and strikethrough');
632
},
633
(strikethrougheoff) => {
634
assert(strikethrougheoff.classList.contains('code-double-underline'), 'Double underline class NOT found after strikethrough off code 29m');
635
assert(strikethrougheoff.classList.contains('code-overline'), 'Overline class NOT found after strikethrough off code 29m');
636
assert(strikethrougheoff.classList.contains('code-strike-through') === false, 'Strike through class found after strikethrough off code 29m');
637
assert.strictEqual(2, strikethrougheoff.classList.length, 'should have found doubleunderline and overline');
638
},
639
(overlineoff) => {
640
assert(overlineoff.classList.contains('code-double-underline'), 'Double underline class NOT found after overline off code 55m');
641
assert(overlineoff.classList.contains('code-strike-through') === false, 'Strike through class found after overline off code 55m');
642
assert(overlineoff.classList.contains('code-overline') === false, 'Overline class found after overline off code 55m');
643
assert.strictEqual(1, overlineoff.classList.length, 'Should have found only double underline');
644
},
645
(nothing) => {
646
assert(nothing.classList.contains('code-double-underline') === false, 'Double underline class found after underline off code 24m');
647
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after underline OFF code 24m');
648
},
649
], 6);
650
651
// superscript and subscript are mutually exclusive, test superscript->subscript->off and subscript->superscript->off
652
assertMultipleSequenceElements('\x1b[73msuperscript\x1b[74msubscript\x1b[75mneither\x1b[74msubscript\x1b[73msuperscript\x1b[75mneither', [
653
(superscript) => {
654
assert.strictEqual(1, superscript.classList.length, 'should only be superscript class');
655
assert(superscript.classList.contains('code-superscript'), 'Superscript class not found after superscript ANSI code 73m.');
656
},
657
(subscript) => {
658
assert(subscript.classList.contains('code-superscript') === false, 'Superscript class found after subscript code 74m');
659
assert(subscript.classList.contains('code-subscript'), 'Subscript class not found after subscript code 74m');
660
assert.strictEqual(1, subscript.classList.length, 'should have found only subscript class');
661
},
662
(nothing) => {
663
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after superscript/subscript off code 75m.');
664
},
665
(subscript) => {
666
assert(subscript.classList.contains('code-subscript'), 'Subscript class not found after subscript code 74m');
667
assert.strictEqual(1, subscript.classList.length, 'should have found only subscript class');
668
},
669
(superscript) => {
670
assert(superscript.classList.contains('code-subscript') === false, 'Subscript class found after superscript code 73m');
671
assert(superscript.classList.contains('code-superscript'), 'Superscript class not found after superscript ANSI code 73m.');
672
assert.strictEqual(1, superscript.classList.length, 'should have found only superscript class');
673
},
674
(nothing) => {
675
assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after superscipt/subscript off code 75m.');
676
},
677
], 6);
678
679
// Consecutive font codes switch to new font class and remove previous and then final switch to default font removes class
680
assertMultipleSequenceElements('\x1b[11mFont1\x1b[12mFont2\x1b[13mFont3\x1b[14mFont4\x1b[15mFont5\x1b[10mdefaultFont', [
681
(font1) => {
682
assert.strictEqual(1, font1.classList.length);
683
assert(font1.classList.contains('code-font-1'), 'font 1 class NOT found after switch to font 1 with ANSI code 11m');
684
},
685
(font2) => {
686
assert.strictEqual(1, font2.classList.length);
687
assert(font2.classList.contains('code-font-1') === false, 'font 1 class found after switch to font 2 with ANSI code 12m');
688
assert(font2.classList.contains('code-font-2'), 'font 2 class NOT found after switch to font 2 with ANSI code 12m');
689
},
690
(font3) => {
691
assert.strictEqual(1, font3.classList.length);
692
assert(font3.classList.contains('code-font-2') === false, 'font 2 class found after switch to font 3 with ANSI code 13m');
693
assert(font3.classList.contains('code-font-3'), 'font 3 class NOT found after switch to font 3 with ANSI code 13m');
694
},
695
(font4) => {
696
assert.strictEqual(1, font4.classList.length);
697
assert(font4.classList.contains('code-font-3') === false, 'font 3 class found after switch to font 4 with ANSI code 14m');
698
assert(font4.classList.contains('code-font-4'), 'font 4 class NOT found after switch to font 4 with ANSI code 14m');
699
},
700
(font5) => {
701
assert.strictEqual(1, font5.classList.length);
702
assert(font5.classList.contains('code-font-4') === false, 'font 4 class found after switch to font 5 with ANSI code 15m');
703
assert(font5.classList.contains('code-font-5'), 'font 5 class NOT found after switch to font 5 with ANSI code 15m');
704
},
705
(defaultfont) => {
706
assert.strictEqual(0, defaultfont.classList.length, 'One or more font style classes still found after reset to default font with ANSI code 10m.');
707
},
708
], 6);
709
710
// More Consecutive font codes switch to new font class and remove previous and then final switch to default font removes class
711
assertMultipleSequenceElements('\x1b[16mFont6\x1b[17mFont7\x1b[18mFont8\x1b[19mFont9\x1b[20mFont10\x1b[10mdefaultFont', [
712
(font6) => {
713
assert.strictEqual(1, font6.classList.length);
714
assert(font6.classList.contains('code-font-6'), 'font 6 class NOT found after switch to font 6 with ANSI code 16m');
715
},
716
(font7) => {
717
assert.strictEqual(1, font7.classList.length);
718
assert(font7.classList.contains('code-font-6') === false, 'font 6 class found after switch to font 7 with ANSI code 17m');
719
assert(font7.classList.contains('code-font-7'), 'font 7 class NOT found after switch to font 7 with ANSI code 17m');
720
},
721
(font8) => {
722
assert.strictEqual(1, font8.classList.length);
723
assert(font8.classList.contains('code-font-7') === false, 'font 7 class found after switch to font 8 with ANSI code 18m');
724
assert(font8.classList.contains('code-font-8'), 'font 8 class NOT found after switch to font 8 with ANSI code 18m');
725
},
726
(font9) => {
727
assert.strictEqual(1, font9.classList.length);
728
assert(font9.classList.contains('code-font-8') === false, 'font 8 class found after switch to font 9 with ANSI code 19m');
729
assert(font9.classList.contains('code-font-9'), 'font 9 class NOT found after switch to font 9 with ANSI code 19m');
730
},
731
(font10) => {
732
assert.strictEqual(1, font10.classList.length);
733
assert(font10.classList.contains('code-font-9') === false, 'font 9 class found after switch to font 10 with ANSI code 20m');
734
assert(font10.classList.contains('code-font-10'), `font 10 class NOT found after switch to font 10 with ANSI code 20m (${font10.classList})`);
735
},
736
(defaultfont) => {
737
assert.strictEqual(0, defaultfont.classList.length, 'One or more font style classes (2nd series) still found after reset to default font with ANSI code 10m.');
738
},
739
], 6);
740
741
// Blackletter font codes can be turned off with other font codes or 23m
742
assertMultipleSequenceElements('\x1b[3mitalic\x1b[20mfont10blacklatter\x1b[23mitalicAndBlackletterOff\x1b[20mFont10Again\x1b[11mFont1\x1b[10mdefaultFont', [
743
(italic) => {
744
assert.strictEqual(1, italic.classList.length);
745
assert(italic.classList.contains('code-italic'), 'italic class NOT found after italic code ANSI code 3m');
746
},
747
(font10) => {
748
assert.strictEqual(2, font10.classList.length);
749
assert(font10.classList.contains('code-italic'), 'no itatic class found after switch to font 10 (blackletter) with ANSI code 20m');
750
assert(font10.classList.contains('code-font-10'), 'font 10 class NOT found after switch to font 10 with ANSI code 20m');
751
},
752
(italicAndBlackletterOff) => {
753
assert.strictEqual(0, italicAndBlackletterOff.classList.length, 'italic or blackletter (font10) class found after both switched off with ANSI code 23m');
754
},
755
(font10) => {
756
assert.strictEqual(1, font10.classList.length);
757
assert(font10.classList.contains('code-font-10'), 'font 10 class NOT found after switch to font 10 with ANSI code 20m');
758
},
759
(font1) => {
760
assert.strictEqual(1, font1.classList.length);
761
assert(font1.classList.contains('code-font-10') === false, 'font 10 class found after switch to font 1 with ANSI code 11m');
762
assert(font1.classList.contains('code-font-1'), 'font 1 class NOT found after switch to font 1 with ANSI code 11m');
763
},
764
(defaultfont) => {
765
assert.strictEqual(0, defaultfont.classList.length, 'One or more font style classes (2nd series) still found after reset to default font with ANSI code 10m.');
766
},
767
], 6);
768
769
// italic can be turned on/off with affecting font codes 1-9 (italic off will clear 'blackletter'(font 23) as per spec)
770
assertMultipleSequenceElements('\x1b[3mitalic\x1b[12mfont2\x1b[23mitalicOff\x1b[3mitalicFont2\x1b[10mjustitalic\x1b[23mnothing', [
771
(italic) => {
772
assert.strictEqual(1, italic.classList.length);
773
assert(italic.classList.contains('code-italic'), 'italic class NOT found after italic code ANSI code 3m');
774
},
775
(font10) => {
776
assert.strictEqual(2, font10.classList.length);
777
assert(font10.classList.contains('code-italic'), 'no itatic class found after switch to font 2 with ANSI code 12m');
778
assert(font10.classList.contains('code-font-2'), 'font 2 class NOT found after switch to font 2 with ANSI code 12m');
779
},
780
(italicOff) => {
781
assert.strictEqual(1, italicOff.classList.length, 'italic class found after both switched off with ANSI code 23m');
782
assert(italicOff.classList.contains('code-italic') === false, 'itatic class found after switching it OFF with ANSI code 23m');
783
assert(italicOff.classList.contains('code-font-2'), 'font 2 class NOT found after switching italic off with ANSI code 23m');
784
},
785
(italicFont2) => {
786
assert.strictEqual(2, italicFont2.classList.length);
787
assert(italicFont2.classList.contains('code-italic'), 'no itatic class found after italic ANSI code 3m');
788
assert(italicFont2.classList.contains('code-font-2'), 'font 2 class NOT found after italic ANSI code 3m');
789
},
790
(justitalic) => {
791
assert.strictEqual(1, justitalic.classList.length);
792
assert(justitalic.classList.contains('code-font-2') === false, 'font 2 class found after switch to default font with ANSI code 10m');
793
assert(justitalic.classList.contains('code-italic'), 'italic class NOT found after switch to default font with ANSI code 10m');
794
},
795
(nothing) => {
796
assert.strictEqual(0, nothing.classList.length, 'One or more classes still found after final italic removal with ANSI code 23m.');
797
},
798
], 6);
799
800
// Reverse video reverses Foreground/Background colors WITH both SET and can called in sequence
801
assertMultipleSequenceElements('\x1b[38;2;10;20;30mfg10,20,30\x1b[48;2;167;168;169mbg167,168,169\x1b[7m8ReverseVideo\x1b[7mDuplicateReverseVideo\x1b[27mReverseOff\x1b[27mDupReverseOff', [
802
(fg10_20_30) => {
803
assert.strictEqual(1, fg10_20_30.classList.length, 'Foreground ANSI color code should add one class.');
804
assert(fg10_20_30.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');
805
assertInlineColor(fg10_20_30, 'foreground', new RGBA(10, 20, 30), '24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');
806
},
807
(bg167_168_169) => {
808
assert.strictEqual(2, bg167_168_169.classList.length, 'background ANSI color codes should only add a single class.');
809
assert(bg167_168_169.classList.contains('code-background-colored'), 'Background ANSI color codes should add custom background color class.');
810
assertInlineColor(bg167_168_169, 'background', new RGBA(167, 168, 169), '24-bit RGBA ANSI background color code (167,168,169) should add matching color inline style.');
811
assert(bg167_168_169.classList.contains('code-foreground-colored'), 'Still Foreground ANSI color codes should add custom foreground color class.');
812
assertInlineColor(bg167_168_169, 'foreground', new RGBA(10, 20, 30), 'Still 24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');
813
},
814
(reverseVideo) => {
815
assert.strictEqual(2, reverseVideo.classList.length, 'background ANSI color codes should only add a single class.');
816
assert(reverseVideo.classList.contains('code-background-colored'), 'Background ANSI color codes should add custom background color class.');
817
assertInlineColor(reverseVideo, 'foreground', new RGBA(167, 168, 169), 'Reversed 24-bit RGBA ANSI foreground color code (167,168,169) should add matching former background color inline style.');
818
assert(reverseVideo.classList.contains('code-foreground-colored'), 'Still Foreground ANSI color codes should add custom foreground color class.');
819
assertInlineColor(reverseVideo, 'background', new RGBA(10, 20, 30), 'Reversed 24-bit RGBA ANSI background color code (10,20,30) should add matching former foreground color inline style.');
820
},
821
(dupReverseVideo) => {
822
assert.strictEqual(2, dupReverseVideo.classList.length, 'After second Reverse Video - background ANSI color codes should only add a single class.');
823
assert(dupReverseVideo.classList.contains('code-background-colored'), 'After second Reverse Video - Background ANSI color codes should add custom background color class.');
824
assertInlineColor(dupReverseVideo, 'foreground', new RGBA(167, 168, 169), 'After second Reverse Video - Reversed 24-bit RGBA ANSI foreground color code (167,168,169) should add matching former background color inline style.');
825
assert(dupReverseVideo.classList.contains('code-foreground-colored'), 'After second Reverse Video - Still Foreground ANSI color codes should add custom foreground color class.');
826
assertInlineColor(dupReverseVideo, 'background', new RGBA(10, 20, 30), 'After second Reverse Video - Reversed 24-bit RGBA ANSI background color code (10,20,30) should add matching former foreground color inline style.');
827
},
828
(reversedBack) => {
829
assert.strictEqual(2, reversedBack.classList.length, 'Reversed Back - background ANSI color codes should only add a single class.');
830
assert(reversedBack.classList.contains('code-background-colored'), 'Reversed Back - Background ANSI color codes should add custom background color class.');
831
assertInlineColor(reversedBack, 'background', new RGBA(167, 168, 169), 'Reversed Back - 24-bit RGBA ANSI background color code (167,168,169) should add matching color inline style.');
832
assert(reversedBack.classList.contains('code-foreground-colored'), 'Reversed Back - Foreground ANSI color codes should add custom foreground color class.');
833
assertInlineColor(reversedBack, 'foreground', new RGBA(10, 20, 30), 'Reversed Back - 24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');
834
},
835
(dupReversedBack) => {
836
assert.strictEqual(2, dupReversedBack.classList.length, '2nd Reversed Back - background ANSI color codes should only add a single class.');
837
assert(dupReversedBack.classList.contains('code-background-colored'), '2nd Reversed Back - Background ANSI color codes should add custom background color class.');
838
assertInlineColor(dupReversedBack, 'background', new RGBA(167, 168, 169), '2nd Reversed Back - 24-bit RGBA ANSI background color code (167,168,169) should add matching color inline style.');
839
assert(dupReversedBack.classList.contains('code-foreground-colored'), '2nd Reversed Back - Foreground ANSI color codes should add custom foreground color class.');
840
assertInlineColor(dupReversedBack, 'foreground', new RGBA(10, 20, 30), '2nd Reversed Back - 24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');
841
},
842
], 6);
843
844
// Reverse video reverses Foreground/Background colors WITH ONLY foreground color SET
845
assertMultipleSequenceElements('\x1b[38;2;10;20;30mfg10,20,30\x1b[7m8ReverseVideo\x1b[27mReverseOff', [
846
(fg10_20_30) => {
847
assert.strictEqual(1, fg10_20_30.classList.length, 'Foreground ANSI color code should add one class.');
848
assert(fg10_20_30.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');
849
assertInlineColor(fg10_20_30, 'foreground', new RGBA(10, 20, 30), '24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');
850
},
851
(reverseVideo) => {
852
assert.strictEqual(1, reverseVideo.classList.length, 'Background ANSI color codes should only add a single class.');
853
assert(reverseVideo.classList.contains('code-background-colored'), 'Background ANSI color codes should add custom background color class.');
854
assert(reverseVideo.classList.contains('code-foreground-colored') === false, 'After Reverse with NO background the Foreground ANSI color codes should NOT BE SET.');
855
assertInlineColor(reverseVideo, 'background', new RGBA(10, 20, 30), 'Reversed 24-bit RGBA ANSI background color code (10,20,30) should add matching former foreground color inline style.');
856
},
857
(reversedBack) => {
858
assert.strictEqual(1, reversedBack.classList.length, 'Reversed Back - background ANSI color codes should only add a single class.');
859
assert(reversedBack.classList.contains('code-background-colored') === false, 'AFTER Reversed Back - Background ANSI color should NOT BE SET.');
860
assert(reversedBack.classList.contains('code-foreground-colored'), 'Reversed Back - Foreground ANSI color codes should add custom foreground color class.');
861
assertInlineColor(reversedBack, 'foreground', new RGBA(10, 20, 30), 'Reversed Back - 24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');
862
},
863
], 3);
864
865
// Reverse video reverses Foreground/Background colors WITH ONLY background color SET
866
assertMultipleSequenceElements('\x1b[48;2;167;168;169mbg167,168,169\x1b[7m8ReverseVideo\x1b[27mReverseOff', [
867
(bg167_168_169) => {
868
assert.strictEqual(1, bg167_168_169.classList.length, 'Background ANSI color code should add one class.');
869
assert(bg167_168_169.classList.contains('code-background-colored'), 'Background ANSI color codes should add custom foreground color class.');
870
assertInlineColor(bg167_168_169, 'background', new RGBA(167, 168, 169), '24-bit RGBA ANSI color code (167, 168, 169) should add matching background color inline style.');
871
},
872
(reverseVideo) => {
873
assert.strictEqual(1, reverseVideo.classList.length, 'After ReverseVideo Foreground ANSI color codes should only add a single class.');
874
assert(reverseVideo.classList.contains('code-foreground-colored'), 'After ReverseVideo Foreground ANSI color codes should add custom background color class.');
875
assert(reverseVideo.classList.contains('code-background-colored') === false, 'After Reverse with NO foreground color the background ANSI color codes should BE SET.');
876
assertInlineColor(reverseVideo, 'foreground', new RGBA(167, 168, 169), 'Reversed 24-bit RGBA ANSI background color code (10,20,30) should add matching former background color inline style.');
877
},
878
(reversedBack) => {
879
assert.strictEqual(1, reversedBack.classList.length, 'Reversed Back - background ANSI color codes should only add a single class.');
880
assert(reversedBack.classList.contains('code-foreground-colored') === false, 'AFTER Reversed Back - Foreground ANSI color should NOT BE SET.');
881
assert(reversedBack.classList.contains('code-background-colored'), 'Reversed Back - Background ANSI color codes should add custom background color class.');
882
assertInlineColor(reversedBack, 'background', new RGBA(167, 168, 169), 'Reversed Back - 24-bit RGBA ANSI color code (10,20,30) should add matching background color inline style.');
883
},
884
], 3);
885
886
// Underline color Different types of color codes still cancel each other
887
assertMultipleSequenceElements('\x1b[58;2;101;102;103m24bitUnderline101,102,103\x1b[58;5;3m8bitsimpleUnderline\x1b[58;2;104;105;106m24bitUnderline104,105,106\x1b[58;5;101m8bitadvanced\x1b[58;2;200;200;200munderline200,200,200\x1b[59mUnderlineColorResetToDefault', [
888
(adv24Bit) => {
889
assert.strictEqual(1, adv24Bit.classList.length, 'Underline ANSI color codes should only add a single class (1).');
890
assert(adv24Bit.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');
891
assertInlineColor(adv24Bit, 'underline', new RGBA(101, 102, 103), '24-bit RGBA ANSI color code (101,102,103) should add matching color inline style.');
892
},
893
(adv8BitSimple) => {
894
assert.strictEqual(1, adv8BitSimple.classList.length, 'Multiple underline ANSI color codes should only add a single class (2).');
895
assert(adv8BitSimple.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');
896
// changed to simple theme color, don't know exactly what it should be, but it should NO LONGER BE 101,102,103
897
assertInlineColor(adv8BitSimple, 'underline', new RGBA(101, 102, 103), 'Change to theme color SHOULD NOT STILL BE 24-bit RGBA ANSI color code (101,102,103) should add matching color inline style.', false);
898
},
899
(adv24BitAgain) => {
900
assert.strictEqual(1, adv24BitAgain.classList.length, 'Multiple underline ANSI color codes should only add a single class (3).');
901
assert(adv24BitAgain.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');
902
assertInlineColor(adv24BitAgain, 'underline', new RGBA(104, 105, 106), '24-bit RGBA ANSI color code (100,100,100) should add matching color inline style.');
903
},
904
(adv8BitAdvanced) => {
905
assert.strictEqual(1, adv8BitAdvanced.classList.length, 'Multiple underline ANSI color codes should only add a single class (4).');
906
assert(adv8BitAdvanced.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');
907
// changed to 8bit advanced color, don't know exactly what it should be, but it should NO LONGER BE 104,105,106
908
assertInlineColor(adv8BitAdvanced, 'underline', new RGBA(104, 105, 106), 'Change to theme color SHOULD NOT BE 24-bit RGBA ANSI color code (104,105,106) should add matching color inline style.', false);
909
},
910
(adv24BitUnderlin200) => {
911
assert.strictEqual(1, adv24BitUnderlin200.classList.length, 'Multiple underline ANSI color codes should only add a single class 4.');
912
assert(adv24BitUnderlin200.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');
913
assertInlineColor(adv24BitUnderlin200, 'underline', new RGBA(200, 200, 200), 'after change underline color SHOULD BE 24-bit RGBA ANSI color code (200,200,200) should add matching color inline style.');
914
},
915
(underlineColorResetToDefault) => {
916
assert.strictEqual(0, underlineColorResetToDefault.classList.length, 'After Underline Color reset to default NO underline color class should be set.');
917
assertInlineColor(underlineColorResetToDefault, 'underline', undefined, 'after RESET TO DEFAULT underline color SHOULD NOT BE SET (no color inline style.)');
918
},
919
], 6);
920
921
// Different types of color codes still cancel each other
922
assertMultipleSequenceElements('\x1b[34msimple\x1b[38;2;101;102;103m24bit\x1b[38;5;3m8bitsimple\x1b[38;2;104;105;106m24bitAgain\x1b[38;5;101m8bitadvanced', [
923
(simple) => {
924
assert.strictEqual(1, simple.classList.length, 'Foreground ANSI color code should add one class.');
925
assert(simple.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');
926
},
927
(adv24Bit) => {
928
assert.strictEqual(1, adv24Bit.classList.length, 'Multiple foreground ANSI color codes should only add a single class.');
929
assert(adv24Bit.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');
930
assertInlineColor(adv24Bit, 'foreground', new RGBA(101, 102, 103), '24-bit RGBA ANSI color code (101,102,103) should add matching color inline style.');
931
},
932
(adv8BitSimple) => {
933
assert.strictEqual(1, adv8BitSimple.classList.length, 'Multiple foreground ANSI color codes should only add a single class.');
934
assert(adv8BitSimple.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');
935
//color is theme based, so we can't check what it should be but we know it should NOT BE 101,102,103 anymore
936
assertInlineColor(adv8BitSimple, 'foreground', new RGBA(101, 102, 103), 'SHOULD NOT LONGER BE 24-bit RGBA ANSI color code (101,102,103) after simple color change.', false);
937
},
938
(adv24BitAgain) => {
939
assert.strictEqual(1, adv24BitAgain.classList.length, 'Multiple foreground ANSI color codes should only add a single class.');
940
assert(adv24BitAgain.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');
941
assertInlineColor(adv24BitAgain, 'foreground', new RGBA(104, 105, 106), '24-bit RGBA ANSI color code (104,105,106) should add matching color inline style.');
942
},
943
(adv8BitAdvanced) => {
944
assert.strictEqual(1, adv8BitAdvanced.classList.length, 'Multiple foreground ANSI color codes should only add a single class.');
945
assert(adv8BitAdvanced.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');
946
// color should NO LONGER BE 104,105,106
947
assertInlineColor(adv8BitAdvanced, 'foreground', new RGBA(104, 105, 106), 'SHOULD NOT LONGER BE 24-bit RGBA ANSI color code (104,105,106) after advanced color change.', false);
948
}
949
], 5);
950
951
});
952
953
/**
954
* Assert that the provided ANSI sequence exactly matches the text content of the resulting
955
* {@link HTMLSpanElement}.
956
*
957
* @param sequence The ANSI sequence to verify.
958
*/
959
function assertSequencestrictEqualToContent(sequence: string): void {
960
const child: HTMLSpanElement = getSequenceOutput(sequence);
961
assert(child.textContent === sequence);
962
}
963
964
test('Invalid codes treated as regular text', () => {
965
966
// Individual components of ANSI code start are printed
967
assertSequencestrictEqualToContent('\x1b');
968
assertSequencestrictEqualToContent('[');
969
970
// Unsupported sequence prints both characters
971
assertSequencestrictEqualToContent('\x1b[');
972
973
// Random strings are displayed properly
974
for (let i = 0; i < 50; i++) {
975
const uuid: string = generateUuid();
976
assertSequencestrictEqualToContent(uuid);
977
}
978
979
});
980
981
/**
982
* Assert that a given ANSI sequence maintains added content following the ANSI code, and that
983
* the expression itself is thrown away.
984
*
985
* @param sequence The ANSI sequence to verify. The provided sequence should contain ANSI codes
986
* only, and should not include actual text content as it is provided by this function.
987
*/
988
function assertEmptyOutput(sequence: string) {
989
const child: HTMLSpanElement = getSequenceOutput(sequence + 'content');
990
assert.strictEqual('content', child.textContent);
991
assert.strictEqual(0, child.classList.length);
992
}
993
994
test('Empty sequence output', () => {
995
996
const sequences: string[] = [
997
// No colour codes
998
'',
999
'\x1b[;m',
1000
'\x1b[1;;m',
1001
'\x1b[m',
1002
'\x1b[99m'
1003
];
1004
1005
sequences.forEach(sequence => {
1006
assertEmptyOutput(sequence);
1007
});
1008
1009
// Check other possible ANSI terminators
1010
const terminators: string[] = 'ABCDHIJKfhmpsu'.split('');
1011
1012
terminators.forEach(terminator => {
1013
assertEmptyOutput('\x1b[content' + terminator);
1014
});
1015
1016
});
1017
1018
test('calcANSI8bitColor', () => {
1019
// Invalid values
1020
// Negative (below range), simple range, decimals
1021
for (let i = -10; i <= 15; i += 0.5) {
1022
assert(calcANSI8bitColor(i) === undefined, 'Values less than 16 passed to calcANSI8bitColor should return undefined.');
1023
}
1024
// In-range range decimals
1025
for (let i = 16.5; i < 254; i += 1) {
1026
assert(calcANSI8bitColor(i) === undefined, 'Floats passed to calcANSI8bitColor should return undefined.');
1027
}
1028
// Above range
1029
for (let i = 256; i < 300; i += 0.5) {
1030
assert(calcANSI8bitColor(i) === undefined, 'Values grather than 255 passed to calcANSI8bitColor should return undefined.');
1031
}
1032
1033
// All valid colors
1034
for (let red = 0; red <= 5; red++) {
1035
for (let green = 0; green <= 5; green++) {
1036
for (let blue = 0; blue <= 5; blue++) {
1037
const colorOut: any = calcANSI8bitColor(16 + red * 36 + green * 6 + blue);
1038
assert(colorOut.r === Math.round(red * (255 / 5)), 'Incorrect red value encountered for color');
1039
assert(colorOut.g === Math.round(green * (255 / 5)), 'Incorrect green value encountered for color');
1040
assert(colorOut.b === Math.round(blue * (255 / 5)), 'Incorrect balue value encountered for color');
1041
}
1042
}
1043
}
1044
1045
// All grays
1046
for (let i = 232; i <= 255; i++) {
1047
const grayOut: any = calcANSI8bitColor(i);
1048
assert(grayOut.r === grayOut.g);
1049
assert(grayOut.r === grayOut.b);
1050
assert(grayOut.r === Math.round((i - 232) / 23 * 255));
1051
}
1052
});
1053
1054
});
1055
1056