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