Path: blob/main/src/vs/workbench/contrib/debug/test/browser/debugANSIHandling.test.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { isHTMLSpanElement } from '../../../../../base/browser/dom.js';7import { Color, RGBA } from '../../../../../base/common/color.js';8import { DisposableStore } from '../../../../../base/common/lifecycle.js';9import { generateUuid } from '../../../../../base/common/uuid.js';10import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';11import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';12import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';13import { registerColors } from '../../../terminal/common/terminalColorRegistry.js';14import { appendStylizedStringToContainer, calcANSI8bitColor, handleANSIOutput } from '../../browser/debugANSIHandling.js';15import { DebugSession } from '../../browser/debugSession.js';16import { LinkDetector } from '../../browser/linkDetector.js';17import { DebugModel } from '../../common/debugModel.js';18import { createTestSession } from './callStack.test.js';19import { createMockDebugModel } from './mockDebugModel.js';2021suite('Debug - ANSI Handling', () => {2223let disposables: DisposableStore;24let model: DebugModel;25let session: DebugSession;26let linkDetector: LinkDetector;2728/**29* Instantiate services for use by the functions being tested.30*/31setup(() => {32disposables = new DisposableStore();33model = createMockDebugModel(disposables);34session = createTestSession(model);3536const instantiationService: TestInstantiationService = <TestInstantiationService>workbenchInstantiationService(undefined, disposables);37linkDetector = instantiationService.createInstance(LinkDetector);38registerColors();39});4041teardown(() => {42disposables.dispose();43});4445ensureNoDisposablesAreLeakedInTestSuite();4647test('appendStylizedStringToContainer', () => {48const root: HTMLSpanElement = document.createElement('span');49let child: Node;5051assert.strictEqual(0, root.children.length);5253appendStylizedStringToContainer(root, 'content1', ['class1', 'class2'], linkDetector, session.root, undefined, undefined, undefined, undefined, 0);54appendStylizedStringToContainer(root, 'content2', ['class2', 'class3'], linkDetector, session.root, undefined, undefined, undefined, undefined, 0);5556assert.strictEqual(2, root.children.length);5758child = root.firstChild!;59if (isHTMLSpanElement(child)) {60assert.strictEqual('content1', child.textContent);61assert(child.classList.contains('class1'));62assert(child.classList.contains('class2'));63} else {64assert.fail('Unexpected assertion error');65}6667child = root.lastChild!;68if (isHTMLSpanElement(child)) {69assert.strictEqual('content2', child.textContent);70assert(child.classList.contains('class2'));71assert(child.classList.contains('class3'));72} else {73assert.fail('Unexpected assertion error');74}75});7677/**78* Apply an ANSI sequence to {@link #getSequenceOutput}.79*80* @param sequence The ANSI sequence to stylize.81* @returns An {@link HTMLSpanElement} that contains the stylized text.82*/83function getSequenceOutput(sequence: string): HTMLSpanElement {84const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, session.root, []);85assert.strictEqual(1, root.children.length);86const child: Node = root.lastChild!;87if (isHTMLSpanElement(child)) {88return child;89} else {90assert.fail('Unexpected assertion error');91}92}9394/**95* Assert that a given ANSI sequence maintains added content following the ANSI code, and that96* the provided {@param assertion} passes.97*98* @param sequence The ANSI sequence to verify. The provided sequence should contain ANSI codes99* only, and should not include actual text content as it is provided by this function.100* @param assertion The function used to verify the output.101*/102function assertSingleSequenceElement(sequence: string, assertion: (child: HTMLSpanElement) => void): void {103const child: HTMLSpanElement = getSequenceOutput(sequence + 'content');104assert.strictEqual('content', child.textContent);105assertion(child);106}107108/**109* Assert that a given DOM element has the custom inline CSS style matching110* the color value provided.111* @param element The HTML span element to look at.112* @param colorType If `foreground`, will check the element's css `color`;113* if `background`, will check the element's css `backgroundColor`.114* if `underline`, will check the elements css `textDecorationColor`.115* @param color RGBA object to compare color to. If `undefined` or not provided,116* will assert that no value is set.117* @param message Optional custom message to pass to assertion.118* @param colorShouldMatch Optional flag (defaults TO true) which allows caller to indicate that the color SHOULD NOT MATCH119* (for testing changes to theme colors where we need color to have changed but we don't know exact color it should have120* changed to (but we do know the color it should NO LONGER BE))121*/122function assertInlineColor(element: HTMLSpanElement, colorType: 'background' | 'foreground' | 'underline', color?: RGBA | undefined, message?: string, colorShouldMatch: boolean = true): void {123if (color !== undefined) {124const cssColor = Color.Format.CSS.formatRGB(125new Color(color)126);127if (colorType === 'background') {128const styleBefore = element.style.backgroundColor;129element.style.backgroundColor = cssColor;130assert((styleBefore === element.style.backgroundColor) === colorShouldMatch, message || `Incorrect ${colorType} color style found (found color: ${styleBefore}, expected ${cssColor}).`);131} else if (colorType === 'foreground') {132const styleBefore = element.style.color;133element.style.color = cssColor;134assert((styleBefore === element.style.color) === colorShouldMatch, message || `Incorrect ${colorType} color style found (found color: ${styleBefore}, expected ${cssColor}).`);135} else {136const styleBefore = element.style.textDecorationColor;137element.style.textDecorationColor = cssColor;138assert((styleBefore === element.style.textDecorationColor) === colorShouldMatch, message || `Incorrect ${colorType} color style found (found color: ${styleBefore}, expected ${cssColor}).`);139}140} else {141if (colorType === 'background') {142assert(!element.style.backgroundColor, message || `Defined ${colorType} color style found when it should not have been defined`);143} else if (colorType === 'foreground') {144assert(!element.style.color, message || `Defined ${colorType} color style found when it should not have been defined`);145} else {146assert(!element.style.textDecorationColor, message || `Defined ${colorType} color style found when it should not have been defined`);147}148}149150}151152test('Expected single sequence operation', () => {153154// Bold code155assertSingleSequenceElement('\x1b[1m', (child) => {156assert(child.classList.contains('code-bold'), 'Bold formatting not detected after bold ANSI code.');157});158159// Italic code160assertSingleSequenceElement('\x1b[3m', (child) => {161assert(child.classList.contains('code-italic'), 'Italic formatting not detected after italic ANSI code.');162});163164// Underline code165assertSingleSequenceElement('\x1b[4m', (child) => {166assert(child.classList.contains('code-underline'), 'Underline formatting not detected after underline ANSI code.');167});168169for (let i = 30; i <= 37; i++) {170const customClassName: string = 'code-foreground-colored';171172// Foreground colour class173assertSingleSequenceElement('\x1b[' + i + 'm', (child) => {174assert(child.classList.contains(customClassName), `Custom foreground class not found on element after foreground ANSI code #${i}.`);175});176177// Cancellation code removes colour class178assertSingleSequenceElement('\x1b[' + i + ';39m', (child) => {179assert(child.classList.contains(customClassName) === false, 'Custom foreground class still found after foreground cancellation code.');180assertInlineColor(child, 'foreground', undefined, 'Custom color style still found after foreground cancellation code.');181});182}183184for (let i = 40; i <= 47; i++) {185const customClassName: string = 'code-background-colored';186187// Foreground colour class188assertSingleSequenceElement('\x1b[' + i + 'm', (child) => {189assert(child.classList.contains(customClassName), `Custom background class not found on element after background ANSI code #${i}.`);190});191192// Cancellation code removes colour class193assertSingleSequenceElement('\x1b[' + i + ';49m', (child) => {194assert(child.classList.contains(customClassName) === false, 'Custom background class still found after background cancellation code.');195assertInlineColor(child, 'foreground', undefined, 'Custom color style still found after background cancellation code.');196});197}198199// check all basic colors for underlines (full range is checked elsewhere, here we check cancelation)200for (let i = 0; i <= 255; i++) {201const customClassName: string = 'code-underline-colored';202203// Underline colour class204assertSingleSequenceElement('\x1b[58;5;' + i + 'm', (child) => {205assert(child.classList.contains(customClassName), `Custom underline color class not found on element after underline color ANSI code 58;5;${i}m.`);206});207208// Cancellation underline color code removes colour class209assertSingleSequenceElement('\x1b[58;5;' + i + 'm\x1b[59m', (child) => {210assert(child.classList.contains(customClassName) === false, 'Custom underline color class still found after underline color cancellation code 59m.');211assertInlineColor(child, 'underline', undefined, 'Custom underline color style still found after underline color cancellation code 59m.');212});213}214215// Different codes do not cancel each other216assertSingleSequenceElement('\x1b[1;3;4;30;41m', (child) => {217assert.strictEqual(5, child.classList.length, 'Incorrect number of classes found for different ANSI codes.');218219assert(child.classList.contains('code-bold'));220assert(child.classList.contains('code-italic'), 'Different ANSI codes should not cancel each other.');221assert(child.classList.contains('code-underline'), 'Different ANSI codes should not cancel each other.');222assert(child.classList.contains('code-foreground-colored'), 'Different ANSI codes should not cancel each other.');223assert(child.classList.contains('code-background-colored'), 'Different ANSI codes should not cancel each other.');224});225226// Different codes do not ACCUMULATE more than one copy of each class227assertSingleSequenceElement('\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) => {228assert(child.classList.contains('code-bold'));229assert(child.classList.contains('code-italic'), 'italic missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');230assert(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.');231assert(child.classList.contains('code-dim'), 'dim missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');232assert(child.classList.contains('code-blink'), 'blink missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');233assert(child.classList.contains('code-rapid-blink'), 'rapid blink mkssing Doubles of each Different ANSI codes should not cancel each other or accumulate.');234assert(child.classList.contains('code-double-underline'), 'double underline missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');235assert(child.classList.contains('code-hidden'), 'hidden missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');236assert(child.classList.contains('code-strike-through'), 'strike-through missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');237assert(child.classList.contains('code-overline'), 'overline missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');238assert(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.');239assert(child.classList.contains('code-subscript'), 'subscript missing Doubles of each Different ANSI codes should not cancel each other or accumulate.');240241assert.strictEqual(10, child.classList.length, 'Incorrect number of classes found for each style code sent twice ANSI codes.');242});243244245246// More Different codes do not cancel each other247assertSingleSequenceElement('\x1b[1;2;5;6;21;8;9m', (child) => {248assert.strictEqual(7, child.classList.length, 'Incorrect number of classes found for different ANSI codes.');249250assert(child.classList.contains('code-bold'));251assert(child.classList.contains('code-dim'), 'Different ANSI codes should not cancel each other.');252assert(child.classList.contains('code-blink'), 'Different ANSI codes should not cancel each other.');253assert(child.classList.contains('code-rapid-blink'), 'Different ANSI codes should not cancel each other.');254assert(child.classList.contains('code-double-underline'), 'Different ANSI codes should not cancel each other.');255assert(child.classList.contains('code-hidden'), 'Different ANSI codes should not cancel each other.');256assert(child.classList.contains('code-strike-through'), 'Different ANSI codes should not cancel each other.');257});258259260261// New foreground codes don't remove old background codes and vice versa262assertSingleSequenceElement('\x1b[40;31;42;33m', (child) => {263assert.strictEqual(2, child.classList.length);264265assert(child.classList.contains('code-background-colored'), 'New foreground ANSI code should not cancel existing background formatting.');266assert(child.classList.contains('code-foreground-colored'), 'New background ANSI code should not cancel existing foreground formatting.');267});268269// Duplicate codes do not change output270assertSingleSequenceElement('\x1b[1;1;4;1;4;4;1;4m', (child) => {271assert(child.classList.contains('code-bold'), 'Duplicate formatting codes should have no effect.');272assert(child.classList.contains('code-underline'), 'Duplicate formatting codes should have no effect.');273});274275// Extra terminating semicolon does not change output276assertSingleSequenceElement('\x1b[1;4;m', (child) => {277assert(child.classList.contains('code-bold'), 'Extra semicolon after ANSI codes should have no effect.');278assert(child.classList.contains('code-underline'), 'Extra semicolon after ANSI codes should have no effect.');279});280281// Cancellation code removes multiple codes282assertSingleSequenceElement('\x1b[1;4;30;41;32;43;34;45;36;47;0m', (child) => {283assert.strictEqual(0, child.classList.length, 'Cancellation ANSI code should clear ALL formatting.');284assertInlineColor(child, 'background', undefined, 'Cancellation ANSI code should clear ALL formatting.');285assertInlineColor(child, 'foreground', undefined, 'Cancellation ANSI code should clear ALL formatting.');286});287288});289290test('Expected single 8-bit color sequence operation', () => {291// Basic and bright color codes specified with 8-bit color code format292for (let i = 0; i <= 15; i++) {293// As these are controlled by theme, difficult to check actual color value294// Foreground codes should add standard classes295assertSingleSequenceElement('\x1b[38;5;' + i + 'm', (child) => {296assert(child.classList.contains('code-foreground-colored'), `Custom color class not found after foreground 8-bit color code 38;5;${i}`);297});298299// Background codes should add standard classes300assertSingleSequenceElement('\x1b[48;5;' + i + 'm', (child) => {301assert(child.classList.contains('code-background-colored'), `Custom color class not found after background 8-bit color code 48;5;${i}`);302});303}304305// 8-bit advanced colors306for (let i = 16; i <= 255; i++) {307// Foreground codes should add custom class and inline style308assertSingleSequenceElement('\x1b[38;5;' + i + 'm', (child) => {309assert(child.classList.contains('code-foreground-colored'), `Custom color class not found after foreground 8-bit color code 38;5;${i}`);310assertInlineColor(child, 'foreground', (calcANSI8bitColor(i) as RGBA), `Incorrect or no color styling found after foreground 8-bit color code 38;5;${i}`);311});312313// Background codes should add custom class and inline style314assertSingleSequenceElement('\x1b[48;5;' + i + 'm', (child) => {315assert(child.classList.contains('code-background-colored'), `Custom color class not found after background 8-bit color code 48;5;${i}`);316assertInlineColor(child, 'background', (calcANSI8bitColor(i) as RGBA), `Incorrect or no color styling found after background 8-bit color code 48;5;${i}`);317});318319// Color underline codes should add custom class and inline style320assertSingleSequenceElement('\x1b[58;5;' + i + 'm', (child) => {321assert(child.classList.contains('code-underline-colored'), `Custom color class not found after underline 8-bit color code 58;5;${i}`);322assertInlineColor(child, 'underline', (calcANSI8bitColor(i) as RGBA), `Incorrect or no color styling found after underline 8-bit color code 58;5;${i}`);323});324}325326// Bad (nonexistent) color should not render327assertSingleSequenceElement('\x1b[48;5;300m', (child) => {328assert.strictEqual(0, child.classList.length, 'Bad ANSI color codes should have no effect.');329});330331// Should ignore any codes after the ones needed to determine color332assertSingleSequenceElement('\x1b[48;5;100;42;77;99;4;24m', (child) => {333assert(child.classList.contains('code-background-colored'));334assert.strictEqual(1, child.classList.length);335assertInlineColor(child, 'background', (calcANSI8bitColor(100) as RGBA));336});337});338339test('Expected single 24-bit color sequence operation', () => {340// 24-bit advanced colors341for (let r = 0; r <= 255; r += 64) {342for (let g = 0; g <= 255; g += 64) {343for (let b = 0; b <= 255; b += 64) {344const color = new RGBA(r, g, b);345// Foreground codes should add class and inline style346assertSingleSequenceElement(`\x1b[38;2;${r};${g};${b}m`, (child) => {347assert(child.classList.contains('code-foreground-colored'), 'DOM should have "code-foreground-colored" class for advanced ANSI colors.');348assertInlineColor(child, 'foreground', color);349});350351// Background codes should add class and inline style352assertSingleSequenceElement(`\x1b[48;2;${r};${g};${b}m`, (child) => {353assert(child.classList.contains('code-background-colored'), 'DOM should have "code-foreground-colored" class for advanced ANSI colors.');354assertInlineColor(child, 'background', color);355});356357// Underline color codes should add class and inline style358assertSingleSequenceElement(`\x1b[58;2;${r};${g};${b}m`, (child) => {359assert(child.classList.contains('code-underline-colored'), 'DOM should have "code-underline-colored" class for advanced ANSI colors.');360assertInlineColor(child, 'underline', color);361});362}363}364}365366// Invalid color should not render367assertSingleSequenceElement('\x1b[38;2;4;4m', (child) => {368assert.strictEqual(0, child.classList.length, `Invalid color code "38;2;4;4" should not add a class (classes found: ${child.classList}).`);369assert(!child.style.color, `Invalid color code "38;2;4;4" should not add a custom color CSS (found color: ${child.style.color}).`);370});371372// Bad (nonexistent) color should not render373assertSingleSequenceElement('\x1b[48;2;150;300;5m', (child) => {374assert.strictEqual(0, child.classList.length, `Nonexistent color code "48;2;150;300;5" should not add a class (classes found: ${child.classList}).`);375});376377// Should ignore any codes after the ones needed to determine color378assertSingleSequenceElement('\x1b[48;2;100;42;77;99;200;75m', (child) => {379assert(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".`);380assert.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}).`);381assertInlineColor(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).`);382});383});384385386/**387* Assert that a given ANSI sequence produces the expected number of {@link HTMLSpanElement} children. For388* each child, run the provided assertion.389*390* @param sequence The ANSI sequence to verify.391* @param assertions A set of assertions to run on the resulting children.392*/393function assertMultipleSequenceElements(sequence: string, assertions: Array<(child: HTMLSpanElement) => void>, elementsExpected?: number): void {394if (elementsExpected === undefined) {395elementsExpected = assertions.length;396}397const root: HTMLSpanElement = handleANSIOutput(sequence, linkDetector, session.root, []);398assert.strictEqual(elementsExpected, root.children.length);399for (let i = 0; i < elementsExpected; i++) {400const child: Node = root.children[i];401if (isHTMLSpanElement(child)) {402assertions[i](child);403} else {404assert.fail('Unexpected assertion error');405}406}407}408409test('Expected multiple sequence operation', () => {410411// Multiple codes affect the same text412assertSingleSequenceElement('\x1b[1m\x1b[3m\x1b[4m\x1b[32m', (child) => {413assert(child.classList.contains('code-bold'), 'Bold class not found after multiple different ANSI codes.');414assert(child.classList.contains('code-italic'), 'Italic class not found after multiple different ANSI codes.');415assert(child.classList.contains('code-underline'), 'Underline class not found after multiple different ANSI codes.');416assert(child.classList.contains('code-foreground-colored'), 'Foreground color class not found after multiple different ANSI codes.');417});418419// Consecutive codes do not affect previous ones420assertMultipleSequenceElements('\x1b[1mbold\x1b[32mgreen\x1b[4munderline\x1b[3mitalic\x1b[0mnothing', [421(bold) => {422assert.strictEqual(1, bold.classList.length);423assert(bold.classList.contains('code-bold'), 'Bold class not found after bold ANSI code.');424},425(green) => {426assert.strictEqual(2, green.classList.length);427assert(green.classList.contains('code-bold'), 'Bold class not found after both bold and color ANSI codes.');428assert(green.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');429},430(underline) => {431assert.strictEqual(3, underline.classList.length);432assert(underline.classList.contains('code-bold'), 'Bold class not found after bold, color, and underline ANSI codes.');433assert(underline.classList.contains('code-foreground-colored'), 'Color class not found after color and underline ANSI codes.');434assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code.');435},436(italic) => {437assert.strictEqual(4, italic.classList.length);438assert(italic.classList.contains('code-bold'), 'Bold class not found after bold, color, underline, and italic ANSI codes.');439assert(italic.classList.contains('code-foreground-colored'), 'Color class not found after color, underline, and italic ANSI codes.');440assert(italic.classList.contains('code-underline'), 'Underline class not found after underline and italic ANSI codes.');441assert(italic.classList.contains('code-italic'), 'Italic class not found after italic ANSI code.');442},443(nothing) => {444assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after reset ANSI code.');445},446], 5);447448// Consecutive codes with ENDING/OFF codes do not LEAVE affect previous ones449assertMultipleSequenceElements('\x1b[1mbold\x1b[22m\x1b[32mgreen\x1b[4munderline\x1b[24m\x1b[3mitalic\x1b[23mjustgreen\x1b[0mnothing', [450(bold) => {451assert.strictEqual(1, bold.classList.length);452assert(bold.classList.contains('code-bold'), 'Bold class not found after bold ANSI code.');453},454(green) => {455assert.strictEqual(1, green.classList.length);456assert(green.classList.contains('code-bold') === false, 'Bold class found after both bold WAS TURNED OFF with 22m');457assert(green.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');458},459(underline) => {460assert.strictEqual(2, underline.classList.length);461assert(underline.classList.contains('code-foreground-colored'), 'Color class not found after color and underline ANSI codes.');462assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code.');463},464(italic) => {465assert.strictEqual(2, italic.classList.length);466assert(italic.classList.contains('code-foreground-colored'), 'Color class not found after color, underline, and italic ANSI codes.');467assert(italic.classList.contains('code-underline') === false, 'Underline class found after underline WAS TURNED OFF with 24m');468assert(italic.classList.contains('code-italic'), 'Italic class not found after italic ANSI code.');469},470(justgreen) => {471assert.strictEqual(1, justgreen.classList.length);472assert(justgreen.classList.contains('code-italic') === false, 'Italic class found after italic WAS TURNED OFF with 23m');473assert(justgreen.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');474},475(nothing) => {476assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after reset ANSI code.');477},478], 6);479480// more Consecutive codes with ENDING/OFF codes do not LEAVE affect previous ones481assertMultipleSequenceElements('\x1b[2mdim\x1b[22m\x1b[32mgreen\x1b[5mslowblink\x1b[25m\x1b[6mrapidblink\x1b[25mjustgreen\x1b[0mnothing', [482(dim) => {483assert.strictEqual(1, dim.classList.length);484assert(dim.classList.contains('code-dim'), 'Dim class not found after dim ANSI code 2m.');485},486(green) => {487assert.strictEqual(1, green.classList.length);488assert(green.classList.contains('code-dim') === false, 'Dim class found after dim WAS TURNED OFF with 22m');489assert(green.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');490},491(slowblink) => {492assert.strictEqual(2, slowblink.classList.length);493assert(slowblink.classList.contains('code-foreground-colored'), 'Color class not found after color and blink ANSI codes.');494assert(slowblink.classList.contains('code-blink'), 'Blink class not found after underline ANSI code 5m.');495},496(rapidblink) => {497assert.strictEqual(2, rapidblink.classList.length);498assert(rapidblink.classList.contains('code-foreground-colored'), 'Color class not found after color, blink, and rapid blink ANSI codes.');499assert(rapidblink.classList.contains('code-blink') === false, 'blink class found after underline WAS TURNED OFF with 25m');500assert(rapidblink.classList.contains('code-rapid-blink'), 'Rapid blink class not found after rapid blink ANSI code 6m.');501},502(justgreen) => {503assert.strictEqual(1, justgreen.classList.length);504assert(justgreen.classList.contains('code-rapid-blink') === false, 'Rapid blink class found after rapid blink WAS TURNED OFF with 25m');505assert(justgreen.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');506},507(nothing) => {508assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after reset ANSI code.');509},510], 6);511512// more Consecutive codes with ENDING/OFF codes do not LEAVE affect previous ones513assertMultipleSequenceElements('\x1b[8mhidden\x1b[28m\x1b[32mgreen\x1b[9mcrossedout\x1b[29m\x1b[21mdoubleunderline\x1b[24mjustgreen\x1b[0mnothing', [514(hidden) => {515assert.strictEqual(1, hidden.classList.length);516assert(hidden.classList.contains('code-hidden'), 'Hidden class not found after dim ANSI code 8m.');517},518(green) => {519assert.strictEqual(1, green.classList.length);520assert(green.classList.contains('code-hidden') === false, 'Hidden class found after Hidden WAS TURNED OFF with 28m');521assert(green.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');522},523(crossedout) => {524assert.strictEqual(2, crossedout.classList.length);525assert(crossedout.classList.contains('code-foreground-colored'), 'Color class not found after color and hidden ANSI codes.');526assert(crossedout.classList.contains('code-strike-through'), 'strike-through class not found after crossout/strikethrough ANSI code 9m.');527},528(doubleunderline) => {529assert.strictEqual(2, doubleunderline.classList.length);530assert(doubleunderline.classList.contains('code-foreground-colored'), 'Color class not found after color, hidden, and crossedout ANSI codes.');531assert(doubleunderline.classList.contains('code-strike-through') === false, 'strike-through class found after strike-through WAS TURNED OFF with 29m');532assert(doubleunderline.classList.contains('code-double-underline'), 'Double underline class not found after double underline ANSI code 21m.');533},534(justgreen) => {535assert.strictEqual(1, justgreen.classList.length);536assert(justgreen.classList.contains('code-double-underline') === false, 'Double underline class found after double underline WAS TURNED OFF with 24m');537assert(justgreen.classList.contains('code-foreground-colored'), 'Color class not found after color ANSI code.');538},539(nothing) => {540assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after reset ANSI code.');541},542], 6);543544// underline, double underline are mutually exclusive, test underline->double underline->off and double underline->underline->off545assertMultipleSequenceElements('\x1b[4munderline\x1b[21mdouble underline\x1b[24munderlineOff\x1b[21mdouble underline\x1b[4munderline\x1b[24munderlineOff', [546(underline) => {547assert.strictEqual(1, underline.classList.length);548assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code 4m.');549},550(doubleunderline) => {551assert(doubleunderline.classList.contains('code-underline') === false, 'Underline class found after double underline code 21m');552assert(doubleunderline.classList.contains('code-double-underline'), 'Double underline class not found after double underline code 21m');553assert.strictEqual(1, doubleunderline.classList.length, 'should have found only double underline');554},555(nothing) => {556assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after underline off code 4m.');557},558(doubleunderline) => {559assert(doubleunderline.classList.contains('code-double-underline'), 'Double underline class not found after double underline code 21m');560assert.strictEqual(1, doubleunderline.classList.length, 'should have found only double underline');561},562(underline) => {563assert(underline.classList.contains('code-double-underline') === false, 'Double underline class found after underline code 4m');564assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code 4m.');565assert.strictEqual(1, underline.classList.length, 'should have found only underline');566},567(nothing) => {568assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after underline off code 4m.');569},570], 6);571572// underline and strike-through and overline can exist at the same time and573// in any combination574assertMultipleSequenceElements('\x1b[4munderline\x1b[9mand strikethough\x1b[53mand overline\x1b[24munderlineOff\x1b[55moverlineOff\x1b[29mstriklethoughOff', [575(underline) => {576assert.strictEqual(1, underline.classList.length, 'should have found only underline');577assert(underline.classList.contains('code-underline'), 'Underline class not found after underline ANSI code 4m.');578},579(strikethrough) => {580assert(strikethrough.classList.contains('code-underline'), 'Underline class NOT found after strikethrough code 9m');581assert(strikethrough.classList.contains('code-strike-through'), 'Strike through class not found after strikethrough code 9m');582assert.strictEqual(2, strikethrough.classList.length, 'should have found underline and strikethrough');583},584(overline) => {585assert(overline.classList.contains('code-underline'), 'Underline class NOT found after overline code 53m');586assert(overline.classList.contains('code-strike-through'), 'Strike through class not found after overline code 53m');587assert(overline.classList.contains('code-overline'), 'Overline class not found after overline code 53m');588assert.strictEqual(3, overline.classList.length, 'should have found underline,strikethrough and overline');589},590(underlineoff) => {591assert(underlineoff.classList.contains('code-underline') === false, 'Underline class found after underline off code 24m');592assert(underlineoff.classList.contains('code-strike-through'), 'Strike through class not found after underline off code 24m');593assert(underlineoff.classList.contains('code-overline'), 'Overline class not found after underline off code 24m');594assert.strictEqual(2, underlineoff.classList.length, 'should have found strikethrough and overline');595},596(overlineoff) => {597assert(overlineoff.classList.contains('code-underline') === false, 'Underline class found after overline off code 55m');598assert(overlineoff.classList.contains('code-overline') === false, 'Overline class found after overline off code 55m');599assert(overlineoff.classList.contains('code-strike-through'), 'Strike through class not found after overline off code 55m');600assert.strictEqual(1, overlineoff.classList.length, 'should have found only strikethrough');601},602(nothing) => {603assert(nothing.classList.contains('code-strike-through') === false, 'Strike through class found after strikethrough off code 29m');604assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after strikethough OFF code 29m');605},606], 6);607608// double underline and strike-through and overline can exist at the same time and609// in any combination610assertMultipleSequenceElements('\x1b[21mdoubleunderline\x1b[9mand strikethough\x1b[53mand overline\x1b[29mstriklethoughOff\x1b[55moverlineOff\x1b[24munderlineOff', [611(doubleunderline) => {612assert.strictEqual(1, doubleunderline.classList.length, 'should have found only doubleunderline');613assert(doubleunderline.classList.contains('code-double-underline'), 'Double underline class not found after double underline ANSI code 21m.');614},615(strikethrough) => {616assert(strikethrough.classList.contains('code-double-underline'), 'Double nderline class NOT found after strikethrough code 9m');617assert(strikethrough.classList.contains('code-strike-through'), 'Strike through class not found after strikethrough code 9m');618assert.strictEqual(2, strikethrough.classList.length, 'should have found doubleunderline and strikethrough');619},620(overline) => {621assert(overline.classList.contains('code-double-underline'), 'Double underline class NOT found after overline code 53m');622assert(overline.classList.contains('code-strike-through'), 'Strike through class not found after overline code 53m');623assert(overline.classList.contains('code-overline'), 'Overline class not found after overline code 53m');624assert.strictEqual(3, overline.classList.length, 'should have found doubleunderline,overline and strikethrough');625},626(strikethrougheoff) => {627assert(strikethrougheoff.classList.contains('code-double-underline'), 'Double underline class NOT found after strikethrough off code 29m');628assert(strikethrougheoff.classList.contains('code-overline'), 'Overline class NOT found after strikethrough off code 29m');629assert(strikethrougheoff.classList.contains('code-strike-through') === false, 'Strike through class found after strikethrough off code 29m');630assert.strictEqual(2, strikethrougheoff.classList.length, 'should have found doubleunderline and overline');631},632(overlineoff) => {633assert(overlineoff.classList.contains('code-double-underline'), 'Double underline class NOT found after overline off code 55m');634assert(overlineoff.classList.contains('code-strike-through') === false, 'Strike through class found after overline off code 55m');635assert(overlineoff.classList.contains('code-overline') === false, 'Overline class found after overline off code 55m');636assert.strictEqual(1, overlineoff.classList.length, 'Should have found only double underline');637},638(nothing) => {639assert(nothing.classList.contains('code-double-underline') === false, 'Double underline class found after underline off code 24m');640assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after underline OFF code 24m');641},642], 6);643644// superscript and subscript are mutually exclusive, test superscript->subscript->off and subscript->superscript->off645assertMultipleSequenceElements('\x1b[73msuperscript\x1b[74msubscript\x1b[75mneither\x1b[74msubscript\x1b[73msuperscript\x1b[75mneither', [646(superscript) => {647assert.strictEqual(1, superscript.classList.length, 'should only be superscript class');648assert(superscript.classList.contains('code-superscript'), 'Superscript class not found after superscript ANSI code 73m.');649},650(subscript) => {651assert(subscript.classList.contains('code-superscript') === false, 'Superscript class found after subscript code 74m');652assert(subscript.classList.contains('code-subscript'), 'Subscript class not found after subscript code 74m');653assert.strictEqual(1, subscript.classList.length, 'should have found only subscript class');654},655(nothing) => {656assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after superscript/subscript off code 75m.');657},658(subscript) => {659assert(subscript.classList.contains('code-subscript'), 'Subscript class not found after subscript code 74m');660assert.strictEqual(1, subscript.classList.length, 'should have found only subscript class');661},662(superscript) => {663assert(superscript.classList.contains('code-subscript') === false, 'Subscript class found after superscript code 73m');664assert(superscript.classList.contains('code-superscript'), 'Superscript class not found after superscript ANSI code 73m.');665assert.strictEqual(1, superscript.classList.length, 'should have found only superscript class');666},667(nothing) => {668assert.strictEqual(0, nothing.classList.length, 'One or more style classes still found after superscipt/subscript off code 75m.');669},670], 6);671672// Consecutive font codes switch to new font class and remove previous and then final switch to default font removes class673assertMultipleSequenceElements('\x1b[11mFont1\x1b[12mFont2\x1b[13mFont3\x1b[14mFont4\x1b[15mFont5\x1b[10mdefaultFont', [674(font1) => {675assert.strictEqual(1, font1.classList.length);676assert(font1.classList.contains('code-font-1'), 'font 1 class NOT found after switch to font 1 with ANSI code 11m');677},678(font2) => {679assert.strictEqual(1, font2.classList.length);680assert(font2.classList.contains('code-font-1') === false, 'font 1 class found after switch to font 2 with ANSI code 12m');681assert(font2.classList.contains('code-font-2'), 'font 2 class NOT found after switch to font 2 with ANSI code 12m');682},683(font3) => {684assert.strictEqual(1, font3.classList.length);685assert(font3.classList.contains('code-font-2') === false, 'font 2 class found after switch to font 3 with ANSI code 13m');686assert(font3.classList.contains('code-font-3'), 'font 3 class NOT found after switch to font 3 with ANSI code 13m');687},688(font4) => {689assert.strictEqual(1, font4.classList.length);690assert(font4.classList.contains('code-font-3') === false, 'font 3 class found after switch to font 4 with ANSI code 14m');691assert(font4.classList.contains('code-font-4'), 'font 4 class NOT found after switch to font 4 with ANSI code 14m');692},693(font5) => {694assert.strictEqual(1, font5.classList.length);695assert(font5.classList.contains('code-font-4') === false, 'font 4 class found after switch to font 5 with ANSI code 15m');696assert(font5.classList.contains('code-font-5'), 'font 5 class NOT found after switch to font 5 with ANSI code 15m');697},698(defaultfont) => {699assert.strictEqual(0, defaultfont.classList.length, 'One or more font style classes still found after reset to default font with ANSI code 10m.');700},701], 6);702703// More Consecutive font codes switch to new font class and remove previous and then final switch to default font removes class704assertMultipleSequenceElements('\x1b[16mFont6\x1b[17mFont7\x1b[18mFont8\x1b[19mFont9\x1b[20mFont10\x1b[10mdefaultFont', [705(font6) => {706assert.strictEqual(1, font6.classList.length);707assert(font6.classList.contains('code-font-6'), 'font 6 class NOT found after switch to font 6 with ANSI code 16m');708},709(font7) => {710assert.strictEqual(1, font7.classList.length);711assert(font7.classList.contains('code-font-6') === false, 'font 6 class found after switch to font 7 with ANSI code 17m');712assert(font7.classList.contains('code-font-7'), 'font 7 class NOT found after switch to font 7 with ANSI code 17m');713},714(font8) => {715assert.strictEqual(1, font8.classList.length);716assert(font8.classList.contains('code-font-7') === false, 'font 7 class found after switch to font 8 with ANSI code 18m');717assert(font8.classList.contains('code-font-8'), 'font 8 class NOT found after switch to font 8 with ANSI code 18m');718},719(font9) => {720assert.strictEqual(1, font9.classList.length);721assert(font9.classList.contains('code-font-8') === false, 'font 8 class found after switch to font 9 with ANSI code 19m');722assert(font9.classList.contains('code-font-9'), 'font 9 class NOT found after switch to font 9 with ANSI code 19m');723},724(font10) => {725assert.strictEqual(1, font10.classList.length);726assert(font10.classList.contains('code-font-9') === false, 'font 9 class found after switch to font 10 with ANSI code 20m');727assert(font10.classList.contains('code-font-10'), `font 10 class NOT found after switch to font 10 with ANSI code 20m (${font10.classList})`);728},729(defaultfont) => {730assert.strictEqual(0, defaultfont.classList.length, 'One or more font style classes (2nd series) still found after reset to default font with ANSI code 10m.');731},732], 6);733734// Blackletter font codes can be turned off with other font codes or 23m735assertMultipleSequenceElements('\x1b[3mitalic\x1b[20mfont10blacklatter\x1b[23mitalicAndBlackletterOff\x1b[20mFont10Again\x1b[11mFont1\x1b[10mdefaultFont', [736(italic) => {737assert.strictEqual(1, italic.classList.length);738assert(italic.classList.contains('code-italic'), 'italic class NOT found after italic code ANSI code 3m');739},740(font10) => {741assert.strictEqual(2, font10.classList.length);742assert(font10.classList.contains('code-italic'), 'no itatic class found after switch to font 10 (blackletter) with ANSI code 20m');743assert(font10.classList.contains('code-font-10'), 'font 10 class NOT found after switch to font 10 with ANSI code 20m');744},745(italicAndBlackletterOff) => {746assert.strictEqual(0, italicAndBlackletterOff.classList.length, 'italic or blackletter (font10) class found after both switched off with ANSI code 23m');747},748(font10) => {749assert.strictEqual(1, font10.classList.length);750assert(font10.classList.contains('code-font-10'), 'font 10 class NOT found after switch to font 10 with ANSI code 20m');751},752(font1) => {753assert.strictEqual(1, font1.classList.length);754assert(font1.classList.contains('code-font-10') === false, 'font 10 class found after switch to font 1 with ANSI code 11m');755assert(font1.classList.contains('code-font-1'), 'font 1 class NOT found after switch to font 1 with ANSI code 11m');756},757(defaultfont) => {758assert.strictEqual(0, defaultfont.classList.length, 'One or more font style classes (2nd series) still found after reset to default font with ANSI code 10m.');759},760], 6);761762// italic can be turned on/off with affecting font codes 1-9 (italic off will clear 'blackletter'(font 23) as per spec)763assertMultipleSequenceElements('\x1b[3mitalic\x1b[12mfont2\x1b[23mitalicOff\x1b[3mitalicFont2\x1b[10mjustitalic\x1b[23mnothing', [764(italic) => {765assert.strictEqual(1, italic.classList.length);766assert(italic.classList.contains('code-italic'), 'italic class NOT found after italic code ANSI code 3m');767},768(font10) => {769assert.strictEqual(2, font10.classList.length);770assert(font10.classList.contains('code-italic'), 'no itatic class found after switch to font 2 with ANSI code 12m');771assert(font10.classList.contains('code-font-2'), 'font 2 class NOT found after switch to font 2 with ANSI code 12m');772},773(italicOff) => {774assert.strictEqual(1, italicOff.classList.length, 'italic class found after both switched off with ANSI code 23m');775assert(italicOff.classList.contains('code-italic') === false, 'itatic class found after switching it OFF with ANSI code 23m');776assert(italicOff.classList.contains('code-font-2'), 'font 2 class NOT found after switching italic off with ANSI code 23m');777},778(italicFont2) => {779assert.strictEqual(2, italicFont2.classList.length);780assert(italicFont2.classList.contains('code-italic'), 'no itatic class found after italic ANSI code 3m');781assert(italicFont2.classList.contains('code-font-2'), 'font 2 class NOT found after italic ANSI code 3m');782},783(justitalic) => {784assert.strictEqual(1, justitalic.classList.length);785assert(justitalic.classList.contains('code-font-2') === false, 'font 2 class found after switch to default font with ANSI code 10m');786assert(justitalic.classList.contains('code-italic'), 'italic class NOT found after switch to default font with ANSI code 10m');787},788(nothing) => {789assert.strictEqual(0, nothing.classList.length, 'One or more classes still found after final italic removal with ANSI code 23m.');790},791], 6);792793// Reverse video reverses Foreground/Background colors WITH both SET and can called in sequence794assertMultipleSequenceElements('\x1b[38;2;10;20;30mfg10,20,30\x1b[48;2;167;168;169mbg167,168,169\x1b[7m8ReverseVideo\x1b[7mDuplicateReverseVideo\x1b[27mReverseOff\x1b[27mDupReverseOff', [795(fg10_20_30) => {796assert.strictEqual(1, fg10_20_30.classList.length, 'Foreground ANSI color code should add one class.');797assert(fg10_20_30.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');798assertInlineColor(fg10_20_30, 'foreground', new RGBA(10, 20, 30), '24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');799},800(bg167_168_169) => {801assert.strictEqual(2, bg167_168_169.classList.length, 'background ANSI color codes should only add a single class.');802assert(bg167_168_169.classList.contains('code-background-colored'), 'Background ANSI color codes should add custom background color class.');803assertInlineColor(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.');804assert(bg167_168_169.classList.contains('code-foreground-colored'), 'Still Foreground ANSI color codes should add custom foreground color class.');805assertInlineColor(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.');806},807(reverseVideo) => {808assert.strictEqual(2, reverseVideo.classList.length, 'background ANSI color codes should only add a single class.');809assert(reverseVideo.classList.contains('code-background-colored'), 'Background ANSI color codes should add custom background color class.');810assertInlineColor(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.');811assert(reverseVideo.classList.contains('code-foreground-colored'), 'Still Foreground ANSI color codes should add custom foreground color class.');812assertInlineColor(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.');813},814(dupReverseVideo) => {815assert.strictEqual(2, dupReverseVideo.classList.length, 'After second Reverse Video - background ANSI color codes should only add a single class.');816assert(dupReverseVideo.classList.contains('code-background-colored'), 'After second Reverse Video - Background ANSI color codes should add custom background color class.');817assertInlineColor(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.');818assert(dupReverseVideo.classList.contains('code-foreground-colored'), 'After second Reverse Video - Still Foreground ANSI color codes should add custom foreground color class.');819assertInlineColor(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.');820},821(reversedBack) => {822assert.strictEqual(2, reversedBack.classList.length, 'Reversed Back - background ANSI color codes should only add a single class.');823assert(reversedBack.classList.contains('code-background-colored'), 'Reversed Back - Background ANSI color codes should add custom background color class.');824assertInlineColor(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.');825assert(reversedBack.classList.contains('code-foreground-colored'), 'Reversed Back - Foreground ANSI color codes should add custom foreground color class.');826assertInlineColor(reversedBack, 'foreground', new RGBA(10, 20, 30), 'Reversed Back - 24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');827},828(dupReversedBack) => {829assert.strictEqual(2, dupReversedBack.classList.length, '2nd Reversed Back - background ANSI color codes should only add a single class.');830assert(dupReversedBack.classList.contains('code-background-colored'), '2nd Reversed Back - Background ANSI color codes should add custom background color class.');831assertInlineColor(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.');832assert(dupReversedBack.classList.contains('code-foreground-colored'), '2nd Reversed Back - Foreground ANSI color codes should add custom foreground color class.');833assertInlineColor(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.');834},835], 6);836837// Reverse video reverses Foreground/Background colors WITH ONLY foreground color SET838assertMultipleSequenceElements('\x1b[38;2;10;20;30mfg10,20,30\x1b[7m8ReverseVideo\x1b[27mReverseOff', [839(fg10_20_30) => {840assert.strictEqual(1, fg10_20_30.classList.length, 'Foreground ANSI color code should add one class.');841assert(fg10_20_30.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');842assertInlineColor(fg10_20_30, 'foreground', new RGBA(10, 20, 30), '24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');843},844(reverseVideo) => {845assert.strictEqual(1, reverseVideo.classList.length, 'Background ANSI color codes should only add a single class.');846assert(reverseVideo.classList.contains('code-background-colored'), 'Background ANSI color codes should add custom background color class.');847assert(reverseVideo.classList.contains('code-foreground-colored') === false, 'After Reverse with NO background the Foreground ANSI color codes should NOT BE SET.');848assertInlineColor(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.');849},850(reversedBack) => {851assert.strictEqual(1, reversedBack.classList.length, 'Reversed Back - background ANSI color codes should only add a single class.');852assert(reversedBack.classList.contains('code-background-colored') === false, 'AFTER Reversed Back - Background ANSI color should NOT BE SET.');853assert(reversedBack.classList.contains('code-foreground-colored'), 'Reversed Back - Foreground ANSI color codes should add custom foreground color class.');854assertInlineColor(reversedBack, 'foreground', new RGBA(10, 20, 30), 'Reversed Back - 24-bit RGBA ANSI color code (10,20,30) should add matching color inline style.');855},856], 3);857858// Reverse video reverses Foreground/Background colors WITH ONLY background color SET859assertMultipleSequenceElements('\x1b[48;2;167;168;169mbg167,168,169\x1b[7m8ReverseVideo\x1b[27mReverseOff', [860(bg167_168_169) => {861assert.strictEqual(1, bg167_168_169.classList.length, 'Background ANSI color code should add one class.');862assert(bg167_168_169.classList.contains('code-background-colored'), 'Background ANSI color codes should add custom foreground color class.');863assertInlineColor(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.');864},865(reverseVideo) => {866assert.strictEqual(1, reverseVideo.classList.length, 'After ReverseVideo Foreground ANSI color codes should only add a single class.');867assert(reverseVideo.classList.contains('code-foreground-colored'), 'After ReverseVideo Foreground ANSI color codes should add custom background color class.');868assert(reverseVideo.classList.contains('code-background-colored') === false, 'After Reverse with NO foreground color the background ANSI color codes should BE SET.');869assertInlineColor(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.');870},871(reversedBack) => {872assert.strictEqual(1, reversedBack.classList.length, 'Reversed Back - background ANSI color codes should only add a single class.');873assert(reversedBack.classList.contains('code-foreground-colored') === false, 'AFTER Reversed Back - Foreground ANSI color should NOT BE SET.');874assert(reversedBack.classList.contains('code-background-colored'), 'Reversed Back - Background ANSI color codes should add custom background color class.');875assertInlineColor(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.');876},877], 3);878879// Underline color Different types of color codes still cancel each other880assertMultipleSequenceElements('\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', [881(adv24Bit) => {882assert.strictEqual(1, adv24Bit.classList.length, 'Underline ANSI color codes should only add a single class (1).');883assert(adv24Bit.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');884assertInlineColor(adv24Bit, 'underline', new RGBA(101, 102, 103), '24-bit RGBA ANSI color code (101,102,103) should add matching color inline style.');885},886(adv8BitSimple) => {887assert.strictEqual(1, adv8BitSimple.classList.length, 'Multiple underline ANSI color codes should only add a single class (2).');888assert(adv8BitSimple.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');889// changed to simple theme color, don't know exactly what it should be, but it should NO LONGER BE 101,102,103890assertInlineColor(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);891},892(adv24BitAgain) => {893assert.strictEqual(1, adv24BitAgain.classList.length, 'Multiple underline ANSI color codes should only add a single class (3).');894assert(adv24BitAgain.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');895assertInlineColor(adv24BitAgain, 'underline', new RGBA(104, 105, 106), '24-bit RGBA ANSI color code (100,100,100) should add matching color inline style.');896},897(adv8BitAdvanced) => {898assert.strictEqual(1, adv8BitAdvanced.classList.length, 'Multiple underline ANSI color codes should only add a single class (4).');899assert(adv8BitAdvanced.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');900// changed to 8bit advanced color, don't know exactly what it should be, but it should NO LONGER BE 104,105,106901assertInlineColor(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);902},903(adv24BitUnderlin200) => {904assert.strictEqual(1, adv24BitUnderlin200.classList.length, 'Multiple underline ANSI color codes should only add a single class 4.');905assert(adv24BitUnderlin200.classList.contains('code-underline-colored'), 'Underline ANSI color codes should add custom underline color class.');906assertInlineColor(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.');907},908(underlineColorResetToDefault) => {909assert.strictEqual(0, underlineColorResetToDefault.classList.length, 'After Underline Color reset to default NO underline color class should be set.');910assertInlineColor(underlineColorResetToDefault, 'underline', undefined, 'after RESET TO DEFAULT underline color SHOULD NOT BE SET (no color inline style.)');911},912], 6);913914// Different types of color codes still cancel each other915assertMultipleSequenceElements('\x1b[34msimple\x1b[38;2;101;102;103m24bit\x1b[38;5;3m8bitsimple\x1b[38;2;104;105;106m24bitAgain\x1b[38;5;101m8bitadvanced', [916(simple) => {917assert.strictEqual(1, simple.classList.length, 'Foreground ANSI color code should add one class.');918assert(simple.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');919},920(adv24Bit) => {921assert.strictEqual(1, adv24Bit.classList.length, 'Multiple foreground ANSI color codes should only add a single class.');922assert(adv24Bit.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');923assertInlineColor(adv24Bit, 'foreground', new RGBA(101, 102, 103), '24-bit RGBA ANSI color code (101,102,103) should add matching color inline style.');924},925(adv8BitSimple) => {926assert.strictEqual(1, adv8BitSimple.classList.length, 'Multiple foreground ANSI color codes should only add a single class.');927assert(adv8BitSimple.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');928//color is theme based, so we can't check what it should be but we know it should NOT BE 101,102,103 anymore929assertInlineColor(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);930},931(adv24BitAgain) => {932assert.strictEqual(1, adv24BitAgain.classList.length, 'Multiple foreground ANSI color codes should only add a single class.');933assert(adv24BitAgain.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');934assertInlineColor(adv24BitAgain, 'foreground', new RGBA(104, 105, 106), '24-bit RGBA ANSI color code (104,105,106) should add matching color inline style.');935},936(adv8BitAdvanced) => {937assert.strictEqual(1, adv8BitAdvanced.classList.length, 'Multiple foreground ANSI color codes should only add a single class.');938assert(adv8BitAdvanced.classList.contains('code-foreground-colored'), 'Foreground ANSI color codes should add custom foreground color class.');939// color should NO LONGER BE 104,105,106940assertInlineColor(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);941}942], 5);943944});945946/**947* Assert that the provided ANSI sequence exactly matches the text content of the resulting948* {@link HTMLSpanElement}.949*950* @param sequence The ANSI sequence to verify.951*/952function assertSequencestrictEqualToContent(sequence: string): void {953const child: HTMLSpanElement = getSequenceOutput(sequence);954assert(child.textContent === sequence);955}956957test('Invalid codes treated as regular text', () => {958959// Individual components of ANSI code start are printed960assertSequencestrictEqualToContent('\x1b');961assertSequencestrictEqualToContent('[');962963// Unsupported sequence prints both characters964assertSequencestrictEqualToContent('\x1b[');965966// Random strings are displayed properly967for (let i = 0; i < 50; i++) {968const uuid: string = generateUuid();969assertSequencestrictEqualToContent(uuid);970}971972});973974/**975* Assert that a given ANSI sequence maintains added content following the ANSI code, and that976* the expression itself is thrown away.977*978* @param sequence The ANSI sequence to verify. The provided sequence should contain ANSI codes979* only, and should not include actual text content as it is provided by this function.980*/981function assertEmptyOutput(sequence: string) {982const child: HTMLSpanElement = getSequenceOutput(sequence + 'content');983assert.strictEqual('content', child.textContent);984assert.strictEqual(0, child.classList.length);985}986987test('Empty sequence output', () => {988989const sequences: string[] = [990// No colour codes991'',992'\x1b[;m',993'\x1b[1;;m',994'\x1b[m',995'\x1b[99m'996];997998sequences.forEach(sequence => {999assertEmptyOutput(sequence);1000});10011002// Check other possible ANSI terminators1003const terminators: string[] = 'ABCDHIJKfhmpsu'.split('');10041005terminators.forEach(terminator => {1006assertEmptyOutput('\x1b[content' + terminator);1007});10081009});10101011test('calcANSI8bitColor', () => {1012// Invalid values1013// Negative (below range), simple range, decimals1014for (let i = -10; i <= 15; i += 0.5) {1015assert(calcANSI8bitColor(i) === undefined, 'Values less than 16 passed to calcANSI8bitColor should return undefined.');1016}1017// In-range range decimals1018for (let i = 16.5; i < 254; i += 1) {1019assert(calcANSI8bitColor(i) === undefined, 'Floats passed to calcANSI8bitColor should return undefined.');1020}1021// Above range1022for (let i = 256; i < 300; i += 0.5) {1023assert(calcANSI8bitColor(i) === undefined, 'Values grather than 255 passed to calcANSI8bitColor should return undefined.');1024}10251026// All valid colors1027for (let red = 0; red <= 5; red++) {1028for (let green = 0; green <= 5; green++) {1029for (let blue = 0; blue <= 5; blue++) {1030const colorOut: any = calcANSI8bitColor(16 + red * 36 + green * 6 + blue);1031assert(colorOut.r === Math.round(red * (255 / 5)), 'Incorrect red value encountered for color');1032assert(colorOut.g === Math.round(green * (255 / 5)), 'Incorrect green value encountered for color');1033assert(colorOut.b === Math.round(blue * (255 / 5)), 'Incorrect balue value encountered for color');1034}1035}1036}10371038// All grays1039for (let i = 232; i <= 255; i++) {1040const grayOut: any = calcANSI8bitColor(i);1041assert(grayOut.r === grayOut.g);1042assert(grayOut.r === grayOut.b);1043assert(grayOut.r === Math.round((i - 232) / 23 * 255));1044}1045});10461047});104810491050