diff --git a/README.md b/README.md index 0e7df5ac..b46b7e93 100644 --- a/README.md +++ b/README.md @@ -170,6 +170,7 @@ Xterm.js is used in several world-class applications to provide great terminal e - [**Intervue**](https://www.intervue.io): Pair programming for interviews. Multiple programming languages supported, with results displayed by xterm.js. - [**TRASA**](https://trasa.io): Zero trust access to Web, SSH, RDP and Database services. - [**Commas**](https://github.com/CyanSalt/commas): Commas is a hackable terminal and command runner. +- [**Devtron**](https://github.com/devtron-labs/devtron): Software Delivery Workflow For Kubernetes. [And much more...](https://github.com/xtermjs/xterm.js/network/dependents) Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. Note: Please add any new contributions to the end of the list only. diff --git a/addons/xterm-addon-fit/package.json b/addons/xterm-addon-fit/package.json index 859d6ebf..f9d96c4a 100644 --- a/addons/xterm-addon-fit/package.json +++ b/addons/xterm-addon-fit/package.json @@ -1,6 +1,6 @@ { "name": "xterm-addon-fit", - "version": "0.4.0", + "version": "0.5.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" diff --git a/addons/xterm-addon-ligatures/package.json b/addons/xterm-addon-ligatures/package.json index a35cfc5c..4e2b4056 100644 --- a/addons/xterm-addon-ligatures/package.json +++ b/addons/xterm-addon-ligatures/package.json @@ -1,6 +1,6 @@ { "name": "xterm-addon-ligatures", - "version": "0.3.0", + "version": "0.4.0", "description": "Add support for programming ligatures to xterm.js", "author": { "name": "The xterm.js authors", diff --git a/addons/xterm-addon-search/package.json b/addons/xterm-addon-search/package.json index 711335cb..659bd834 100644 --- a/addons/xterm-addon-search/package.json +++ b/addons/xterm-addon-search/package.json @@ -1,6 +1,6 @@ { "name": "xterm-addon-search", - "version": "0.7.0", + "version": "0.8.0", "author": { "name": "The xterm.js authors", "url": "https://xtermjs.org/" diff --git a/addons/xterm-addon-serialize/src/SerializeAddon.ts b/addons/xterm-addon-serialize/src/SerializeAddon.ts index dad6aa4f..0caaaa03 100644 --- a/addons/xterm-addon-serialize/src/SerializeAddon.ts +++ b/addons/xterm-addon-serialize/src/SerializeAddon.ts @@ -21,7 +21,7 @@ abstract class BaseSerializeHandler { const cell2 = this._buffer.getNullCell(); let oldCell = cell1; - this._beforeSerialize(endRow - startRow); + this._beforeSerialize(endRow - startRow, startRow, endRow); for (let row = startRow; row < endRow; row++) { const line = this._buffer.getLine(row); @@ -36,7 +36,7 @@ abstract class BaseSerializeHandler { oldCell = c; } } - this._rowEnd(row); + this._rowEnd(row, row === endRow - 1); } this._afterSerialize(); @@ -45,8 +45,8 @@ abstract class BaseSerializeHandler { } protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { } - protected _rowEnd(row: number): void { } - protected _beforeSerialize(rows: number): void { } + protected _rowEnd(row: number, isLastRow: boolean): void { } + protected _beforeSerialize(rows: number, startRow: number, endRow: number): void { } protected _afterSerialize(): void { } protected _serializeString(): string { return ''; } } @@ -71,27 +71,152 @@ function equalFlags(cell1: IBufferCell, cell2: IBufferCell): boolean { && cell1.isDim() === cell2.isDim(); } + + class StringSerializeHandler extends BaseSerializeHandler { private _rowIndex: number = 0; private _allRows: string[] = new Array(); + private _allRowSeparators: string[] = new Array(); private _currentRow: string = ''; private _nullCellCount: number = 0; - constructor(buffer: IBuffer) { - super(buffer); + // we can see a full colored cell and a null cell that only have background the same style + // but the information isn't preserved by null cell itself + // so wee need to record it when required. + private _cursorStyle: IBufferCell = this._buffer1.getNullCell(); + + // where exact the cursor styles comes from + // because we can't copy the cell directly + // so we remember where the content comes from instead + private _cursorStyleRow: number = 0; + private _cursorStyleCol: number = 0; + + // this is a null cell for reference for checking whether background is empty or not + private _backgroundCell: IBufferCell = this._buffer1.getNullCell(); + + private _firstRow: number = 0; + private _lastCursorRow: number = 0; + private _lastCursorCol: number = 0; + private _lastContentCursorRow: number = 0; + private _lastContentCursorCol: number = 0; + + constructor(private _buffer1: IBuffer, private _terminal: Terminal) { + super(_buffer1); } - protected _beforeSerialize(rows: number): void { + protected _beforeSerialize(rows: number, start: number, end: number): void { this._allRows = new Array(rows); + this._lastContentCursorRow = start; + this._lastCursorRow = start; + this._firstRow = start; } - protected _rowEnd(row: number): void { - this._allRows[this._rowIndex++] = this._currentRow; + private _thisRowLastChar: IBufferCell = this._buffer1.getNullCell(); + private _thisRowLastSecondChar: IBufferCell = this._buffer1.getNullCell(); + private _nextRowFirstChar: IBufferCell = this._buffer1.getNullCell(); + protected _rowEnd(row: number, isLastRow: boolean): void { + // if there is colorful empty cell at line end, whe must pad it back, or the the color block will missing + if (this._nullCellCount > 0 && !equalBg(this._cursorStyle, this._backgroundCell)) { + // use clear right to set background. + this._currentRow += `\x1b[${this._nullCellCount}X`; + } + + let rowSeparator = ''; + + // handle row separator + if (!isLastRow) { + // Enable BCE + if (row - this._firstRow >= this._terminal.rows) { + this._buffer1.getLine(this._cursorStyleRow)?.getCell(this._cursorStyleCol, this._backgroundCell); + } + + // Fetch current line + const currentLine = this._buffer1.getLine(row)!; + // Fetch next line + const nextLine = this._buffer1.getLine(row + 1)!; + + if (!nextLine.isWrapped) { + // just insert the line break + rowSeparator = '\r\n'; + // we sended the enter + this._lastCursorRow = row + 1; + this._lastCursorCol = 0; + } else { + rowSeparator = ''; + const thisRowLastChar = currentLine.getCell(currentLine.length - 1, this._thisRowLastChar)!; + const thisRowLastSecondChar = currentLine.getCell(currentLine.length - 2, this._thisRowLastSecondChar)!; + const nextRowFirstChar = nextLine.getCell(0, this._nextRowFirstChar)!; + const isNextRowFirstCharDoubleWidth = nextRowFirstChar.getWidth() > 1; + + // validate whether this line wrap is ever possible + // which mean whether cursor can placed at a overflow position (x === row) naturally + let isValid = false; + + if ( + // you must output character to cause overflow, control sequence can't do this + nextRowFirstChar.getChars() && + isNextRowFirstCharDoubleWidth ? this._nullCellCount <= 1 : this._nullCellCount <= 0 + ) { + if ( + // the last character can't be null, + // you can't use control sequence to move cursor to (x === row) + (thisRowLastChar.getChars() || thisRowLastChar.getWidth() === 0) && + // change background of the first wrapped cell also affects BCE + // so we mark it as invalid to simply the process to determine line separator + equalBg(thisRowLastChar, nextRowFirstChar) + ) { + isValid = true; + } + + if ( + // the second to last character can't be null if the next line starts with CJK, + // you can't use control sequence to move cursor to (x === row) + isNextRowFirstCharDoubleWidth && + (thisRowLastSecondChar.getChars() || thisRowLastSecondChar.getWidth() === 0) && + // change background of the first wrapped cell also affects BCE + // so we mark it as invalid to simply the process to determine line separator + equalBg(thisRowLastChar, nextRowFirstChar) && + equalBg(thisRowLastSecondChar, nextRowFirstChar) + ) { + isValid = true; + } + } + + if (!isValid) { + // force the wrap with magic + // insert enough character to force the wrap + rowSeparator = '-'.repeat(this._nullCellCount + 1); + // move back and erase next line head + rowSeparator += '\x1b[1D\x1b[1X'; + + if (this._nullCellCount > 0) { + // do these because we filled the last several null slot, which we shouldn't + rowSeparator += '\x1b[A'; + rowSeparator += `\x1b[${currentLine.length - this._nullCellCount}C`; + rowSeparator += `\x1b[${this._nullCellCount}X`; + rowSeparator += `\x1b[${currentLine.length - this._nullCellCount}D`; + rowSeparator += '\x1b[B'; + } + + // This is content and need the be serialized even it is invisible. + // without this, wrap will be missing from outputs. + this._lastContentCursorRow = row + 1; + this._lastContentCursorCol = 0; + + // force commit the cursor position + this._lastCursorRow = row + 1; + this._lastCursorCol = 0; + } + } + } + + this._allRows[this._rowIndex] = this._currentRow; + this._allRowSeparators[this._rowIndex++] = rowSeparator; this._currentRow = ''; this._nullCellCount = 0; } - protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { + private _diffStyle(cell: IBufferCell, oldCell: IBufferCell): number[] { const sgrSeq: number[] = []; const fgChanged = !equalFg(cell, oldCell); const bgChanged = !equalBg(cell, oldCell); @@ -99,7 +224,9 @@ class StringSerializeHandler extends BaseSerializeHandler { if (fgChanged || bgChanged || flagsChanged) { if (cell.isAttributeDefault()) { - this._currentRow += '\x1b[0m'; + if (!oldCell.isAttributeDefault()) { + sgrSeq.push(0); + } } else { if (fgChanged) { const color = cell.getFgColor(); @@ -131,30 +258,129 @@ class StringSerializeHandler extends BaseSerializeHandler { } } - if (sgrSeq.length) { + return sgrSeq; + } + + protected _nextCell(cell: IBufferCell, oldCell: IBufferCell, row: number, col: number): void { + // a width 0 cell don't need to be count because it is just a placeholder after a CJK character; + const isPlaceHolderCell = cell.getWidth() === 0; + + if (isPlaceHolderCell) { + return; + } + + // this cell don't have content + const isEmptyCell = cell.getChars() === ''; + + const sgrSeq = this._diffStyle(cell, this._cursorStyle); + + // the empty cell style is only assumed to be changed when background changed, because foreground is always 0. + const styleChanged = isEmptyCell ? !equalBg(this._cursorStyle, cell) : sgrSeq.length > 0; + + /** + * handles style change + */ + if (styleChanged) { + // before update the style, we need to fill empty cell back + if (this._nullCellCount > 0) { + // use clear right to set background. + if (!equalBg(this._cursorStyle, this._backgroundCell)) { + this._currentRow += `\x1b[${this._nullCellCount}X`; + } + // use move right to move cursor. + this._currentRow += `\x1b[${this._nullCellCount}C`; + this._nullCellCount = 0; + } + + this._lastContentCursorRow = this._lastCursorRow = row; + this._lastContentCursorCol = this._lastCursorCol = col; + this._currentRow += `\x1b[${sgrSeq.join(';')}m`; + + // update the last cursor style + const line = this._buffer1.getLine(row); + if (line !== undefined) { + line.getCell(col, this._cursorStyle); + this._cursorStyleRow = row; + this._cursorStyleCol = col; + } } - // Count number of null cells encountered after the last non-null cell and move the cursor - // if a non-null cell is found (eg. \t or cursor move) - if (cell.getChars() === '') { + /** + * handles actual content + */ + if (isEmptyCell) { this._nullCellCount += cell.getWidth(); - } else if (this._nullCellCount > 0) { - this._currentRow += `\x1b[${this._nullCellCount}C`; - this._nullCellCount = 0; - } + } else { + if (this._nullCellCount > 0) { + // we can just assume we have same style with previous one here + // because style change is handled by previous stage + // use move right when background is empty, use clear right when there is background. + if (equalBg(this._cursorStyle, this._backgroundCell)) { + this._currentRow += `\x1b[${this._nullCellCount}C`; + } else { + this._currentRow += `\x1b[${this._nullCellCount}X`; + this._currentRow += `\x1b[${this._nullCellCount}C`; + } + this._nullCellCount = 0; + } - this._currentRow += cell.getChars(); + this._currentRow += cell.getChars(); + + // update cursor + this._lastContentCursorRow = this._lastCursorRow = row; + this._lastContentCursorCol = this._lastCursorCol = col + cell.getWidth(); + } } protected _serializeString(): string { let rowEnd = this._allRows.length; - for (; rowEnd > 0; rowEnd--) { - if (this._allRows[rowEnd - 1]) { - break; + + // the fixup is only required for data without scrollback + // because it will always be placed at last line otherwise + if (this._buffer1.length - this._firstRow <= this._terminal.rows) { + rowEnd = this._lastContentCursorRow + 1 - this._firstRow; + this._lastCursorCol = this._lastContentCursorCol; + this._lastCursorRow = this._lastContentCursorRow; + } + + let content = ''; + + for (let i = 0; i < rowEnd; i++) { + content += this._allRows[i]; + if (i + 1 < rowEnd) { + content += this._allRowSeparators[i]; } } - return this._allRows.slice(0, rowEnd).join('\r\n'); + + // restore the cursor + const realCursorRow = this._buffer1.baseY + this._buffer1.cursorY; + const realCursorCol = this._buffer1.cursorX; + + const cursorMoved = (realCursorRow !== this._lastCursorRow || realCursorCol !== this._lastCursorCol); + + const moveRight = (offset: number): void => { + if (offset > 0) { + content += `\u001b[${offset}C`; + } else if (offset < 0) { + content += `\u001b[${-offset}D`; + } + }; + const moveDown = (offset: number): void => { + if (offset > 0) { + content += `\u001b[${offset}B`; + } else if (offset < 0) { + content += `\u001b[${-offset}A`; + } + }; + + if (cursorMoved) { + moveDown(realCursorRow - this._lastCursorRow); + moveRight(realCursorCol - this._lastCursorCol); + } + + + return content; } } @@ -167,20 +393,33 @@ export class SerializeAddon implements ITerminalAddon { this._terminal = terminal; } - public serialize(rows?: number): string { - // TODO: Add re-position cursor support - // TODO: Add word wrap mode support + private _getString(buffer: IBuffer, scrollback?: number): string { + const maxRows = buffer.length; + const handler = new StringSerializeHandler(buffer, this._terminal!); + + const correctRows = (scrollback === undefined) ? maxRows : constrain(scrollback + this!._terminal!.rows, 0, maxRows); + const result = handler.serialize(maxRows - correctRows, maxRows); + + return result; + } + + public serialize(scrollback?: number): string { // TODO: Add combinedData support if (!this._terminal) { throw new Error('Cannot use addon until it has been loaded'); } - const maxRows = this._terminal.buffer.active.length; - const handler = new StringSerializeHandler(this._terminal.buffer.active); + if (this._terminal.buffer.active.type === 'normal') { + return this._getString(this._terminal.buffer.active, scrollback); + } - rows = (rows === undefined) ? maxRows : constrain(rows, 0, maxRows); + const normalScreenContent = this._getString(this._terminal.buffer.normal, scrollback); + // alt screen don't have scrollback + const alternativeScreenContent = this._getString(this._terminal.buffer.alternate, undefined); - return handler.serialize(maxRows - rows, maxRows); + return normalScreenContent + + '\u001b[?1049h\u001b[H' + + alternativeScreenContent; } public dispose(): void { } diff --git a/addons/xterm-addon-serialize/test/SerializeAddon.api.ts b/addons/xterm-addon-serialize/test/SerializeAddon.api.ts index 5b615db1..47af9d91 100644 --- a/addons/xterm-addon-serialize/test/SerializeAddon.api.ts +++ b/addons/xterm-addon-serialize/test/SerializeAddon.api.ts @@ -14,6 +14,22 @@ let page: Page; const width = 800; const height = 600; +const writeRawSync = (page: any, str: string): Promise => writeSync(page, '\' +' + JSON.stringify(str) + '+ \''); + +const testNormalScreenEqual = async (page: any, str: string): Promise => { + await writeRawSync(page, str); + const originalBuffer = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + const result = await page.evaluate(`serializeAddon.serialize();`) as string; + await page.evaluate(`term.reset();`); + await writeRawSync(page, result); + const newBuffer = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + // chai decides -0 and 0 are different number... + // and firefox have a bug that output -0 for unknown reason + assert.equal(JSON.stringify(originalBuffer), JSON.stringify(newBuffer)); +}; + describe('SerializeAddon', () => { before(async function(): Promise { const browserType = getBrowserType(); @@ -27,19 +43,74 @@ describe('SerializeAddon', () => { await page.evaluate(` window.serializeAddon = new SerializeAddon(); window.term.loadAddon(window.serializeAddon); + window.inspectBuffer = (buffer) => { + const lines = []; + for (let i = 0; i < buffer.length; i++) { + // Do this intentionally to get content of underlining source + const bufferLine = buffer.getLine(i)._line; + lines.push(JSON.stringify(bufferLine)); + } + return { + x: buffer.cursorX, + y: buffer.cursorY, + data: lines + }; + } `); }); after(async () => await browser.close()); beforeEach(async () => await page.evaluate(`window.term.reset()`)); + it('produce different output when we call test util with different text', async function(): Promise { + await writeRawSync(page, '12345'); + const buffer1 = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + await page.evaluate(`term.reset();`); + await writeRawSync(page, '67890'); + const buffer2 = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + assert.throw(() => { + assert.equal(JSON.stringify(buffer1), JSON.stringify(buffer2)); + }); + }); + + it('produce different output when we call test util with different line wrap', async function(): Promise { + await writeRawSync(page, '1234567890\r\n12345'); + const buffer3 = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + await page.evaluate(`term.reset();`); + await writeRawSync(page, '1234567890n12345'); + const buffer4 = await page.evaluate(`inspectBuffer(term.buffer.normal);`); + + assert.throw(() => { + assert.equal(JSON.stringify(buffer3), JSON.stringify(buffer4)); + }); + }); + it('empty content', async function(): Promise { const rows = 10; const cols = 10; assert.equal(await page.evaluate(`serializeAddon.serialize();`), ''); }); - it('trim last empty lines', async function(): Promise { + it('unwrap wrapped line', async function(): Promise { + const lines = ['123456789123456789']; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + it('does not unwrap non-wrapped line', async function(): Promise { + const lines = [ + '123456789', + '123456789' + ]; + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); + }); + + + it('preserve last empty lines', async function(): Promise { const cols = 10; const lines = [ '', @@ -55,7 +126,7 @@ describe('SerializeAddon', () => { '' ]; await writeSync(page, lines.join('\\r\\n')); - assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.slice(0, 8).join('\r\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); }); it('digits content', async function(): Promise { @@ -67,21 +138,22 @@ describe('SerializeAddon', () => { assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); }); - it('serialize half rows of content', async function(): Promise { - const rows = 10; - const halfRows = rows >> 1; + it('serialize with half of scrollback', async function(): Promise { + const rows = 20; + const scrollback = rows - 10; + const halfScrollback = scrollback / 2; const cols = 10; const lines = newArray((index: number) => digitsString(cols, index), rows); await writeSync(page, lines.join('\\r\\n')); - assert.equal(await page.evaluate(`serializeAddon.serialize(${halfRows});`), lines.slice(halfRows, 2 * halfRows).join('\r\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize(${halfScrollback});`), lines.slice(halfScrollback, rows).join('\r\n')); }); - it('serialize 0 rows of content', async function(): Promise { - const rows = 10; + it('serialize 0 rows of scrollback', async function(): Promise { + const rows = 20; const cols = 10; const lines = newArray((index: number) => digitsString(cols, index), rows); await writeSync(page, lines.join('\\r\\n')); - assert.equal(await page.evaluate(`serializeAddon.serialize(0);`), ''); + assert.equal(await page.evaluate(`serializeAddon.serialize(0);`), lines.slice(rows - 10, rows).join('\r\n')); }); it('serialize all rows of content with color16', async function(): Promise { @@ -276,17 +348,15 @@ describe('SerializeAddon', () => { '中文中文', '12中文', '中文12', - '1中文中文中' // this line is going to be wrapped at last character because it has line length of 11 (1+2*5) - ]; - const expected = [ - '中文中文', - '12中文', - '中文12', - '1中文中文', - '中' + // This line is going to be wrapped at last character + // because it has line length of 11 (1+2*5). + // We concat it back without the null cell currently. + // But this may be incorrect. + // see also #3097 + '1中文中文中' ]; await writeSync(page, lines.join('\\r\\n')); - assert.equal(await page.evaluate(`serializeAddon.serialize();`), expected.join('\r\n')); + assert.equal(await page.evaluate(`serializeAddon.serialize();`), lines.join('\r\n')); }); it('serialize CJK Mixed with tab correctly', async () => { @@ -299,6 +369,118 @@ describe('SerializeAddon', () => { await writeSync(page, lines.join('\\r\\n')); assert.equal(await page.evaluate(`serializeAddon.serialize();`), expected.join('\r\n')); }); + + it('serialize with alt screen correctly', async () => { + const SMCUP = '\u001b[?1049h'; + const CUP = '\u001b[H'; + + const lines = [ + `1${SMCUP}${CUP}2` + ]; + const expected = [ + `1${SMCUP}${CUP}2` + ]; + + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`window.term.buffer.active.type`), 'alternate'); + assert.equal(JSON.stringify(await page.evaluate(`serializeAddon.serialize();`)), JSON.stringify(expected.join('\r\n'))); + }); + + it('serialize without alt screen correctly', async () => { + const SMCUP = '\u001b[?1049h'; + const RMCUP = '\u001b[?1049l'; + + const lines = [ + `1${SMCUP}2${RMCUP}` + ]; + const expected = [ + `1` + ]; + + await writeSync(page, lines.join('\\r\\n')); + assert.equal(await page.evaluate(`window.term.buffer.active.type`), 'normal'); + assert.equal(JSON.stringify(await page.evaluate(`serializeAddon.serialize();`)), JSON.stringify(expected.join('\r\n'))); + }); + + it('serialize with background', async () => { + const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; + + const lines = [ + `1\u001b[44m${CLEAR_RIGHT(5)}`, + `2${CLEAR_RIGHT(9)}` + ]; + + await testNormalScreenEqual(page, lines.join('\r\n')); + }); + + it('cause the BCE on scroll', async () => { + const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; + + const padLines = newArray( + (index: number) => digitsString(10, index), + 10 + ); + + const lines = [ + ...padLines, + `\u001b[44m${CLEAR_RIGHT(5)}1111111111111111` + ]; + + await testNormalScreenEqual(page, lines.join('\r\n')); + }); + + it('handle invalid wrap before scroll', async () => { + const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; + const MOVE_UP = (l: number): string => `\u001b[${l}A`; + const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; + const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; + + // A line wrap happened after current line. + // But there is no content. + // so wrap shouldn't even be able to happen. + const segments = [ + `123456789012345`, + MOVE_UP(1), + CLEAR_RIGHT(5), + MOVE_DOWN(1), + MOVE_LEFT(5), + CLEAR_RIGHT(5), + MOVE_UP(1), + '1' + ]; + + await testNormalScreenEqual(page, segments.join('')); + }); + + it('handle invalid wrap after scroll', async () => { + const CLEAR_RIGHT = (l: number): string => `\u001b[${l}X`; + const MOVE_UP = (l: number): string => `\u001b[${l}A`; + const MOVE_DOWN = (l: number): string => `\u001b[${l}B`; + const MOVE_LEFT = (l: number): string => `\u001b[${l}D`; + + const padLines = newArray( + (index: number) => digitsString(10, index), + 10 + ); + + // A line wrap happened after current line. + // But there is no content. + // so wrap shouldn't even be able to happen. + const lines = [ + padLines.join('\r\n'), + '\r\n', + `123456789012345`, + MOVE_UP(1), + CLEAR_RIGHT(5), + MOVE_DOWN(1), + MOVE_LEFT(5), + CLEAR_RIGHT(5), + MOVE_UP(1), + '1' + ]; + + await testNormalScreenEqual(page, lines.join('')); + }); }); function newArray(initial: T | ((index: number) => T), count: number): T[] { diff --git a/package.json b/package.json index 59902931..91d9a646 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "xterm", "description": "Full xterm terminal, in your browser", - "version": "4.9.0", + "version": "4.10.0", "main": "lib/xterm.js", "style": "css/xterm.css", "types": "typings/xterm.d.ts", @@ -11,6 +11,7 @@ "prepackage": "npm run build", "package": "webpack", "start": "node demo/start", + "start-debug": "node --inspect-brk demo/start", "lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/", "test": "npm run test-unit", "posttest": "npm run lint", diff --git a/src/browser/Terminal.test.ts b/src/browser/Terminal.test.ts index 8626be09..8f919302 100644 --- a/src/browser/Terminal.test.ts +++ b/src/browser/Terminal.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { assert, expect } from 'chai'; +import { assert } from 'chai'; import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from 'browser/TestUtils.test'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; import { CellData } from 'common/buffer/CellData'; @@ -75,7 +75,7 @@ describe('Terminal', () => { it('should fire a key event after a keypress DOM event', (done) => { term.onKey(e => { assert.equal(typeof e.key, 'string'); - expect(e.domEvent).to.be.an.instanceof(Object); + assert.equal(e.domEvent instanceof Object, true); done(); }); const evKeyPress = { @@ -89,7 +89,7 @@ describe('Terminal', () => { it('should fire a key event after a keydown DOM event', (done) => { term.onKey(e => { assert.equal(typeof e.key, 'string'); - expect(e.domEvent).to.be.an.instanceof(Object); + assert.equal(e.domEvent instanceof Object, true); done(); }); (term).textarea = { value: '' }; @@ -103,7 +103,6 @@ describe('Terminal', () => { }); it('should fire the onResize event', (done) => { term.onResize(e => { - expect(e).to.have.keys(['cols', 'rows']); assert.equal(typeof e.cols, 'number'); assert.equal(typeof e.rows, 'number'); done(); @@ -724,10 +723,10 @@ describe('Terminal', () => { for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.writeSync(high + String.fromCharCode(i)); const tchar = term.buffer.lines.get(0)!.loadCell(0, cell); - expect(tchar.getChars()).eql(high + String.fromCharCode(i)); - expect(tchar.getChars().length).eql(2); - expect(tchar.getWidth()).eql(1); - expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql(''); + assert.equal(tchar.getChars(), high + String.fromCharCode(i)); + assert.equal(tchar.getChars().length, 2); + assert.equal(tchar.getWidth(), 1); + assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), ''); term.reset(); } }); @@ -737,9 +736,9 @@ describe('Terminal', () => { for (let i = 0xDC00; i <= 0xDCFF; ++i) { term.buffer.x = term.cols - 1; term.writeSync(high + String.fromCharCode(i)); - expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i)); - expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length).eql(2); - expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql(''); + assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars(), high + String.fromCharCode(i)); + assert.equal(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length, 2); + assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), ''); term.reset(); } }); @@ -750,10 +749,10 @@ describe('Terminal', () => { term.buffer.x = term.cols - 1; term.writeSync('a' + high + String.fromCharCode(i)); - expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql('a'); - expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i)); - expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length).eql(2); - expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql(''); + assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), 'a'); + assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars(), high + String.fromCharCode(i)); + assert.equal(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length, 2); + assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), ''); term.reset(); } }); @@ -769,9 +768,9 @@ describe('Terminal', () => { } term.writeSync('a' + high + String.fromCharCode(i)); // auto wraparound mode should cut off the rest of the line - expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i)); - expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length).eql(2); - expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql(''); + assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars(), high + String.fromCharCode(i)); + assert.equal(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length, 2); + assert.equal(term.buffer.lines.get(1)!.loadCell(1, cell).getChars(), ''); term.reset(); } }); @@ -782,10 +781,10 @@ describe('Terminal', () => { term.writeSync(high); term.writeSync(String.fromCharCode(i)); const tchar = term.buffer.lines.get(0)!.loadCell(0, cell); - expect(tchar.getChars()).eql(high + String.fromCharCode(i)); - expect(tchar.getChars().length).eql(2); - expect(tchar.getWidth()).eql(1); - expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql(''); + assert.equal(tchar.getChars(), high + String.fromCharCode(i)); + assert.equal(tchar.getChars().length, 2); + assert.equal(tchar.getWidth(), 1); + assert.equal(term.buffer.lines.get(0)!.loadCell(1, cell).getChars(), ''); term.reset(); } }); @@ -796,81 +795,81 @@ describe('Terminal', () => { it('café', () => { term.writeSync('cafe\u0301'); term.buffer.lines.get(0)!.loadCell(3, cell); - expect(cell.getChars()).eql('e\u0301'); - expect(cell.getChars().length).eql(2); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), 'e\u0301'); + assert.equal(cell.getChars().length, 2); + assert.equal(cell.getWidth(), 1); }); it('café - end of line', () => { term.buffer.x = term.cols - 1 - 3; term.writeSync('cafe\u0301'); term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell); - expect(cell.getChars()).eql('e\u0301'); - expect(cell.getChars().length).eql(2); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), 'e\u0301'); + assert.equal(cell.getChars().length, 2); + assert.equal(cell.getWidth(), 1); term.buffer.lines.get(0)!.loadCell(1, cell); - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 1); }); it('multiple combined é', () => { term.writeSync(Array(100).join('e\u0301')); for (let i = 0; i < term.cols; ++i) { term.buffer.lines.get(0)!.loadCell(i, cell); - expect(cell.getChars()).eql('e\u0301'); - expect(cell.getChars().length).eql(2); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), 'e\u0301'); + assert.equal(cell.getChars().length, 2); + assert.equal(cell.getWidth(), 1); } term.buffer.lines.get(1)!.loadCell(0, cell); - expect(cell.getChars()).eql('e\u0301'); - expect(cell.getChars().length).eql(2); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), 'e\u0301'); + assert.equal(cell.getChars().length, 2); + assert.equal(cell.getWidth(), 1); }); it('multiple surrogate with combined', () => { term.writeSync(Array(100).join('\uD800\uDC00\u0301')); for (let i = 0; i < term.cols; ++i) { term.buffer.lines.get(0)!.loadCell(i, cell); - expect(cell.getChars()).eql('\uD800\uDC00\u0301'); - expect(cell.getChars().length).eql(3); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), '\uD800\uDC00\u0301'); + assert.equal(cell.getChars().length, 3); + assert.equal(cell.getWidth(), 1); } term.buffer.lines.get(1)!.loadCell(0, cell); - expect(cell.getChars()).eql('\uD800\uDC00\u0301'); - expect(cell.getChars().length).eql(3); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), '\uD800\uDC00\u0301'); + assert.equal(cell.getChars().length, 3); + assert.equal(cell.getWidth(), 1); }); }); describe('unicode - fullwidth characters', () => { const cell = new CellData(); it('cursor movement even', () => { - expect(term.buffer.x).eql(0); + assert.equal(term.buffer.x, 0); term.writeSync('¥'); - expect(term.buffer.x).eql(2); + assert.equal(term.buffer.x, 2); }); it('cursor movement odd', () => { term.buffer.x = 1; - expect(term.buffer.x).eql(1); + assert.equal(term.buffer.x, 1); term.writeSync('¥'); - expect(term.buffer.x).eql(3); + assert.equal(term.buffer.x, 3); }); it('line of ¥ even', () => { term.writeSync(Array(50).join('¥')); for (let i = 0; i < term.cols; ++i) { term.buffer.lines.get(0)!.loadCell(i, cell); if (i % 2) { - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(0); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 0); } else { - expect(cell.getChars()).eql('¥'); - expect(cell.getChars().length).eql(1); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '¥'); + assert.equal(cell.getChars().length, 1); + assert.equal(cell.getWidth(), 2); } } term.buffer.lines.get(1)!.loadCell(0, cell); - expect(cell.getChars()).eql('¥'); - expect(cell.getChars().length).eql(1); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '¥'); + assert.equal(cell.getChars().length, 1); + assert.equal(cell.getWidth(), 2); }); it('line of ¥ odd', () => { term.buffer.x = 1; @@ -878,23 +877,23 @@ describe('Terminal', () => { for (let i = 1; i < term.cols - 1; ++i) { term.buffer.lines.get(0)!.loadCell(i, cell); if (!(i % 2)) { - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(0); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 0); } else { - expect(cell.getChars()).eql('¥'); - expect(cell.getChars().length).eql(1); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '¥'); + assert.equal(cell.getChars().length, 1); + assert.equal(cell.getWidth(), 2); } } term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell); - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 1); term.buffer.lines.get(1)!.loadCell(0, cell); - expect(cell.getChars()).eql('¥'); - expect(cell.getChars().length).eql(1); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '¥'); + assert.equal(cell.getChars().length, 1); + assert.equal(cell.getWidth(), 2); }); it('line of ¥ with combining odd', () => { term.buffer.x = 1; @@ -902,42 +901,42 @@ describe('Terminal', () => { for (let i = 1; i < term.cols - 1; ++i) { term.buffer.lines.get(0)!.loadCell(i, cell); if (!(i % 2)) { - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(0); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 0); } else { - expect(cell.getChars()).eql('¥\u0301'); - expect(cell.getChars().length).eql(2); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '¥\u0301'); + assert.equal(cell.getChars().length, 2); + assert.equal(cell.getWidth(), 2); } } term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell); - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 1); term.buffer.lines.get(1)!.loadCell(0, cell); - expect(cell.getChars()).eql('¥\u0301'); - expect(cell.getChars().length).eql(2); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '¥\u0301'); + assert.equal(cell.getChars().length, 2); + assert.equal(cell.getWidth(), 2); }); it('line of ¥ with combining even', () => { term.writeSync(Array(50).join('¥\u0301')); for (let i = 0; i < term.cols; ++i) { term.buffer.lines.get(0)!.loadCell(i, cell); if (i % 2) { - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(0); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 0); } else { - expect(cell.getChars()).eql('¥\u0301'); - expect(cell.getChars().length).eql(2); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '¥\u0301'); + assert.equal(cell.getChars().length, 2); + assert.equal(cell.getWidth(), 2); } } term.buffer.lines.get(1)!.loadCell(0, cell); - expect(cell.getChars()).eql('¥\u0301'); - expect(cell.getChars().length).eql(2); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '¥\u0301'); + assert.equal(cell.getChars().length, 2); + assert.equal(cell.getWidth(), 2); }); it('line of surrogate fullwidth with combining odd', () => { term.buffer.x = 1; @@ -945,42 +944,42 @@ describe('Terminal', () => { for (let i = 1; i < term.cols - 1; ++i) { term.buffer.lines.get(0)!.loadCell(i, cell); if (!(i % 2)) { - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(0); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 0); } else { - expect(cell.getChars()).eql('\ud843\ude6d\u0301'); - expect(cell.getChars().length).eql(3); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '\ud843\ude6d\u0301'); + assert.equal(cell.getChars().length, 3); + assert.equal(cell.getWidth(), 2); } } term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell); - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(1); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 1); term.buffer.lines.get(1)!.loadCell(0, cell); - expect(cell.getChars()).eql('\ud843\ude6d\u0301'); - expect(cell.getChars().length).eql(3); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '\ud843\ude6d\u0301'); + assert.equal(cell.getChars().length, 3); + assert.equal(cell.getWidth(), 2); }); it('line of surrogate fullwidth with combining even', () => { term.writeSync(Array(50).join('\ud843\ude6d\u0301')); for (let i = 0; i < term.cols; ++i) { term.buffer.lines.get(0)!.loadCell(i, cell); if (i % 2) { - expect(cell.getChars()).eql(''); - expect(cell.getChars().length).eql(0); - expect(cell.getWidth()).eql(0); + assert.equal(cell.getChars(), ''); + assert.equal(cell.getChars().length, 0); + assert.equal(cell.getWidth(), 0); } else { - expect(cell.getChars()).eql('\ud843\ude6d\u0301'); - expect(cell.getChars().length).eql(3); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '\ud843\ude6d\u0301'); + assert.equal(cell.getChars().length, 3); + assert.equal(cell.getWidth(), 2); } } term.buffer.lines.get(1)!.loadCell(0, cell); - expect(cell.getChars()).eql('\ud843\ude6d\u0301'); - expect(cell.getChars().length).eql(3); - expect(cell.getWidth()).eql(2); + assert.equal(cell.getChars(), '\ud843\ude6d\u0301'); + assert.equal(cell.getChars().length, 3); + assert.equal(cell.getWidth(), 2); }); }); @@ -992,11 +991,11 @@ describe('Terminal', () => { term.buffer.y = 0; term.write('\x1b[4h'); term.writeSync('abcde'); - expect(term.buffer.lines.get(0)!.length).eql(term.cols); - expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a'); - expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('e'); - expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('0'); - expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('4'); + assert.equal(term.buffer.lines.get(0)!.length, term.cols); + assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), 'a'); + assert.equal(term.buffer.lines.get(0)!.loadCell(14, cell).getChars(), 'e'); + assert.equal(term.buffer.lines.get(0)!.loadCell(15, cell).getChars(), '0'); + assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), '4'); }); it('fullwidth - insert', () => { term.writeSync(Array(9).join('0123456789').slice(-80)); @@ -1004,12 +1003,12 @@ describe('Terminal', () => { term.buffer.y = 0; term.write('\x1b[4h'); term.writeSync('¥¥¥'); - expect(term.buffer.lines.get(0)!.length).eql(term.cols); - expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('¥'); - expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql(''); - expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('¥'); - expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql(''); - expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('3'); + assert.equal(term.buffer.lines.get(0)!.length, term.cols); + assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), '¥'); + assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), ''); + assert.equal(term.buffer.lines.get(0)!.loadCell(14, cell).getChars(), '¥'); + assert.equal(term.buffer.lines.get(0)!.loadCell(15, cell).getChars(), ''); + assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), '3'); }); it('fullwidth - right border', () => { term.writeSync(Array(41).join('¥')); @@ -1017,15 +1016,15 @@ describe('Terminal', () => { term.buffer.y = 0; term.write('\x1b[4h'); term.writeSync('a'); - expect(term.buffer.lines.get(0)!.length).eql(term.cols); - expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a'); - expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('¥'); - expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced + assert.equal(term.buffer.lines.get(0)!.length, term.cols); + assert.equal(term.buffer.lines.get(0)!.loadCell(10, cell).getChars(), 'a'); + assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), '¥'); + assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), ''); // fullwidth char got replaced term.writeSync('b'); - expect(term.buffer.lines.get(0)!.length).eql(term.cols); - expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('b'); - expect(term.buffer.lines.get(0)!.loadCell(12, cell).getChars()).eql('¥'); - expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth + assert.equal(term.buffer.lines.get(0)!.length, term.cols); + assert.equal(term.buffer.lines.get(0)!.loadCell(11, cell).getChars(), 'b'); + assert.equal(term.buffer.lines.get(0)!.loadCell(12, cell).getChars(), '¥'); + assert.equal(term.buffer.lines.get(0)!.loadCell(79, cell).getChars(), ''); // empty cell after fullwidth }); }); @@ -1346,7 +1345,7 @@ describe('Terminal', () => { ]; terminal.writeSync(data.join('')); // brute force test with insane values - expect(() => { + assert.doesNotThrow(() => { for (let overscan = 0; overscan < 20; ++overscan) { for (let start = -10; start < 20; ++start) { for (let end = -10; end < 20; ++end) { @@ -1357,7 +1356,7 @@ describe('Terminal', () => { } } } - }).to.not.throw(); + }); }); }); @@ -1406,18 +1405,18 @@ describe('Terminal', () => { // not converting const termNotConverting = new TestTerminal({cols: 15, rows: 10}); termNotConverting.writeSync('Hello\nWorld'); - expect(termNotConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello '); - expect(termNotConverting.buffer.lines.get(1)!.translateToString(false)).equals(' World '); - expect(termNotConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello'); - expect(termNotConverting.buffer.lines.get(1)!.translateToString(true)).equals(' World'); + assert.equal(termNotConverting.buffer.lines.get(0)!.translateToString(false), 'Hello '); + assert.equal(termNotConverting.buffer.lines.get(1)!.translateToString(false), ' World '); + assert.equal(termNotConverting.buffer.lines.get(0)!.translateToString(true), 'Hello'); + assert.equal(termNotConverting.buffer.lines.get(1)!.translateToString(true), ' World'); // converting const termConverting = new TestTerminal({cols: 15, rows: 10, convertEol: true}); termConverting.writeSync('Hello\nWorld'); - expect(termConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello '); - expect(termConverting.buffer.lines.get(1)!.translateToString(false)).equals('World '); - expect(termConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello'); - expect(termConverting.buffer.lines.get(1)!.translateToString(true)).equals('World'); + assert.equal(termConverting.buffer.lines.get(0)!.translateToString(false), 'Hello '); + assert.equal(termConverting.buffer.lines.get(1)!.translateToString(false), 'World '); + assert.equal(termConverting.buffer.lines.get(0)!.translateToString(true), 'Hello'); + assert.equal(termConverting.buffer.lines.get(1)!.translateToString(true), 'World'); }); describe('Terminal InputHandler integration', () => { function getLines(term: TestTerminal, limit: number = term.rows): string[] { diff --git a/src/browser/Terminal.ts b/src/browser/Terminal.ts index 06ac57f3..b0964c21 100644 --- a/src/browser/Terminal.ts +++ b/src/browser/Terminal.ts @@ -386,7 +386,7 @@ export class Terminal extends CoreTerminal implements ITerminal { throw new Error('Terminal requires a parent element.'); } - if (!document.body.contains(parent)) { + if (!parent.isConnected) { this._logService.debug('Terminal.open was called on an element that was not attached to the DOM'); } diff --git a/src/common/Clone.test.ts b/src/common/Clone.test.ts index 370538ea..5fbe4bf9 100644 --- a/src/common/Clone.test.ts +++ b/src/common/Clone.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { assert, expect } from 'chai'; +import { assert } from 'chai'; import { clone } from 'common/Clone'; describe('clone', () => { @@ -124,6 +124,6 @@ describe('clone', () => { test.a.b.c = test; - expect(() => clone(test)).to.not.throw(); + assert.doesNotThrow(() => clone(test)); }); }); diff --git a/src/common/InputHandler.test.ts b/src/common/InputHandler.test.ts index 4c0d8559..e1fcd727 100644 --- a/src/common/InputHandler.test.ts +++ b/src/common/InputHandler.test.ts @@ -3,7 +3,7 @@ * @license MIT */ -import { assert, expect } from 'chai'; +import { assert } from 'chai'; import { InputHandler } from 'common/InputHandler'; import { IBufferLine, IAttributeData, IAnsiColorChangeEvent } from 'common/Types'; import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine'; @@ -136,7 +136,16 @@ describe('InputHandler', () => { it('insertChars', function(): void { const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); + const inputHandler = new TestInputHandler( + bufferService, + new MockCharsetService(), + new MockCoreService(), + new MockDirtyRowService(), + new MockLogService(), + new MockOptionsService(), + new MockCoreMouseService(), + new MockUnicodeService() + ); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -144,36 +153,45 @@ describe('InputHandler', () => { inputHandler.parse(Array(bufferService.cols - 9).join('a')); inputHandler.parse('1234567890'); const line1: IBufferLine = bufferService.buffer.lines.get(0)!; - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890'); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '1234567890'); // insert one char from params = [0] bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.insertChars(Params.fromArray([0])); - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456789'); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 123456789'); // insert one char from params = [1] bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.insertChars(Params.fromArray([1])); - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 12345678'); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 12345678'); // insert two chars from params = [2] bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.insertChars(Params.fromArray([2])); - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456'); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' 123456'); // insert 10 chars from params = [10] bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.insertChars(Params.fromArray([10])); - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' '); - expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a')); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' '); + assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a')); }); it('deleteChars', function(): void { const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); + const inputHandler = new TestInputHandler( + bufferService, + new MockCharsetService(), + new MockCoreService(), + new MockDirtyRowService(), + new MockLogService(), + new MockOptionsService(), + new MockCoreMouseService(), + new MockUnicodeService() + ); // insert some data in first and second line inputHandler.parse(Array(bufferService.cols - 9).join('a')); @@ -181,39 +199,49 @@ describe('InputHandler', () => { inputHandler.parse(Array(bufferService.cols - 9).join('a')); inputHandler.parse('1234567890'); const line1: IBufferLine = bufferService.buffer.lines.get(0)!; - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890'); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '1234567890'); // delete one char from params = [0] bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.deleteChars(Params.fromArray([0])); - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '234567890 '); - expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '234567890'); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '234567890 '); + assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '234567890'); // insert one char from params = [1] bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.deleteChars(Params.fromArray([1])); - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '34567890 '); - expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '34567890'); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '34567890 '); + assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '34567890'); // insert two chars from params = [2] bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.deleteChars(Params.fromArray([2])); - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '567890 '); - expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '567890'); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + '567890 '); + assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a') + '567890'); + // insert 10 chars from params = [10] bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.deleteChars(Params.fromArray([10])); - expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' '); - expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a')); + assert.equal(line1.translateToString(false), Array(bufferService.cols - 9).join('a') + ' '); + assert.equal(line1.translateToString(true), Array(bufferService.cols - 9).join('a')); }); it('eraseInLine', function(): void { const bufferService = new MockBufferService(80, 30); - const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); + const inputHandler = new TestInputHandler( + bufferService, + new MockCharsetService(), + new MockCoreService(), + new MockDirtyRowService(), + new MockLogService(), + new MockOptionsService(), + new MockCoreMouseService(), + new MockUnicodeService() + ); // fill 6 lines to test 3 different states inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -224,24 +252,33 @@ describe('InputHandler', () => { bufferService.buffer.y = 0; bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([0])); - expect(bufferService.buffer.lines.get(0)!.translateToString(false)).equals(Array(71).join('a') + ' '); + assert.equal(bufferService.buffer.lines.get(0)!.translateToString(false), Array(71).join('a') + ' '); // params[1] - left erase bufferService.buffer.y = 1; bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([1])); - expect(bufferService.buffer.lines.get(1)!.translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa'); + assert.equal(bufferService.buffer.lines.get(1)!.translateToString(false), Array(71).join(' ') + ' aaaaaaaaa'); // params[1] - left erase bufferService.buffer.y = 2; bufferService.buffer.x = 70; inputHandler.eraseInLine(Params.fromArray([2])); - expect(bufferService.buffer.lines.get(2)!.translateToString(false)).equals(Array(bufferService.cols + 1).join(' ')); + assert.equal(bufferService.buffer.lines.get(2)!.translateToString(false), Array(bufferService.cols + 1).join(' ')); }); it('eraseInDisplay', function(): void { const bufferService = new MockBufferService(80, 7); - const inputHandler = new InputHandler(bufferService, new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); + const inputHandler = new TestInputHandler( + bufferService, + new MockCharsetService(), + new MockCoreService(), + new MockDirtyRowService(), + new MockLogService(), + new MockOptionsService(), + new MockCoreMouseService(), + new MockUnicodeService() + ); // fill display with a's for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a')); @@ -250,7 +287,7 @@ describe('InputHandler', () => { bufferService.buffer.y = 5; bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([0])); - expect(termContent(bufferService, false)).eql([ + assert.deepEqual(termContent(bufferService, false), [ Array(bufferService.cols + 1).join('a'), Array(bufferService.cols + 1).join('a'), Array(bufferService.cols + 1).join('a'), @@ -259,7 +296,7 @@ describe('InputHandler', () => { Array(40 + 1).join('a') + Array(bufferService.cols - 40 + 1).join(' '), Array(bufferService.cols + 1).join(' ') ]); - expect(termContent(bufferService, true)).eql([ + assert.deepEqual(termContent(bufferService, true), [ Array(bufferService.cols + 1).join('a'), Array(bufferService.cols + 1).join('a'), Array(bufferService.cols + 1).join('a'), @@ -278,7 +315,7 @@ describe('InputHandler', () => { bufferService.buffer.y = 5; bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([1])); - expect(termContent(bufferService, false)).eql([ + assert.deepEqual(termContent(bufferService, false), [ Array(bufferService.cols + 1).join(' '), Array(bufferService.cols + 1).join(' '), Array(bufferService.cols + 1).join(' '), @@ -287,7 +324,7 @@ describe('InputHandler', () => { Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'), Array(bufferService.cols + 1).join('a') ]); - expect(termContent(bufferService, true)).eql([ + assert.deepEqual(termContent(bufferService, true), [ '', '', '', @@ -306,7 +343,7 @@ describe('InputHandler', () => { bufferService.buffer.y = 5; bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([2])); - expect(termContent(bufferService, false)).eql([ + assert.deepEqual(termContent(bufferService, false), [ Array(bufferService.cols + 1).join(' '), Array(bufferService.cols + 1).join(' '), Array(bufferService.cols + 1).join(' '), @@ -315,7 +352,7 @@ describe('InputHandler', () => { Array(bufferService.cols + 1).join(' '), Array(bufferService.cols + 1).join(' ') ]); - expect(termContent(bufferService, true)).eql([ + assert.deepEqual(termContent(bufferService, true), [ '', '', '', @@ -334,11 +371,11 @@ describe('InputHandler', () => { // params[1] left and above with wrap // confirm precondition that line 2 is wrapped - expect(bufferService.buffer.lines.get(2)!.isWrapped).true; + assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 2; bufferService.buffer.x = 40; inputHandler.eraseInDisplay(Params.fromArray([1])); - expect(bufferService.buffer.lines.get(2)!.isWrapped).false; + assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false); // reset and add a wrapped line bufferService.buffer.y = 0; @@ -349,16 +386,25 @@ describe('InputHandler', () => { // params[1] left and above with wrap // confirm precondition that line 2 is wrapped - expect(bufferService.buffer.lines.get(2)!.isWrapped).true; + assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, true); bufferService.buffer.y = 1; bufferService.buffer.x = 90; // Cursor is beyond last column inputHandler.eraseInDisplay(Params.fromArray([1])); - expect(bufferService.buffer.lines.get(2)!.isWrapped).false; + assert.equal(bufferService.buffer.lines.get(2)!.isWrapped, false); }); }); describe('print', () => { it('should not cause an infinite loop (regression test)', () => { - const inputHandler = new InputHandler(new MockBufferService(80, 30), new MockCharsetService(), new MockCoreService(), new MockDirtyRowService(), new MockLogService(), new MockOptionsService(), new MockCoreMouseService(), new MockUnicodeService()); + const inputHandler = new TestInputHandler( + new MockBufferService(80, 30), + new MockCharsetService(), + new MockCoreService(), + new MockDirtyRowService(), + new MockLogService(), + new MockOptionsService(), + new MockCoreMouseService(), + new MockUnicodeService() + ); const container = new Uint32Array(10); container[0] = 0x200B; inputHandler.print(container, 0, 1); @@ -383,48 +429,48 @@ describe('InputHandler', () => { }); it('should handle DECSET/DECRST 47 (alt screen buffer)', () => { handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST'); - expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal(''); - expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST'); + assert.equal(bufferService.buffer.translateBufferLineToString(0, true), ''); + assert.equal(bufferService.buffer.translateBufferLineToString(1, true), ' TEST'); // Text color of 'TEST' should be red - expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1); + assert.equal((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor()), 1); }); it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => { handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST'); - expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal(''); - expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST'); + assert.equal(bufferService.buffer.translateBufferLineToString(0, true), ''); + assert.equal(bufferService.buffer.translateBufferLineToString(1, true), ' TEST'); // Text color of 'TEST' should be red - expect((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor())).to.equal(1); + assert.equal((bufferService.buffer.lines.get(1)!.loadCell(4, new CellData()).getFgColor()), 1); }); it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => { handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST'); - expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); - expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('JUNK'); + assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST'); + assert.equal(bufferService.buffer.translateBufferLineToString(1, true), 'JUNK'); // Text color of 'TEST' should be default - expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, DEFAULT_ATTR_DATA.fg); // Text color of 'JUNK' should be red - expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1); + assert.equal((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor()), 1); }); it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => { handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST'); - expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); - expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(''); + assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST'); + assert.equal(bufferService.buffer.translateBufferLineToString(1, true), ''); // Text color of 'TEST' should be default - expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, DEFAULT_ATTR_DATA.fg); }); it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => { handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST'); - expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST'); + assert.equal(bufferService.buffer.translateBufferLineToString(0, true), 'TEST'); // Text color of 'TEST' should be default - expect(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg); + assert.equal(bufferService.buffer.lines.get(0)!.loadCell(0, new CellData()).fg, DEFAULT_ATTR_DATA.fg); handler.parse('\x1b[?1049h\x1b[uTEST'); - expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('TEST'); + assert.equal(bufferService.buffer.translateBufferLineToString(1, true), 'TEST'); // Text color of 'TEST' should be red - expect((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor())).to.equal(1); + assert.equal((bufferService.buffer.lines.get(1)!.loadCell(0, new CellData()).getFgColor()), 1); }); it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => { handler.parse('\x1b[42m\x1b[?1049h'); // Buffer should be filled with green background - expect(bufferService.buffer.lines.get(20)!.loadCell(10, new CellData()).getBgColor()).to.equal(2); + assert.equal(bufferService.buffer.lines.get(20)!.loadCell(10, new CellData()).getBgColor(), 2); }); }); diff --git a/src/common/buffer/BufferLine.test.ts b/src/common/buffer/BufferLine.test.ts index 4c5fc3a4..fa15a854 100644 --- a/src/common/buffer/BufferLine.test.ts +++ b/src/common/buffer/BufferLine.test.ts @@ -6,7 +6,7 @@ import { NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, DEFAULT_ATTR, Content, import { BufferLine } from 'common/buffer//BufferLine'; import { CellData } from 'common/buffer/CellData'; import { CharData, IBufferLine } from '../Types'; -import { assert, expect } from 'chai'; +import { assert } from 'chai'; import { AttributeData } from 'common/buffer/AttributeData'; @@ -151,20 +151,20 @@ describe('CellData', () => { describe('BufferLine', function(): void { it('ctor', function(): void { let line: IBufferLine = new TestBufferLine(0); - expect(line.length).equals(0); - expect(line.isWrapped).equals(false); + assert.equal(line.length, 0); + assert.equal(line.isWrapped, false); line = new TestBufferLine(10); - expect(line.length).equals(10); - expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); - expect(line.isWrapped).equals(false); + assert.equal(line.length, 10); + assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); + assert.equal(line.isWrapped, false); line = new TestBufferLine(10, undefined, true); - expect(line.length).equals(10); - expect(line.loadCell(0, new CellData()).getAsCharData()).eql([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); - expect(line.isWrapped).equals(true); + assert.equal(line.length, 10); + assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]); + assert.equal(line.isWrapped, true); line = new TestBufferLine(10, CellData.fromCharData([123, 'a', 456, 'a'.charCodeAt(0)]), true); - expect(line.length).equals(10); - expect(line.loadCell(0, new CellData()).getAsCharData()).eql([123, 'a', 456, 'a'.charCodeAt(0)]); - expect(line.isWrapped).equals(true); + assert.equal(line.length, 10); + assert.deepEqual(line.loadCell(0, new CellData()).getAsCharData(), [123, 'a', 456, 'a'.charCodeAt(0)]); + assert.equal(line.isWrapped, true); }); it('insertCells', function(): void { const line = new TestBufferLine(3); @@ -172,7 +172,7 @@ describe('BufferLine', function(): void { line.setCell(1, CellData.fromCharData([2, 'b', 0, 'b'.charCodeAt(0)])); line.setCell(2, CellData.fromCharData([3, 'c', 0, 'c'.charCodeAt(0)])); line.insertCells(1, 3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)])); - expect(line.toArray()).eql([ + assert.deepEqual(line.toArray(), [ [1, 'a', 0, 'a'.charCodeAt(0)], [4, 'd', 0, 'd'.charCodeAt(0)], [4, 'd', 0, 'd'.charCodeAt(0)] @@ -186,7 +186,7 @@ describe('BufferLine', function(): void { line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)])); line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)])); line.deleteCells(1, 2, CellData.fromCharData([6, 'f', 0, 'f'.charCodeAt(0)])); - expect(line.toArray()).eql([ + assert.deepEqual(line.toArray(), [ [1, 'a', 0, 'a'.charCodeAt(0)], [4, 'd', 0, 'd'.charCodeAt(0)], [5, 'e', 0, 'e'.charCodeAt(0)], @@ -202,7 +202,7 @@ describe('BufferLine', function(): void { line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)])); line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)])); line.replaceCells(2, 4, CellData.fromCharData([6, 'f', 0, 'f'.charCodeAt(0)])); - expect(line.toArray()).eql([ + assert.deepEqual(line.toArray(), [ [1, 'a', 0, 'a'.charCodeAt(0)], [2, 'b', 0, 'b'.charCodeAt(0)], [6, 'f', 0, 'f'.charCodeAt(0)], @@ -218,7 +218,7 @@ describe('BufferLine', function(): void { line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)])); line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)])); line.fill(CellData.fromCharData([123, 'z', 0, 'z'.charCodeAt(0)])); - expect(line.toArray()).eql([ + assert.deepEqual(line.toArray(), [ [123, 'z', 0, 'z'.charCodeAt(0)], [123, 'z', 0, 'z'.charCodeAt(0)], [123, 'z', 0, 'z'.charCodeAt(0)], @@ -234,9 +234,9 @@ describe('BufferLine', function(): void { line.setCell(3, CellData.fromCharData([4, 'd', 0, 'd'.charCodeAt(0)])); line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)])); const line2 = line.clone(); - expect(TestBufferLine.prototype.toArray.apply(line2)).eql(line.toArray()); - expect(line2.length).equals(line.length); - expect(line2.isWrapped).equals(line.isWrapped); + assert.deepEqual(TestBufferLine.prototype.toArray.apply(line2), line.toArray()); + assert.equal(line2.length, line.length); + assert.equal(line2.isWrapped, line.isWrapped); }); it('copyFrom', function(): void { const line = new TestBufferLine(5); @@ -247,92 +247,92 @@ describe('BufferLine', function(): void { line.setCell(4, CellData.fromCharData([5, 'e', 0, 'e'.charCodeAt(0)])); const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), true); line2.copyFrom(line); - expect(line2.toArray()).eql(line.toArray()); - expect(line2.length).equals(line.length); - expect(line2.isWrapped).equals(line.isWrapped); + assert.deepEqual(line2.toArray(), line.toArray()); + assert.equal(line2.length, line.length); + assert.equal(line2.isWrapped, line.isWrapped); }); it('should support combining chars', function(): void { // CHAR_DATA_CODE_INDEX resembles current behavior in InputHandler.print // --> set code to the last charCodeAt value of the string // Note: needs to be fixed once the string pointer is in place const line = new TestBufferLine(2, CellData.fromCharData([1, 'e\u0301', 0, '\u0301'.charCodeAt(0)])); - expect(line.toArray()).eql([[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]); + assert.deepEqual(line.toArray(), [[1, 'e\u0301', 0, '\u0301'.charCodeAt(0)], [1, 'e\u0301', 0, '\u0301'.charCodeAt(0)]]); const line2 = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, '\u0301'.charCodeAt(0)]), true); line2.copyFrom(line); - expect(line2.toArray()).eql(line.toArray()); + assert.deepEqual(line2.toArray(), line.toArray()); const line3 = line.clone(); - expect(TestBufferLine.prototype.toArray.apply(line3)).eql(line.toArray()); + assert.deepEqual(TestBufferLine.prototype.toArray.apply(line3), line.toArray()); }); describe('resize', function(): void { it('enlarge(false)', function(): void { const line = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false); line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)])); - expect(line.toArray()).eql((Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)])); + assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)])); }); it('enlarge(true)', function(): void { const line = new TestBufferLine(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false); line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)])); - expect(line.toArray()).eql((Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)])); + assert.deepEqual(line.toArray(), (Array(10) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)])); }); it('shrink(true) - should apply new size', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false); line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)])); - expect(line.toArray()).eql((Array(5) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)])); + assert.deepEqual(line.toArray(), (Array(5) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)])); }); it('shrink to 0 length', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false); line.resize(0, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)])); - expect(line.toArray()).eql((Array(0) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)])); + assert.deepEqual(line.toArray(), (Array(0) as any).fill([1, 'a', 0, 'a'.charCodeAt(0)])); }); it('should remove combining data on replaced cells after shrinking then enlarging', () => { const line = new TestBufferLine(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)]), false); line.set(2, [ 0, '😁', 1, '😁'.charCodeAt(0) ]); line.set(9, [ 0, '😁', 1, '😁'.charCodeAt(0) ]); - expect(line.translateToString()).eql('aa😁aaaaaa😁'); - expect(Object.keys(line.combined).length).eql(2); + assert.equal(line.translateToString(), 'aa😁aaaaaa😁'); + assert.equal(Object.keys(line.combined).length, 2); line.resize(5, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)])); - expect(line.translateToString()).eql('aa😁aa'); + assert.equal(line.translateToString(), 'aa😁aa'); line.resize(10, CellData.fromCharData([1, 'a', 0, 'a'.charCodeAt(0)])); - expect(line.translateToString()).eql('aa😁aaaaaaa'); - expect(Object.keys(line.combined).length).eql(1); + assert.equal(line.translateToString(), 'aa😁aaaaaaa'); + assert.equal(Object.keys(line.combined).length, 1); }); }); describe('getTrimLength', function(): void { it('empty line', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); - expect(line.getTrimmedLength()).equal(0); + assert.equal(line.getTrimmedLength(), 0); }); it('ASCII', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); - expect(line.getTrimmedLength()).equal(3); + assert.equal(line.getTrimmedLength(), 3); }); it('surrogate', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(2, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)])); - expect(line.getTrimmedLength()).equal(3); + assert.equal(line.getTrimmedLength(), 3); }); it('combining', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(2, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)])); - expect(line.getTrimmedLength()).equal(3); + assert.equal(line.getTrimmedLength(), 3); }); it('fullwidth', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(2, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)])); line.setCell(3, CellData.fromCharData([0, '', 0, 0])); - expect(line.getTrimmedLength()).equal(4); // also counts null cell after fullwidth + assert.equal(line.getTrimmedLength(), 4); // also counts null cell after fullwidth }); }); describe('translateToString with and w\'o trimming', function(): void { it('empty line', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); - expect(line.translateToString(false)).equal(' '); - expect(line.translateToString(true)).equal(''); + assert.equal(line.translateToString(false), ' '); + assert.equal(line.translateToString(true), ''); }); it('ASCII', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); @@ -340,14 +340,14 @@ describe('BufferLine', function(): void { line.setCell(2, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(5, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); - expect(line.translateToString(false)).equal('a a aa '); - expect(line.translateToString(true)).equal('a a aa'); - expect(line.translateToString(false, 0, 5)).equal('a a a'); - expect(line.translateToString(false, 0, 4)).equal('a a '); - expect(line.translateToString(false, 0, 3)).equal('a a'); - expect(line.translateToString(true, 0, 5)).equal('a a a'); - expect(line.translateToString(true, 0, 4)).equal('a a '); - expect(line.translateToString(true, 0, 3)).equal('a a'); + assert.equal(line.translateToString(false), 'a a aa '); + assert.equal(line.translateToString(true), 'a a aa'); + assert.equal(line.translateToString(false, 0, 5), 'a a a'); + assert.equal(line.translateToString(false, 0, 4), 'a a '); + assert.equal(line.translateToString(false, 0, 3), 'a a'); + assert.equal(line.translateToString(true, 0, 5), 'a a a'); + assert.equal(line.translateToString(true, 0, 4), 'a a '); + assert.equal(line.translateToString(true, 0, 3), 'a a'); }); it('surrogate', function(): void { @@ -356,14 +356,14 @@ describe('BufferLine', function(): void { line.setCell(2, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)])); line.setCell(4, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)])); line.setCell(5, CellData.fromCharData([1, '𝄞', 1, '𝄞'.charCodeAt(0)])); - expect(line.translateToString(false)).equal('a 𝄞 𝄞𝄞 '); - expect(line.translateToString(true)).equal('a 𝄞 𝄞𝄞'); - expect(line.translateToString(false, 0, 5)).equal('a 𝄞 𝄞'); - expect(line.translateToString(false, 0, 4)).equal('a 𝄞 '); - expect(line.translateToString(false, 0, 3)).equal('a 𝄞'); - expect(line.translateToString(true, 0, 5)).equal('a 𝄞 𝄞'); - expect(line.translateToString(true, 0, 4)).equal('a 𝄞 '); - expect(line.translateToString(true, 0, 3)).equal('a 𝄞'); + assert.equal(line.translateToString(false), 'a 𝄞 𝄞𝄞 '); + assert.equal(line.translateToString(true), 'a 𝄞 𝄞𝄞'); + assert.equal(line.translateToString(false, 0, 5), 'a 𝄞 𝄞'); + assert.equal(line.translateToString(false, 0, 4), 'a 𝄞 '); + assert.equal(line.translateToString(false, 0, 3), 'a 𝄞'); + assert.equal(line.translateToString(true, 0, 5), 'a 𝄞 𝄞'); + assert.equal(line.translateToString(true, 0, 4), 'a 𝄞 '); + assert.equal(line.translateToString(true, 0, 3), 'a 𝄞'); }); it('combining', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); @@ -371,14 +371,14 @@ describe('BufferLine', function(): void { line.setCell(2, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)])); line.setCell(4, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)])); line.setCell(5, CellData.fromCharData([1, 'e\u0301', 1, '\u0301'.charCodeAt(0)])); - expect(line.translateToString(false)).equal('a e\u0301 e\u0301e\u0301 '); - expect(line.translateToString(true)).equal('a e\u0301 e\u0301e\u0301'); - expect(line.translateToString(false, 0, 5)).equal('a e\u0301 e\u0301'); - expect(line.translateToString(false, 0, 4)).equal('a e\u0301 '); - expect(line.translateToString(false, 0, 3)).equal('a e\u0301'); - expect(line.translateToString(true, 0, 5)).equal('a e\u0301 e\u0301'); - expect(line.translateToString(true, 0, 4)).equal('a e\u0301 '); - expect(line.translateToString(true, 0, 3)).equal('a e\u0301'); + assert.equal(line.translateToString(false), 'a e\u0301 e\u0301e\u0301 '); + assert.equal(line.translateToString(true), 'a e\u0301 e\u0301e\u0301'); + assert.equal(line.translateToString(false, 0, 5), 'a e\u0301 e\u0301'); + assert.equal(line.translateToString(false, 0, 4), 'a e\u0301 '); + assert.equal(line.translateToString(false, 0, 3), 'a e\u0301'); + assert.equal(line.translateToString(true, 0, 5), 'a e\u0301 e\u0301'); + assert.equal(line.translateToString(true, 0, 4), 'a e\u0301 '); + assert.equal(line.translateToString(true, 0, 3), 'a e\u0301'); }); it('fullwidth', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); @@ -389,20 +389,20 @@ describe('BufferLine', function(): void { line.setCell(6, CellData.fromCharData([0, '', 0, 0])); line.setCell(7, CellData.fromCharData([1, '1', 2, '1'.charCodeAt(0)])); line.setCell(8, CellData.fromCharData([0, '', 0, 0])); - expect(line.translateToString(false)).equal('a 1 11 '); - expect(line.translateToString(true)).equal('a 1 11'); - expect(line.translateToString(false, 0, 7)).equal('a 1 1'); - expect(line.translateToString(false, 0, 6)).equal('a 1 1'); - expect(line.translateToString(false, 0, 5)).equal('a 1 '); - expect(line.translateToString(false, 0, 4)).equal('a 1'); - expect(line.translateToString(false, 0, 3)).equal('a 1'); - expect(line.translateToString(false, 0, 2)).equal('a '); - expect(line.translateToString(true, 0, 7)).equal('a 1 1'); - expect(line.translateToString(true, 0, 6)).equal('a 1 1'); - expect(line.translateToString(true, 0, 5)).equal('a 1 '); - expect(line.translateToString(true, 0, 4)).equal('a 1'); - expect(line.translateToString(true, 0, 3)).equal('a 1'); - expect(line.translateToString(true, 0, 2)).equal('a '); + assert.equal(line.translateToString(false), 'a 1 11 '); + assert.equal(line.translateToString(true), 'a 1 11'); + assert.equal(line.translateToString(false, 0, 7), 'a 1 1'); + assert.equal(line.translateToString(false, 0, 6), 'a 1 1'); + assert.equal(line.translateToString(false, 0, 5), 'a 1 '); + assert.equal(line.translateToString(false, 0, 4), 'a 1'); + assert.equal(line.translateToString(false, 0, 3), 'a 1'); + assert.equal(line.translateToString(false, 0, 2), 'a '); + assert.equal(line.translateToString(true, 0, 7), 'a 1 1'); + assert.equal(line.translateToString(true, 0, 6), 'a 1 1'); + assert.equal(line.translateToString(true, 0, 5), 'a 1 '); + assert.equal(line.translateToString(true, 0, 4), 'a 1'); + assert.equal(line.translateToString(true, 0, 3), 'a 1'); + assert.equal(line.translateToString(true, 0, 2), 'a '); }); it('space at end', function(): void { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]), false); @@ -411,21 +411,21 @@ describe('BufferLine', function(): void { line.setCell(4, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(5, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); line.setCell(6, CellData.fromCharData([1, ' ', 1, ' '.charCodeAt(0)])); - expect(line.translateToString(false)).equal('a a aa '); - expect(line.translateToString(true)).equal('a a aa '); + assert.equal(line.translateToString(false), 'a a aa '); + assert.equal(line.translateToString(true), 'a a aa '); }); it('should always return some sane value', function(): void { // sanity check - broken line with invalid out of bound null width cells // this can atm happen with deleting/inserting chars in inputhandler by "breaking" // fullwidth pairs --> needs to be fixed after settling BufferLine impl const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false); - expect(line.translateToString(false)).equal(' '); - expect(line.translateToString(true)).equal(''); + assert.equal(line.translateToString(false), ' '); + assert.equal(line.translateToString(true), ''); }); it('should work with endCol=0', () => { const line = new TestBufferLine(10, CellData.fromCharData([DEFAULT_ATTR, NULL_CELL_CHAR, 0, NULL_CELL_CODE]), false); line.setCell(0, CellData.fromCharData([1, 'a', 1, 'a'.charCodeAt(0)])); - expect(line.translateToString(true, 0, 0)).equal(''); + assert.equal(line.translateToString(true, 0, 0), ''); }); }); describe('addCharToCell', () => { diff --git a/src/common/input/TextDecoder.test.ts b/src/common/input/TextDecoder.test.ts index 92b0e03a..da1760a2 100644 --- a/src/common/input/TextDecoder.test.ts +++ b/src/common/input/TextDecoder.test.ts @@ -58,8 +58,8 @@ describe('text encodings', () => { const decoder = new StringToUtf32(); const target = new Uint32Array(5); for (let i = 0; i < 65536; ++i) { - // skip surrogate pairs - if (i >= 0xD800 && i <= 0xDFFF) { + // skip surrogate pairs and a BOM + if ((i >= 0xD800 && i <= 0xDFFF) || i === 0xFEFF) { continue; } const length = decoder.decode(String.fromCharCode(i), target); @@ -84,6 +84,14 @@ describe('text encodings', () => { decoder.clear(); } }); + + it('0xFEFF(BOM)', () => { + const decoder = new StringToUtf32(); + const target = new Uint32Array(5); + const length = decoder.decode(String.fromCharCode(0xFEFF), target); + assert.equal(length, 0); + decoder.clear(); + }); }); it('test strings', () => { @@ -118,8 +126,8 @@ describe('text encodings', () => { const decoder = new Utf8ToUtf32(); const target = new Uint32Array(5); for (let i = 0; i < 65536; ++i) { - // skip surrogate pairs - if (i >= 0xD800 && i <= 0xDFFF) { + // skip surrogate pairs and a BOM + if ((i >= 0xD800 && i <= 0xDFFF) || i === 0xFEFF) { continue; } const utf8Data = fromByteString(encode(String.fromCharCode(i))); @@ -142,6 +150,15 @@ describe('text encodings', () => { decoder.clear(); } }); + + it('0xFEFF(BOM)', () => { + const decoder = new Utf8ToUtf32(); + const target = new Uint32Array(5); + const utf8Data = fromByteString(encode(String.fromCharCode(0xFEFF))); + const length = decoder.decode(utf8Data, target); + assert.equal(length, 0); + decoder.clear(); + }); }); it('test strings', () => { @@ -215,6 +232,19 @@ describe('text encodings', () => { } assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€'); }); + + it('BOMs (3 byte sequences) - advance by 2', () => { + const decoder = new Utf8ToUtf32(); + const target = new Uint32Array(5); + const utf8Data = fromByteString('\xef\xbb\xbf\xef\xbb\xbf'); + let decoded = ''; + for (let i = 0; i < utf8Data.length; i += 2) { + const written = decoder.decode(utf8Data.slice(i, i + 2), target); + decoded += toString(target, written); + } + assert.equal(decoded, ''); + }); + it('test break after 3 bytes - issue #2495', () => { const decoder = new Utf8ToUtf32(); const target = new Uint32Array(5); diff --git a/src/common/input/TextDecoder.ts b/src/common/input/TextDecoder.ts index 6ecab011..715e9197 100644 --- a/src/common/input/TextDecoder.ts +++ b/src/common/input/TextDecoder.ts @@ -105,6 +105,10 @@ export class StringToUtf32 { } continue; } + if (code === 0xFEFF) { + // BOM + continue; + } target[size++] = code; } return size; @@ -188,8 +192,8 @@ export class Utf8ToUtf32 { target[size++] = cp; } } else if (type === 3) { - if (cp < 0x0800 || (cp >= 0xD800 && cp <= 0xDFFF)) { - // illegal codepoint + if (cp < 0x0800 || (cp >= 0xD800 && cp <= 0xDFFF) || cp === 0xFEFF) { + // illegal codepoint or BOM } else { target[size++] = cp; } @@ -286,8 +290,8 @@ export class Utf8ToUtf32 { continue; } codepoint = (byte1 & 0x0F) << 12 | (byte2 & 0x3F) << 6 | (byte3 & 0x3F); - if (codepoint < 0x0800 || (codepoint >= 0xD800 && codepoint <= 0xDFFF)) { - // illegal codepoint, no i-- here + if (codepoint < 0x0800 || (codepoint >= 0xD800 && codepoint <= 0xDFFF) || codepoint === 0xFEFF) { + // illegal codepoint or BOM, no i-- here continue; } target[size++] = codepoint; diff --git a/src/common/parser/EscapeSequenceParser.test.ts b/src/common/parser/EscapeSequenceParser.test.ts index 468dc934..f4ec5025 100644 --- a/src/common/parser/EscapeSequenceParser.test.ts +++ b/src/common/parser/EscapeSequenceParser.test.ts @@ -5,7 +5,7 @@ import { IParsingState, IParams, ParamsArray, IOscParser, IOscHandler, OscFallbackHandlerType, IFunctionIdentifier } from 'common/parser/Types'; import { EscapeSequenceParser, TransitionTable, VT500_TRANSITION_TABLE } from 'common/parser/EscapeSequenceParser'; -import * as chai from 'chai'; +import { assert } from 'chai'; import { StringToUtf32, stringFromCodePoint, utf32ToString } from 'common/input/TextDecoder'; import { ParserState } from 'common/parser/Constants'; import { Params } from 'common/parser/Params'; @@ -97,38 +97,38 @@ class TestEscapeSequenceParser extends EscapeSequenceParser { // test object to collect parser actions and compare them with expected values const testTerminal: any = { calls: [], - clear: function (): void { + clear(): void { this.calls = []; }, - compare: function (value: any): void { - chai.expect(this.calls.slice()).eql(value); // weird bug w'o slicing here + compare(value: any): void { + assert.deepEqual(this.calls, value); }, - print: function (data: Uint32Array, start: number, end: number): void { + print(data: Uint32Array, start: number, end: number): void { let s = ''; for (let i = start; i < end; ++i) { s += stringFromCodePoint(data[i]); } this.calls.push(['print', s]); }, - actionOSC: function (s: string): void { + actionOSC(s: string): void { this.calls.push(['osc', s]); }, - actionExecute: function (flag: string): void { + actionExecute(flag: string): void { this.calls.push(['exe', flag]); }, - actionCSI: function (collect: string, params: IParams, flag: string): void { + actionCSI(collect: string, params: IParams, flag: string): void { this.calls.push(['csi', collect, params.toArray(), flag]); }, - actionESC: function (collect: string, flag: string): void { + actionESC(collect: string, flag: string): void { this.calls.push(['esc', collect, flag]); }, - actionDCSHook: function (params: IParams): void { + actionDCSHook(params: IParams): void { this.calls.push(['dcs hook', params.toArray()]); }, - actionDCSPrint: function (s: string): void { + actionDCSPrint(s: string): void { this.calls.push(['dcs put', s]); }, - actionDCSUnhook: function (success: boolean): void { + actionDCSUnhook(success: boolean): void { this.calls.push(['dcs unhook', success]); } }; @@ -191,40 +191,40 @@ function parse(parser: TestEscapeSequenceParser, data: string): void { parser.parse(container, decoder.decode(data, container)); } -describe('EscapeSequenceParser', function (): void { +describe('EscapeSequenceParser', () => { const parser = testParser; - describe('Parser init and methods', function (): void { - it('constructor', function (): void { + describe('Parser init and methods', () => { + it('constructor', () => { let p = new TestEscapeSequenceParser(); - chai.expect(p.transitions).equal(VT500_TRANSITION_TABLE); + assert.deepEqual(p.transitions, VT500_TRANSITION_TABLE); p = new TestEscapeSequenceParser(VT500_TRANSITION_TABLE); - chai.expect(p.transitions).equal(VT500_TRANSITION_TABLE); + assert.deepEqual(p.transitions, VT500_TRANSITION_TABLE); const tansitions: TransitionTable = new TransitionTable(10); p = new TestEscapeSequenceParser(tansitions); - chai.expect(p.transitions).equal(tansitions); + assert.deepEqual(p.transitions, tansitions); }); - it('inital states', function (): void { - chai.expect(parser.initialState).equal(ParserState.GROUND); - chai.expect(parser.currentState).equal(ParserState.GROUND); - chai.expect(parser.osc).equal(''); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); + it('inital states', () => { + assert.equal(parser.initialState, ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); + assert.equal(parser.osc, ''); + assert.deepEqual(parser.params, [0]); + assert.equal(parser.collect, ''); }); - it('reset states', function (): void { + it('reset states', () => { parser.currentState = 124; parser.osc = '#'; parser.params = [123]; parser.collect = '#'; parser.reset(); - chai.expect(parser.currentState).equal(ParserState.GROUND); - chai.expect(parser.osc).equal(''); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); + assert.equal(parser.currentState, ParserState.GROUND); + assert.equal(parser.osc, ''); + assert.deepEqual(parser.params, [0]); + assert.equal(parser.collect, ''); }); }); - describe('state transitions and actions', function (): void { - it('state GROUND execute action', function (): void { + describe('state transitions and actions', () => { + it('state GROUND execute action', () => { parser.reset(); testTerminal.clear(); let exes = r(0x00, 0x18); @@ -233,26 +233,26 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.GROUND; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); - it('state GROUND print action', function (): void { + it('state GROUND print action', () => { parser.reset(); testTerminal.clear(); const printables = r(0x20, 0x7f); // NOTE: DEL excluded for (let i = 0; i < printables.length; ++i) { parser.currentState = ParserState.GROUND; parse(parser, printables[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([['print', printables[i]]]); parser.reset(); testTerminal.clear(); } }); - it('trans ANYWHERE --> GROUND with actions', function (): void { + it('trans ANYWHERE --> GROUND with actions', () => { const exes = [ '\x18', '\x1a', '\x80', '\x81', '\x82', '\x83', '\x84', '\x85', '\x86', '\x87', '\x88', @@ -269,32 +269,32 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < exes.length; ++i) { parser.currentState = state; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare((state in exceptions ? exceptions[state][exes[i]] : 0) || [['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } parse(parser, '\x9c'); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([]); parser.reset(); testTerminal.clear(); } }); - it('trans ANYWHERE --> ESCAPE with clear', function (): void { + it('trans ANYWHERE --> ESCAPE with clear', () => { parser.reset(); for (state in states) { parser.currentState = state; parser.params = [23]; parser.collect = '#'; parse(parser, '\x1b'); - chai.expect(parser.currentState).equal(ParserState.ESCAPE); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); + assert.equal(parser.currentState, ParserState.ESCAPE); + assert.deepEqual(parser.params, [0]); + assert.equal(parser.collect, ''); parser.reset(); } }); - it('state ESCAPE execute rules', function (): void { + it('state ESCAPE execute rules', () => { parser.reset(); testTerminal.clear(); let exes = r(0x00, 0x18); @@ -303,23 +303,23 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.ESCAPE; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.ESCAPE); + assert.equal(parser.currentState, ParserState.ESCAPE); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); - it('state ESCAPE ignore', function (): void { + it('state ESCAPE ignore', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.ESCAPE; parse(parser, '\x7f'); - chai.expect(parser.currentState).equal(ParserState.ESCAPE); + assert.equal(parser.currentState, ParserState.ESCAPE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); - it('trans ESCAPE --> GROUND with ecs_dispatch action', function (): void { + it('trans ESCAPE --> GROUND with ecs_dispatch action', () => { parser.reset(); testTerminal.clear(); let dispatches = r(0x30, 0x50); @@ -329,24 +329,24 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.ESCAPE; parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([['esc', '', dispatches[i]]]); parser.reset(); testTerminal.clear(); } }); - it('trans ESCAPE --> ESCAPE_INTERMEDIATE with collect action', function (): void { + it('trans ESCAPE --> ESCAPE_INTERMEDIATE with collect action', () => { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.ESCAPE; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.ESCAPE_INTERMEDIATE); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('state ESCAPE_INTERMEDIATE execute rules', function (): void { + it('state ESCAPE_INTERMEDIATE execute rules', () => { parser.reset(); testTerminal.clear(); let exes = r(0x00, 0x18); @@ -355,57 +355,57 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); + assert.equal(parser.currentState, ParserState.ESCAPE_INTERMEDIATE); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); - it('state ESCAPE_INTERMEDIATE ignore', function (): void { + it('state ESCAPE_INTERMEDIATE ignore', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parse(parser, '\x7f'); - chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); + assert.equal(parser.currentState, ParserState.ESCAPE_INTERMEDIATE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); - it('state ESCAPE_INTERMEDIATE collect action', function (): void { + it('state ESCAPE_INTERMEDIATE collect action', () => { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.ESCAPE_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.ESCAPE_INTERMEDIATE); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('trans ESCAPE_INTERMEDIATE --> GROUND with esc_dispatch action', function (): void { + it('trans ESCAPE_INTERMEDIATE --> GROUND with esc_dispatch action', () => { parser.reset(); testTerminal.clear(); const collect = r(0x30, 0x7f); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.ESCAPE_INTERMEDIATE; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); // '\x5c' --> ESC + \ (7bit ST) parser does not expose this as it already got handled testTerminal.compare((collect[i] === '\x5c') ? [] : [['esc', '', collect[i]]]); parser.reset(); testTerminal.clear(); } }); - it('trans ANYWHERE/ESCAPE --> CSI_ENTRY with clear', function (): void { + it('trans ANYWHERE/ESCAPE --> CSI_ENTRY with clear', () => { parser.reset(); // C0 parser.currentState = ParserState.ESCAPE; parser.params = [123]; parser.collect = '#'; parse(parser, '['); - chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); + assert.equal(parser.currentState, ParserState.CSI_ENTRY); + assert.deepEqual(parser.params, [0]); + assert.equal(parser.collect, ''); parser.reset(); // C1 for (state in states) { @@ -413,13 +413,13 @@ describe('EscapeSequenceParser', function (): void { parser.params = [123]; parser.collect = '#'; parse(parser, '\x9b'); - chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); - chai.expect(parser.params).eql([0]); - chai.expect(parser.collect).equal(''); + assert.equal(parser.currentState, ParserState.CSI_ENTRY); + assert.deepEqual(parser.params, [0]); + assert.equal(parser.collect, ''); parser.reset(); } }); - it('state CSI_ENTRY execute rules', function (): void { + it('state CSI_ENTRY execute rules', () => { parser.reset(); testTerminal.clear(); let exes = r(0x00, 0x18); @@ -428,59 +428,59 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); + assert.equal(parser.currentState, ParserState.CSI_ENTRY); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); - it('state CSI_ENTRY ignore', function (): void { + it('state CSI_ENTRY ignore', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.CSI_ENTRY; parse(parser, '\x7f'); - chai.expect(parser.currentState).equal(ParserState.CSI_ENTRY); + assert.equal(parser.currentState, ParserState.CSI_ENTRY); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); - it('trans CSI_ENTRY --> GROUND with csi_dispatch action', function (): void { + it('trans CSI_ENTRY --> GROUND with csi_dispatch action', () => { parser.reset(); const dispatches = r(0x40, 0x7f); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([['csi', '', [0], dispatches[i]]]); parser.reset(); testTerminal.clear(); } }); - it('trans CSI_ENTRY --> CSI_PARAM with param/collect actions', function (): void { + it('trans CSI_ENTRY --> CSI_PARAM with param/collect actions', () => { parser.reset(); const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; const collect = ['\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parse(parser, params[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); + assert.equal(parser.currentState, ParserState.CSI_PARAM); + assert.deepEqual(parser.params, [params[i].charCodeAt(0) - 48]); parser.reset(); } parser.currentState = ParserState.CSI_ENTRY; parse(parser, '\x3b'); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.params).eql([0, 0]); + assert.equal(parser.currentState, ParserState.CSI_PARAM); + assert.deepEqual(parser.params, [0, 0]); parser.reset(); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.CSI_PARAM); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('state CSI_PARAM execute rules', function (): void { + it('state CSI_PARAM execute rules', () => { parser.reset(); testTerminal.clear(); let exes = r(0x00, 0x18); @@ -489,74 +489,74 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); + assert.equal(parser.currentState, ParserState.CSI_PARAM); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); - it('state CSI_PARAM param action', function (): void { + it('state CSI_PARAM param action', () => { parser.reset(); const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parse(parser, params[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); + assert.equal(parser.currentState, ParserState.CSI_PARAM); + assert.deepEqual(parser.params, [params[i].charCodeAt(0) - 48]); parser.reset(); } parser.currentState = ParserState.CSI_PARAM; parse(parser, '\x3b'); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); - chai.expect(parser.params).eql([0, 0]); + assert.equal(parser.currentState, ParserState.CSI_PARAM); + assert.deepEqual(parser.params, [0, 0]); parser.reset(); }); - it('state CSI_PARAM ignore', function (): void { + it('state CSI_PARAM ignore', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.CSI_PARAM; parse(parser, '\x7f'); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); + assert.equal(parser.currentState, ParserState.CSI_PARAM); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); - it('trans CSI_PARAM --> GROUND with csi_dispatch action', function (): void { + it('trans CSI_PARAM --> GROUND with csi_dispatch action', () => { parser.reset(); const dispatches = r(0x40, 0x7f); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parser.params = [0, 1]; parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]); parser.reset(); testTerminal.clear(); } }); - it('trans CSI_ENTRY --> CSI_INTERMEDIATE with collect action', function (): void { + it('trans CSI_ENTRY --> CSI_INTERMEDIATE with collect action', () => { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.CSI_ENTRY; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('trans CSI_PARAM --> CSI_INTERMEDIATE with collect action', function (): void { + it('trans CSI_PARAM --> CSI_INTERMEDIATE with collect action', () => { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('state CSI_INTERMEDIATE execute rules', function (): void { + it('state CSI_INTERMEDIATE execute rules', () => { parser.reset(); testTerminal.clear(); let exes = r(0x00, 0x18); @@ -565,88 +565,88 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_INTERMEDIATE; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); + assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); - it('state CSI_INTERMEDIATE collect', function (): void { + it('state CSI_INTERMEDIATE collect', () => { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.CSI_INTERMEDIATE; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('state CSI_INTERMEDIATE ignore', function (): void { + it('state CSI_INTERMEDIATE ignore', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.CSI_INTERMEDIATE; parse(parser, '\x7f'); - chai.expect(parser.currentState).equal(ParserState.CSI_INTERMEDIATE); + assert.equal(parser.currentState, ParserState.CSI_INTERMEDIATE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); - it('trans CSI_INTERMEDIATE --> GROUND with csi_dispatch action', function (): void { + it('trans CSI_INTERMEDIATE --> GROUND with csi_dispatch action', () => { parser.reset(); const dispatches = r(0x40, 0x7f); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_INTERMEDIATE; parser.params = [0, 1]; parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([['csi', '', [0, 1], dispatches[i]]]); parser.reset(); testTerminal.clear(); } }); - it('trans CSI_ENTRY --> CSI_PARAM for ":" (0x3a)', function (): void { + it('trans CSI_ENTRY --> CSI_PARAM for ":" (0x3a)', () => { parser.reset(); parser.currentState = ParserState.CSI_ENTRY; parse(parser, '\x3a'); - chai.expect(parser.currentState).equal(ParserState.CSI_PARAM); + assert.equal(parser.currentState, ParserState.CSI_PARAM); parser.reset(); }); - it('trans CSI_PARAM --> CSI_IGNORE', function (): void { + it('trans CSI_PARAM --> CSI_IGNORE', () => { parser.reset(); const chars = ['\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.CSI_PARAM; parse(parser, '\x3b' + chars[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); - chai.expect(parser.params).eql([0, 0]); + assert.equal(parser.currentState, ParserState.CSI_IGNORE); + assert.deepEqual(parser.params, [0, 0]); parser.reset(); } }); - it('trans CSI_PARAM --> CSI_IGNORE', function (): void { + it('trans CSI_PARAM --> CSI_IGNORE', () => { parser.reset(); const chars = ['\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < chars.length; ++i) { - chai.expect(parser.params).eql([0]); + assert.deepEqual(parser.params, [0]); parser.currentState = ParserState.CSI_PARAM; parse(parser, '\x3b' + chars[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); - chai.expect(parser.params).eql([0, 0]); + assert.equal(parser.currentState, ParserState.CSI_IGNORE); + assert.deepEqual(parser.params, [0, 0]); parser.reset(); } }); - it('trans CSI_INTERMEDIATE --> CSI_IGNORE', function (): void { + it('trans CSI_INTERMEDIATE --> CSI_IGNORE', () => { parser.reset(); const chars = r(0x30, 0x40); for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.CSI_INTERMEDIATE; parse(parser, chars[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); - chai.expect(parser.params).eql([0]); + assert.equal(parser.currentState, ParserState.CSI_IGNORE); + assert.deepEqual(parser.params, [0]); parser.reset(); } }); - it('state CSI_IGNORE execute rules', function (): void { + it('state CSI_IGNORE execute rules', () => { parser.reset(); testTerminal.clear(); let exes = r(0x00, 0x18); @@ -655,13 +655,13 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < exes.length; ++i) { parser.currentState = ParserState.CSI_IGNORE; parse(parser, exes[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); + assert.equal(parser.currentState, ParserState.CSI_IGNORE); testTerminal.compare([['exe', exes[i]]]); parser.reset(); testTerminal.clear(); } }); - it('state CSI_IGNORE ignore', function (): void { + it('state CSI_IGNORE ignore', () => { parser.reset(); testTerminal.clear(); let ignored = r(0x20, 0x40); @@ -669,32 +669,32 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.CSI_IGNORE; parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); + assert.equal(parser.currentState, ParserState.CSI_IGNORE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); } }); - it('trans CSI_IGNORE --> GROUND', function (): void { + it('trans CSI_IGNORE --> GROUND', () => { parser.reset(); const dispatches = r(0x40, 0x7f); for (let i = 0; i < dispatches.length; ++i) { parser.currentState = ParserState.CSI_IGNORE; parser.params = [0, 1]; parse(parser, dispatches[i]); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([]); parser.reset(); testTerminal.clear(); } }); - it('trans ANYWHERE/ESCAPE --> SOS_PM_APC_STRING', function (): void { + it('trans ANYWHERE/ESCAPE --> SOS_PM_APC_STRING', () => { parser.reset(); // C0 let initializers = ['\x58', '\x5e', '\x5f']; for (let i = 0; i < initializers.length; ++i) { parse(parser, '\x1b' + initializers[i]); - chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); + assert.equal(parser.currentState, ParserState.SOS_PM_APC_STRING); parser.reset(); } // C1 @@ -703,12 +703,12 @@ describe('EscapeSequenceParser', function (): void { initializers = ['\x98', '\x9e', '\x9f']; for (let i = 0; i < initializers.length; ++i) { parse(parser, initializers[i]); - chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); + assert.equal(parser.currentState, ParserState.SOS_PM_APC_STRING); parser.reset(); } } }); - it('state SOS_PM_APC_STRING ignore rules', function (): void { + it('state SOS_PM_APC_STRING ignore rules', () => { parser.reset(); let ignored = r(0x00, 0x18); ignored = ignored.concat(['\x19']); @@ -717,25 +717,25 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.SOS_PM_APC_STRING; parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.SOS_PM_APC_STRING); + assert.equal(parser.currentState, ParserState.SOS_PM_APC_STRING); parser.reset(); } }); - it('trans ANYWHERE/ESCAPE --> OSC_STRING', function (): void { + it('trans ANYWHERE/ESCAPE --> OSC_STRING', () => { parser.reset(); // C0 parse(parser, '\x1b]'); - chai.expect(parser.currentState).equal(ParserState.OSC_STRING); + assert.equal(parser.currentState, ParserState.OSC_STRING); parser.reset(); // C1 for (state in states) { parser.currentState = state; parse(parser, '\x9d'); - chai.expect(parser.currentState).equal(ParserState.OSC_STRING); + assert.equal(parser.currentState, ParserState.OSC_STRING); parser.reset(); } }); - it('state OSC_STRING ignore rules', function (): void { + it('state OSC_STRING ignore rules', () => { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', /* '\x07', */ '\x08', @@ -744,37 +744,37 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.OSC_STRING; parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.OSC_STRING); - chai.expect(parser.osc).equal(''); + assert.equal(parser.currentState, ParserState.OSC_STRING); + assert.equal(parser.osc, ''); parser.reset(); } }); - it('state OSC_STRING put action', function (): void { + it('state OSC_STRING put action', () => { parser.reset(); const puts = r(0x20, 0x80); for (let i = 0; i < puts.length; ++i) { parser.currentState = ParserState.OSC_STRING; parse(parser, puts[i]); - chai.expect(parser.currentState).equal(ParserState.OSC_STRING); - chai.expect(parser.osc).equal(puts[i]); + assert.equal(parser.currentState, ParserState.OSC_STRING); + assert.equal(parser.osc, puts[i]); parser.reset(); } }); - it('state DCS_ENTRY', function (): void { + it('state DCS_ENTRY', () => { parser.reset(); // C0 parse(parser, '\x1bP'); - chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); + assert.equal(parser.currentState, ParserState.DCS_ENTRY); parser.reset(); // C1 for (state in states) { parser.currentState = state; parse(parser, '\x90'); - chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); + assert.equal(parser.currentState, ParserState.DCS_ENTRY); parser.reset(); } }); - it('state DCS_ENTRY ignore rules', function (): void { + it('state DCS_ENTRY ignore rules', () => { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', @@ -783,35 +783,35 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_ENTRY); + assert.equal(parser.currentState, ParserState.DCS_ENTRY); parser.reset(); } }); - it('state DCS_ENTRY --> DCS_PARAM with param/collect actions', function (): void { + it('state DCS_ENTRY --> DCS_PARAM with param/collect actions', () => { parser.reset(); const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; const collect = ['\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parse(parser, params[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); + assert.equal(parser.currentState, ParserState.DCS_PARAM); + assert.deepEqual(parser.params, [params[i].charCodeAt(0) - 48]); parser.reset(); } parser.currentState = ParserState.DCS_ENTRY; parse(parser, '\x3b'); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.params).eql([0, 0]); + assert.equal(parser.currentState, ParserState.DCS_PARAM); + assert.deepEqual(parser.params, [0, 0]); parser.reset(); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.DCS_PARAM); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('state DCS_PARAM ignore rules', function (): void { + it('state DCS_PARAM ignore rules', () => { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', @@ -820,55 +820,55 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); + assert.equal(parser.currentState, ParserState.DCS_PARAM); parser.reset(); } }); - it('state DCS_PARAM param action', function (): void { + it('state DCS_PARAM param action', () => { parser.reset(); const params = ['\x30', '\x31', '\x32', '\x33', '\x34', '\x35', '\x36', '\x37', '\x38', '\x39']; for (let i = 0; i < params.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parse(parser, params[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.params).eql([params[i].charCodeAt(0) - 48]); + assert.equal(parser.currentState, ParserState.DCS_PARAM); + assert.deepEqual(parser.params, [params[i].charCodeAt(0) - 48]); parser.reset(); } parser.currentState = ParserState.DCS_PARAM; parse(parser, '\x3b'); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); - chai.expect(parser.params).eql([0, 0]); + assert.equal(parser.currentState, ParserState.DCS_PARAM); + assert.deepEqual(parser.params, [0, 0]); parser.reset(); }); - it('trans DCS_ENTRY --> DCS_PARAM for ":" (0x3a)', function (): void { + it('trans DCS_ENTRY --> DCS_PARAM for ":" (0x3a)', () => { parser.reset(); parser.currentState = ParserState.DCS_ENTRY; parse(parser, '\x3a'); - chai.expect(parser.currentState).equal(ParserState.DCS_PARAM); + assert.equal(parser.currentState, ParserState.DCS_PARAM); parser.reset(); }); - it('trans DCS_PARAM --> DCS_IGNORE', function (): void { + it('trans DCS_PARAM --> DCS_IGNORE', () => { parser.reset(); const chars = ['\x3c', '\x3d', '\x3e', '\x3f']; for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parse(parser, '\x3b' + chars[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); - chai.expect(parser.params).eql([0, 0]); + assert.equal(parser.currentState, ParserState.DCS_IGNORE); + assert.deepEqual(parser.params, [0, 0]); parser.reset(); } }); - it('trans DCS_INTERMEDIATE --> DCS_IGNORE', function (): void { + it('trans DCS_INTERMEDIATE --> DCS_IGNORE', () => { parser.reset(); const chars = r(0x30, 0x40); for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parse(parser, chars[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); + assert.equal(parser.currentState, ParserState.DCS_IGNORE); parser.reset(); } }); - it('state DCS_IGNORE ignore rules', function (): void { + it('state DCS_IGNORE ignore rules', () => { parser.reset(); let ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', @@ -878,33 +878,33 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_IGNORE; parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); + assert.equal(parser.currentState, ParserState.DCS_IGNORE); parser.reset(); } }); - it('trans DCS_ENTRY --> DCS_INTERMEDIATE with collect action', function (): void { + it('trans DCS_ENTRY --> DCS_INTERMEDIATE with collect action', () => { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('trans DCS_PARAM --> DCS_INTERMEDIATE with collect action', function (): void { + it('trans DCS_PARAM --> DCS_INTERMEDIATE with collect action', () => { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('state DCS_INTERMEDIATE ignore rules', function (): void { + it('state DCS_INTERMEDIATE ignore rules', () => { parser.reset(); const ignored = [ '\x00', '\x01', '\x02', '\x03', '\x04', '\x05', '\x06', '\x07', '\x08', @@ -913,72 +913,72 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < ignored.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parse(parser, ignored[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); + assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE); parser.reset(); } }); - it('state DCS_INTERMEDIATE collect action', function (): void { + it('state DCS_INTERMEDIATE collect action', () => { parser.reset(); const collect = r(0x20, 0x30); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_INTERMEDIATE); - chai.expect(parser.collect).equal(collect[i]); + assert.equal(parser.currentState, ParserState.DCS_INTERMEDIATE); + assert.equal(parser.collect, collect[i]); parser.reset(); } }); - it('trans DCS_INTERMEDIATE --> DCS_IGNORE', function (): void { + it('trans DCS_INTERMEDIATE --> DCS_IGNORE', () => { parser.reset(); const chars = r(0x30, 0x40); for (let i = 0; i < chars.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parse(parser, '\x20' + chars[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); - chai.expect(parser.collect).equal('\x20'); + assert.equal(parser.currentState, ParserState.DCS_IGNORE); + assert.equal(parser.collect, '\x20'); parser.reset(); } }); - it('trans DCS_ENTRY --> DCS_PASSTHROUGH with hook', function (): void { + it('trans DCS_ENTRY --> DCS_PASSTHROUGH with hook', () => { parser.reset(); testTerminal.clear(); const collect = r(0x40, 0x7f); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_ENTRY; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + assert.equal(parser.currentState, ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs hook', [0]]]); parser.reset(); testTerminal.clear(); } }); - it('trans DCS_PARAM --> DCS_PASSTHROUGH with hook', function (): void { + it('trans DCS_PARAM --> DCS_PASSTHROUGH with hook', () => { parser.reset(); testTerminal.clear(); const collect = r(0x40, 0x7f); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_PARAM; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + assert.equal(parser.currentState, ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs hook', [0]]]); parser.reset(); testTerminal.clear(); } }); - it('trans DCS_INTERMEDIATE --> DCS_PASSTHROUGH with hook', function (): void { + it('trans DCS_INTERMEDIATE --> DCS_PASSTHROUGH with hook', () => { parser.reset(); testTerminal.clear(); const collect = r(0x40, 0x7f); for (let i = 0; i < collect.length; ++i) { parser.currentState = ParserState.DCS_INTERMEDIATE; parse(parser, collect[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + assert.equal(parser.currentState, ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs hook', [0]]]); parser.reset(); testTerminal.clear(); } }); - it('state DCS_PASSTHROUGH put action', function (): void { + it('state DCS_PASSTHROUGH put action', () => { parser.reset(); testTerminal.clear(); let puts = r(0x00, 0x18); @@ -988,18 +988,18 @@ describe('EscapeSequenceParser', function (): void { for (let i = 0; i < puts.length; ++i) { parser.currentState = ParserState.DCS_PASSTHROUGH; parse(parser, puts[i]); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + assert.equal(parser.currentState, ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs put', puts[i]]]); parser.reset(); testTerminal.clear(); } }); - it('state DCS_PASSTHROUGH ignore', function (): void { + it('state DCS_PASSTHROUGH ignore', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.DCS_PASSTHROUGH; parse(parser, '\x7f'); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + assert.equal(parser.currentState, ParserState.DCS_PASSTHROUGH); testTerminal.compare([]); parser.reset(); testTerminal.clear(); @@ -1015,8 +1015,8 @@ describe('EscapeSequenceParser', function (): void { testTerminal.compare(value); } - describe('escape sequence examples', function (): void { - it('CSI with print and execute', function (): void { + describe('escape sequence examples', () => { + it('CSI with print and execute', () => { test('\x1b[<31;5mHello World! öäü€\nabc', [ ['csi', '<', [31, 5], 'm'], @@ -1025,19 +1025,19 @@ describe('EscapeSequenceParser', function (): void { ['print', 'abc'] ], null); }); - it('OSC', function (): void { + it('OSC', () => { test('\x1b]0;abc123€öäü\x07', [ ['osc', '0;abc123€öäü, success: true'] ], null); }); - it('single DCS', function (): void { + it('single DCS', () => { test('\x1bP1;2;3+$aäbc;däe\x9c', [ ['dcs hook', [1, 2, 3]], ['dcs put', 'äbc;däe'], ['dcs unhook', true] ], null); }); - it('multi DCS', function (): void { + it('multi DCS', () => { test('\x1bP1;2;3+$abc;de', [ ['dcs hook', [1, 2, 3]], ['dcs put', 'bc;de'] @@ -1048,7 +1048,7 @@ describe('EscapeSequenceParser', function (): void { ['dcs unhook', true] ], true); }); - it('print + DCS(C1)', function (): void { + it('print + DCS(C1)', () => { test('abc\x901;2;3+$abc;de\x9c', [ ['print', 'abc'], ['dcs hook', [1, 2, 3]], @@ -1056,26 +1056,26 @@ describe('EscapeSequenceParser', function (): void { ['dcs unhook', true] ], null); }); - it('print + PM(C1) + print', function (): void { + it('print + PM(C1) + print', () => { test('abc\x98123tzf\x9cdefg', [ ['print', 'abc'], ['print', 'defg'] ], null); }); - it('print + OSC(C1) + print', function (): void { + it('print + OSC(C1) + print', () => { test('abc\x9d123;tzf\x9cdefg', [ ['print', 'abc'], ['osc', '123;tzf, success: true'], ['print', 'defg'] ], null); }); - it('error recovery', function (): void { + it('error recovery', () => { test('\x1b[1€abcdefg\x9b<;c', [ ['print', 'abcdefg'], ['csi', '<', [0, 0], 'c'] ], null); }); - it('7bit ST should be swallowed', function (): void { + it('7bit ST should be swallowed', () => { test('abc\x9d123;tzf\x1b\\defg', [ ['print', 'abc'], ['osc', '123;tzf, success: true'], @@ -1091,7 +1091,7 @@ describe('EscapeSequenceParser', function (): void { ['print', 'abc'] ], null); }); - it('colon notation in DCS params', function (): void { + it('colon notation in DCS params', () => { test('abc\x901;2::55;3+$abc;de\x9c', [ ['print', 'abc'], ['dcs hook', [1, 2, [-1, 55], 3]], @@ -1127,50 +1127,50 @@ describe('EscapeSequenceParser', function (): void { }); }); - describe('coverage tests', function (): void { - it('CSI_IGNORE error', function (): void { + describe('coverage tests', () => { + it('CSI_IGNORE error', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.CSI_IGNORE; parse(parser, '€öäü'); - chai.expect(parser.currentState).equal(ParserState.CSI_IGNORE); + assert.equal(parser.currentState, ParserState.CSI_IGNORE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); - it('DCS_IGNORE error', function (): void { + it('DCS_IGNORE error', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.DCS_IGNORE; parse(parser, '€öäü'); - chai.expect(parser.currentState).equal(ParserState.DCS_IGNORE); + assert.equal(parser.currentState, ParserState.DCS_IGNORE); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); - it('DCS_PASSTHROUGH error', function (): void { + it('DCS_PASSTHROUGH error', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.DCS_PASSTHROUGH; parse(parser, '\x901;2;3+$a€öäü'); - chai.expect(parser.currentState).equal(ParserState.DCS_PASSTHROUGH); + assert.equal(parser.currentState, ParserState.DCS_PASSTHROUGH); testTerminal.compare([['dcs hook', [1, 2, 3]], ['dcs put', '€öäü']]); parser.reset(); testTerminal.clear(); }); - it('error else of if (code > 159)', function (): void { + it('error else of if (code > 159)', () => { parser.reset(); testTerminal.clear(); parser.currentState = ParserState.GROUND; parse(parser, '\x9c'); - chai.expect(parser.currentState).equal(ParserState.GROUND); + assert.equal(parser.currentState, ParserState.GROUND); testTerminal.compare([]); parser.reset(); testTerminal.clear(); }); }); - describe('set/clear handler', function (): void { + describe('set/clear handler', () => { const INPUT = '\x1b[1;31mhello \x1b%Gwor\x1bEld!\x1b[0m\r\n$>\x1b]1;foo=bar\x1b\\'; let parser2: TestEscapeSequenceParser; let print = ''; @@ -1187,25 +1187,25 @@ describe('EscapeSequenceParser', function (): void { osc.length = 0; dcs.length = 0; } - beforeEach(function (): void { + beforeEach(() => { parser2 = new TestEscapeSequenceParser(); clearAccu(); }); - it('print handler', function (): void { + it('print handler', () => { parser2.setPrintHandler(function (data: Uint32Array, start: number, end: number): void { for (let i = start; i < end; ++i) { print += stringFromCodePoint(data[i]); } }); parse(parser2, INPUT); - chai.expect(print).equal('hello world!$>'); + assert.equal(print, 'hello world!$>'); parser2.clearPrintHandler(); parser2.clearPrintHandler(); // should not throw clearAccu(); parse(parser2, INPUT); - chai.expect(print).equal(''); + assert.equal(print, ''); }); - it('ESC handler', function (): void { + it('ESC handler', () => { parser2.registerEscHandler({intermediates: '%', final: 'G'}, function (): boolean { esc.push('%G'); return true; @@ -1215,43 +1215,43 @@ describe('EscapeSequenceParser', function (): void { return true; }); parse(parser2, INPUT); - chai.expect(esc).eql(['%G', 'E']); + assert.deepEqual(esc, ['%G', 'E']); parser2.clearEscHandler({intermediates: '%', final: 'G'}); parser2.clearEscHandler({intermediates: '%', final: 'G'}); // should not throw clearAccu(); parse(parser2, INPUT); - chai.expect(esc).eql(['E']); + assert.deepEqual(esc, ['E']); parser2.clearEscHandler({final: 'E'}); clearAccu(); parse(parser2, INPUT); - chai.expect(esc).eql([]); + assert.deepEqual(esc, []); }); describe('ESC custom handlers', () => { it('prevent fallback', () => { parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; }); parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; }); parse(parser2, INPUT); - chai.expect(esc).eql(['custom - %G']); + assert.deepEqual(esc, ['custom - %G']); }); it('allow fallback', () => { parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; }); parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return false; }); parse(parser2, INPUT); - chai.expect(esc).eql(['custom - %G', 'default - %G']); + assert.deepEqual(esc, ['custom - %G', 'default - %G']); }); it('Multiple custom handlers fallback once', () => { parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; }); parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; }); parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom2 - %G'); return false; }); parse(parser2, INPUT); - chai.expect(esc).eql(['custom2 - %G', 'custom - %G']); + assert.deepEqual(esc, ['custom2 - %G', 'custom - %G']); }); it('Multiple custom handlers no fallback', () => { parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; }); parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; }); parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom2 - %G'); return true; }); parse(parser2, INPUT); - chai.expect(esc).eql(['custom2 - %G']); + assert.deepEqual(esc, ['custom2 - %G']); }); it('Execution order should go from latest handler down to the original', () => { const order: number[] = []; @@ -1259,14 +1259,14 @@ describe('EscapeSequenceParser', function (): void { parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { order.push(2); return false; }); parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { order.push(3); return false; }); parse(parser2, '\x1b%G'); - chai.expect(order).eql([3, 2, 1]); + assert.deepEqual(order, [3, 2, 1]); }); it('Dispose should work', () => { parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; }); const dispo = parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('custom - %G'); return true; }); dispo.dispose(); parse(parser2, INPUT); - chai.expect(esc).eql(['default - %G']); + assert.deepEqual(esc, ['default - %G']); }); it('Should not corrupt the parser when dispose is called twice', () => { parser2.registerEscHandler({intermediates: '%', final: 'G'}, () => { esc.push('default - %G'); return true; }); @@ -1274,21 +1274,21 @@ describe('EscapeSequenceParser', function (): void { dispo.dispose(); dispo.dispose(); parse(parser2, INPUT); - chai.expect(esc).eql(['default - %G']); + assert.deepEqual(esc, ['default - %G']); }); }); - it('CSI handler', function (): void { + it('CSI handler', () => { parser2.registerCsiHandler({final: 'm'}, function (params: IParams): boolean { csi.push(['m', params.toArray(), '']); return true; }); parse(parser2, INPUT); - chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]); + assert.deepEqual(csi, [['m', [1, 31], ''], ['m', [0], '']]); parser2.clearCsiHandler({final: 'm'}); parser2.clearCsiHandler({final: 'm'}); // should not throw clearAccu(); parse(parser2, INPUT); - chai.expect(csi).eql([]); + assert.deepEqual(csi, []); }); describe('CSI custom handlers', () => { it('Prevent fallback', () => { @@ -1296,16 +1296,16 @@ describe('EscapeSequenceParser', function (): void { parser2.registerCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); return true; }); parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; }); parse(parser2, INPUT); - chai.expect(csi).eql([], 'Should not fallback to original handler'); - chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]); + assert.deepEqual(csi, [], 'Should not fallback to original handler'); + assert.deepEqual(csiCustom, [['m', [1, 31], ''], ['m', [0], '']]); }); it('Allow fallback', () => { const csiCustom: [string, ParamsArray, string][] = []; parser2.registerCsiHandler({final: 'm'}, params => { csi.push(['m', params.toArray(), '']); return true; }); parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return false; }); parse(parser2, INPUT); - chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler'); - chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]); + assert.deepEqual(csi, [['m', [1, 31], ''], ['m', [0], '']], 'Should fallback to original handler'); + assert.deepEqual(csiCustom, [['m', [1, 31], ''], ['m', [0], '']]); }); it('Multiple custom handlers fallback once', () => { const csiCustom: [string, ParamsArray, string][] = []; @@ -1314,9 +1314,9 @@ describe('EscapeSequenceParser', function (): void { parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; }); parser2.registerCsiHandler({final: 'm'}, params => { csiCustom2.push(['m', params.toArray(), '']); return false; }); parse(parser2, INPUT); - chai.expect(csi).eql([], 'Should not fallback to original handler'); - chai.expect(csiCustom).eql([['m', [1, 31], ''], ['m', [0], '']]); - chai.expect(csiCustom2).eql([['m', [1, 31], ''], ['m', [0], '']]); + assert.deepEqual(csi, [], 'Should not fallback to original handler'); + assert.deepEqual(csiCustom, [['m', [1, 31], ''], ['m', [0], '']]); + assert.deepEqual(csiCustom2, [['m', [1, 31], ''], ['m', [0], '']]); }); it('Multiple custom handlers no fallback', () => { const csiCustom: [string, ParamsArray, string][] = []; @@ -1325,9 +1325,9 @@ describe('EscapeSequenceParser', function (): void { parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; }); parser2.registerCsiHandler({final: 'm'}, params => { csiCustom2.push(['m', params.toArray(), '']); return true; }); parse(parser2, INPUT); - chai.expect(csi).eql([], 'Should not fallback to original handler'); - chai.expect(csiCustom).eql([], 'Should not fallback once'); - chai.expect(csiCustom2).eql([['m', [1, 31], ''], ['m', [0], '']]); + assert.deepEqual(csi, [], 'Should not fallback to original handler'); + assert.deepEqual(csiCustom, [], 'Should not fallback once'); + assert.deepEqual(csiCustom2, [['m', [1, 31], ''], ['m', [0], '']]); }); it('Execution order should go from latest handler down to the original', () => { const order: number[] = []; @@ -1335,7 +1335,7 @@ describe('EscapeSequenceParser', function (): void { parser2.registerCsiHandler({final: 'm'}, () => { order.push(2); return false; }); parser2.registerCsiHandler({final: 'm'}, () => { order.push(3); return false; }); parse(parser2, '\x1b[0m'); - chai.expect(order).eql([3, 2, 1]); + assert.deepEqual(order, [3, 2, 1]); }); it('Dispose should work', () => { const csiCustom: [string, ParamsArray, string][] = []; @@ -1343,8 +1343,8 @@ describe('EscapeSequenceParser', function (): void { const customHandler = parser2.registerCsiHandler({final: 'm'}, params => { csiCustom.push(['m', params.toArray(), '']); return true; }); customHandler.dispose(); parse(parser2, INPUT); - chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]); - chai.expect(csiCustom).eql([], 'Should not use custom handler as it was disposed'); + assert.deepEqual(csi, [['m', [1, 31], ''], ['m', [0], '']]); + assert.deepEqual(csiCustom, [], 'Should not use custom handler as it was disposed'); }); it('Should not corrupt the parser when dispose is called twice', () => { const csiCustom: [string, ParamsArray, string][] = []; @@ -1353,11 +1353,11 @@ describe('EscapeSequenceParser', function (): void { customHandler.dispose(); customHandler.dispose(); parse(parser2, INPUT); - chai.expect(csi).eql([['m', [1, 31], ''], ['m', [0], '']]); - chai.expect(csiCustom).eql([], 'Should not use custom handler as it was disposed'); + assert.deepEqual(csi, [['m', [1, 31], ''], ['m', [0], '']]); + assert.deepEqual(csiCustom, [], 'Should not use custom handler as it was disposed'); }); }); - it('EXECUTE handler', function (): void { + it('EXECUTE handler', () => { parser2.setExecuteHandler('\n', function (): boolean { exe.push('\n'); return true; @@ -1367,25 +1367,25 @@ describe('EscapeSequenceParser', function (): void { return true; }); parse(parser2, INPUT); - chai.expect(exe).eql(['\r', '\n']); + assert.deepEqual(exe, ['\r', '\n']); parser2.clearExecuteHandler('\r'); parser2.clearExecuteHandler('\r'); // should not throw clearAccu(); parse(parser2, INPUT); - chai.expect(exe).eql(['\n']); + assert.deepEqual(exe, ['\n']); }); - it('OSC handler', function (): void { + it('OSC handler', () => { parser2.registerOscHandler(1, new OscHandler(function (data: string): boolean { osc.push([1, data]); return true; })); parse(parser2, INPUT); - chai.expect(osc).eql([[1, 'foo=bar']]); + assert.deepEqual(osc, [[1, 'foo=bar']]); parser2.clearOscHandler(1); parser2.clearOscHandler(1); // should not throw clearAccu(); parse(parser2, INPUT); - chai.expect(osc).eql([]); + assert.deepEqual(osc, []); }); describe('OSC custom handlers', () => { it('Prevent fallback', () => { @@ -1393,16 +1393,16 @@ describe('EscapeSequenceParser', function (): void { parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; })); parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; })); parse(parser2, INPUT); - chai.expect(osc).eql([], 'Should not fallback to original handler'); - chai.expect(oscCustom).eql([[1, 'foo=bar']]); + assert.deepEqual(osc, [], 'Should not fallback to original handler'); + assert.deepEqual(oscCustom, [[1, 'foo=bar']]); }); it('Allow fallback', () => { const oscCustom: [number, string][] = []; parser2.registerOscHandler(1, new OscHandler(data => { osc.push([1, data]); return true; })); parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return false; })); parse(parser2, INPUT); - chai.expect(osc).eql([[1, 'foo=bar']], 'Should fallback to original handler'); - chai.expect(oscCustom).eql([[1, 'foo=bar']]); + assert.deepEqual(osc, [[1, 'foo=bar']], 'Should fallback to original handler'); + assert.deepEqual(oscCustom, [[1, 'foo=bar']]); }); it('Multiple custom handlers fallback once', () => { const oscCustom: [number, string][] = []; @@ -1411,9 +1411,9 @@ describe('EscapeSequenceParser', function (): void { parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; })); parser2.registerOscHandler(1, new OscHandler(data => { oscCustom2.push([1, data]); return false; })); parse(parser2, INPUT); - chai.expect(osc).eql([], 'Should not fallback to original handler'); - chai.expect(oscCustom).eql([[1, 'foo=bar']]); - chai.expect(oscCustom2).eql([[1, 'foo=bar']]); + assert.deepEqual(osc, [], 'Should not fallback to original handler'); + assert.deepEqual(oscCustom, [[1, 'foo=bar']]); + assert.deepEqual(oscCustom2, [[1, 'foo=bar']]); }); it('Multiple custom handlers no fallback', () => { const oscCustom: [number, string][] = []; @@ -1422,9 +1422,9 @@ describe('EscapeSequenceParser', function (): void { parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; })); parser2.registerOscHandler(1, new OscHandler(data => { oscCustom2.push([1, data]); return true; })); parse(parser2, INPUT); - chai.expect(osc).eql([], 'Should not fallback to original handler'); - chai.expect(oscCustom).eql([], 'Should not fallback once'); - chai.expect(oscCustom2).eql([[1, 'foo=bar']]); + assert.deepEqual(osc, [], 'Should not fallback to original handler'); + assert.deepEqual(oscCustom, [], 'Should not fallback once'); + assert.deepEqual(oscCustom2, [[1, 'foo=bar']]); }); it('Execution order should go from latest handler down to the original', () => { const order: number[] = []; @@ -1432,7 +1432,7 @@ describe('EscapeSequenceParser', function (): void { parser2.registerOscHandler(1, new OscHandler(() => { order.push(2); return false; })); parser2.registerOscHandler(1, new OscHandler(() => { order.push(3); return false; })); parse(parser2, '\x1b]1;foo=bar\x1b\\'); - chai.expect(order).eql([3, 2, 1]); + assert.deepEqual(order, [3, 2, 1]); }); it('Dispose should work', () => { const oscCustom: [number, string][] = []; @@ -1440,8 +1440,8 @@ describe('EscapeSequenceParser', function (): void { const customHandler = parser2.registerOscHandler(1, new OscHandler(data => { oscCustom.push([1, data]); return true; })); customHandler.dispose(); parse(parser2, INPUT); - chai.expect(osc).eql([[1, 'foo=bar']]); - chai.expect(oscCustom).eql([], 'Should not use custom handler as it was disposed'); + assert.deepEqual(osc, [[1, 'foo=bar']]); + assert.deepEqual(oscCustom, [], 'Should not use custom handler as it was disposed'); }); it('Should not corrupt the parser when dispose is called twice', () => { const oscCustom: [number, string][] = []; @@ -1450,11 +1450,11 @@ describe('EscapeSequenceParser', function (): void { customHandler.dispose(); customHandler.dispose(); parse(parser2, INPUT); - chai.expect(osc).eql([[1, 'foo=bar']]); - chai.expect(oscCustom).eql([], 'Should not use custom handler as it was disposed'); + assert.deepEqual(osc, [[1, 'foo=bar']]); + assert.deepEqual(oscCustom, [], 'Should not use custom handler as it was disposed'); }); }); - it('DCS handler', function (): void { + it('DCS handler', () => { parser2.registerDcsHandler({intermediates: '+', final: 'p'}, { hook: function (params: IParams): void { dcs.push(['hook', '', params.toArray(), 0]); @@ -1473,7 +1473,7 @@ describe('EscapeSequenceParser', function (): void { }); parse(parser2, '\x1bP1;2;3+pabc'); parse(parser2, ';de\x9c'); - chai.expect(dcs).eql([ + assert.deepEqual(dcs, [ ['hook', '', [1, 2, 3], 0], ['put', 'abc'], ['put', ';de'], ['unhook'] @@ -1483,7 +1483,7 @@ describe('EscapeSequenceParser', function (): void { clearAccu(); parse(parser2, '\x1bP1;2;3+pabc'); parse(parser2, ';de\x9c'); - chai.expect(dcs).eql([]); + assert.deepEqual(dcs, []); }); describe('DCS custom handlers', () => { const DCS_INPUT = '\x1bP1;2;3+pabc\x1b\\'; @@ -1492,14 +1492,14 @@ describe('EscapeSequenceParser', function (): void { parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; })); parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; })); parse(parser2, DCS_INPUT); - chai.expect(dcsCustom).eql([['B', [1, 2, 3], 'abc']]); + assert.deepEqual(dcsCustom, [['B', [1, 2, 3], 'abc']]); }); it('Allow fallback', () => { const dcsCustom: [string, (number | number[])[], string][] = []; parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['A', params.toArray(), data]); return true; })); parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return false; })); parse(parser2, DCS_INPUT); - chai.expect(dcsCustom).eql([['B', [1, 2, 3], 'abc'], ['A', [1, 2, 3], 'abc']]); + assert.deepEqual(dcsCustom, [['B', [1, 2, 3], 'abc'], ['A', [1, 2, 3], 'abc']]); }); it('Multiple custom handlers fallback once', () => { const dcsCustom: [string, (number | number[])[], string][] = []; @@ -1507,7 +1507,7 @@ describe('EscapeSequenceParser', function (): void { parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; })); parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return false; })); parse(parser2, DCS_INPUT); - chai.expect(dcsCustom).eql([['C', [1, 2, 3], 'abc'], ['B', [1, 2, 3], 'abc']]); + assert.deepEqual(dcsCustom, [['C', [1, 2, 3], 'abc'], ['B', [1, 2, 3], 'abc']]); }); it('Multiple custom handlers no fallback', () => { const dcsCustom: [string, (number | number[])[], string][] = []; @@ -1515,7 +1515,7 @@ describe('EscapeSequenceParser', function (): void { parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; })); parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['C', params.toArray(), data]); return true; })); parse(parser2, DCS_INPUT); - chai.expect(dcsCustom).eql([['C', [1, 2, 3], 'abc']]); + assert.deepEqual(dcsCustom, [['C', [1, 2, 3], 'abc']]); }); it('Execution order should go from latest handler down to the original', () => { const order: number[] = []; @@ -1523,7 +1523,7 @@ describe('EscapeSequenceParser', function (): void { parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler(() => { order.push(2); return false; })); parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler(() => { order.push(3); return false; })); parse(parser2, DCS_INPUT); - chai.expect(order).eql([3, 2, 1]); + assert.deepEqual(order, [3, 2, 1]); }); it('Dispose should work', () => { const dcsCustom: [string, (number | number[])[], string][] = []; @@ -1531,7 +1531,7 @@ describe('EscapeSequenceParser', function (): void { const dispo = parser2.registerDcsHandler({intermediates: '+', final: 'p'}, new DcsHandler((data, params) => { dcsCustom.push(['B', params.toArray(), data]); return true; })); dispo.dispose(); parse(parser2, DCS_INPUT); - chai.expect(dcsCustom).eql([['A', [1, 2, 3], 'abc']]); + assert.deepEqual(dcsCustom, [['A', [1, 2, 3], 'abc']]); }); it('Should not corrupt the parser when dispose is called twice', () => { const dcsCustom: [string, (number | number[])[], string][] = []; @@ -1540,17 +1540,17 @@ describe('EscapeSequenceParser', function (): void { dispo.dispose(); dispo.dispose(); parse(parser2, DCS_INPUT); - chai.expect(dcsCustom).eql([['A', [1, 2, 3], 'abc']]); + assert.deepEqual(dcsCustom, [['A', [1, 2, 3], 'abc']]); }); }); - it('ERROR handler', function (): void { + it('ERROR handler', () => { let errorState: IParsingState | null = null; parser2.setErrorHandler(function (state: IParsingState): IParsingState { errorState = state; return state; }); parse(parser2, '\x1b[1;2;€;3m'); // faulty escape sequence - chai.expect(errorState).eql({ + assert.deepEqual(errorState, { position: 6, code: '€'.charCodeAt(0), currentState: ParserState.CSI_PARAM, @@ -1562,7 +1562,7 @@ describe('EscapeSequenceParser', function (): void { parser2.clearErrorHandler(); // should not throw errorState = null; parse(parser2, '\x1b[1;2;a;3m'); - chai.expect(errorState).eql(null); + assert.equal(errorState, null); }); }); describe('function identifiers', () => { @@ -1570,46 +1570,46 @@ describe('EscapeSequenceParser', function (): void { it('prefix range 0x3c .. 0x3f, one byte', () => { for (let i = 0x3c; i <= 0x3f; ++i) { const c = String.fromCharCode(i); - chai.expect(parser.identToString(parser.identifier({prefix: c, final: 'z'}))).eql(c + 'z'); + assert.equal(parser.identToString(parser.identifier({prefix: c, final: 'z'})), c + 'z'); } - chai.assert.throws(() => { parser.identifier({prefix: '\x3b', final: 'z'}); }, 'prefix must be in range 0x3c .. 0x3f'); - chai.assert.throws(() => { parser.identifier({prefix: '\x40', final: 'z'}); }, 'prefix must be in range 0x3c .. 0x3f'); - chai.assert.throws(() => { parser.identifier({prefix: '??', final: 'z'}); }, 'only one byte as prefix supported'); + assert.throws(() => { parser.identifier({prefix: '\x3b', final: 'z'}); }, 'prefix must be in range 0x3c .. 0x3f'); + assert.throws(() => { parser.identifier({prefix: '\x40', final: 'z'}); }, 'prefix must be in range 0x3c .. 0x3f'); + assert.throws(() => { parser.identifier({prefix: '??', final: 'z'}); }, 'only one byte as prefix supported'); }); it('intermediates range 0x20 .. 0x2f, up to two bytes', () => { for (let i = 0x20; i <= 0x2f; ++i) { const c = String.fromCharCode(i); - chai.expect(parser.identToString(parser.identifier({intermediates: c + c, final: 'z'}))).eql(c + c + 'z'); + assert.equal(parser.identToString(parser.identifier({intermediates: c + c, final: 'z'})), c + c + 'z'); } - chai.assert.throws(() => { parser.identifier({intermediates: '\x1f', final: 'z'}); }, 'intermediate must be in range 0x20 .. 0x2f'); - chai.assert.throws(() => { parser.identifier({intermediates: '\x30', final: 'z'}); }, 'intermediate must be in range 0x20 .. 0x2f'); - chai.assert.throws(() => { parser.identifier({intermediates: '!!!', final: 'z'}); }, 'only two bytes as intermediates are supported'); + assert.throws(() => { parser.identifier({intermediates: '\x1f', final: 'z'}); }, 'intermediate must be in range 0x20 .. 0x2f'); + assert.throws(() => { parser.identifier({intermediates: '\x30', final: 'z'}); }, 'intermediate must be in range 0x20 .. 0x2f'); + assert.throws(() => { parser.identifier({intermediates: '!!!', final: 'z'}); }, 'only two bytes as intermediates are supported'); }); it('final CSI/DCS range 0x40 .. 0x7e (default), one byte', () => { for (let i = 0x40; i <= 0x7e; ++i) { const c = String.fromCharCode(i); - chai.expect(parser.identToString(parser.identifier({final: c}))).eql(c); + assert.equal(parser.identToString(parser.identifier({final: c})), c); } - chai.assert.throws(() => { parser.identifier({final: '\x3f'}); }, 'final must be in range 64 .. 126'); - chai.assert.throws(() => { parser.identifier({final: '\x7f'}); }, 'final must be in range 64 .. 126'); - chai.assert.throws(() => { parser.identifier({final: 'zz'}); }, 'final must be a single byte'); + assert.throws(() => { parser.identifier({final: '\x3f'}); }, 'final must be in range 64 .. 126'); + assert.throws(() => { parser.identifier({final: '\x7f'}); }, 'final must be in range 64 .. 126'); + assert.throws(() => { parser.identifier({final: 'zz'}); }, 'final must be a single byte'); }); it('final ESC range 0x30 .. 0x7e, one byte', () => { for (let i = 0x30; i <= 0x7e; ++i) { const final = String.fromCharCode(i); let handler: IDisposable | undefined; - chai.assert.doesNotThrow(() => { handler = parser.registerEscHandler({final}, () => true); }, 'final must be in range 48 .. 126'); + assert.doesNotThrow(() => { handler = parser.registerEscHandler({final}, () => true); }, 'final must be in range 48 .. 126'); if (handler) handler.dispose(); } - chai.assert.throws(() => { parser.registerEscHandler({final: '\x2f'}, () => true); }, 'final must be in range 48 .. 126'); - chai.assert.throws(() => { parser.registerEscHandler({final: '\x7f'}, () => true); }, 'final must be in range 48 .. 126'); + assert.throws(() => { parser.registerEscHandler({final: '\x2f'}, () => true); }, 'final must be in range 48 .. 126'); + assert.throws(() => { parser.registerEscHandler({final: '\x7f'}, () => true); }, 'final must be in range 48 .. 126'); }); it('id calculation - should stacking prefix -> intermediate -> final', () => { - chai.expect(parser.identToString(parser.identifier({final: 'z'}))).eql('z'); - chai.expect(parser.identToString(parser.identifier({prefix: '?', final: 'z'}))).eql('?z'); - chai.expect(parser.identToString(parser.identifier({intermediates: '!', final: 'z'}))).eql('!z'); - chai.expect(parser.identToString(parser.identifier({prefix: '?', intermediates: '!', final: 'z'}))).eql('?!z'); - chai.expect(parser.identToString(parser.identifier({prefix: '?', intermediates: '!!', final: 'z'}))).eql('?!!z'); + assert.equal(parser.identToString(parser.identifier({final: 'z'})), 'z'); + assert.equal(parser.identToString(parser.identifier({prefix: '?', final: 'z'})), '?z'); + assert.equal(parser.identToString(parser.identifier({intermediates: '!', final: 'z'})), '!z'); + assert.equal(parser.identToString(parser.identifier({prefix: '?', intermediates: '!', final: 'z'})), '?!z'); + assert.equal(parser.identToString(parser.identifier({prefix: '?', intermediates: '!!', final: 'z'})), '?!!z'); }); }); describe('identifier invocation', () => { @@ -1623,7 +1623,7 @@ describe('EscapeSequenceParser', function (): void { h2.dispose(); h3.dispose(); parse(parser, '\x1bz\x1b!z\x1b!!z'); - chai.expect(callstack).eql(['z', '!z', '!!z']); + assert.deepEqual(callstack, ['z', '!z', '!!z']); }); it('CSI', () => { const callstack: any[] = []; @@ -1641,7 +1641,10 @@ describe('EscapeSequenceParser', function (): void { h5.dispose(); h6.dispose(); parse(parser, '\x1b[1;z\x1b[1;!z\x1b[1;!!z\x1b[?1;z\x1b[?1;!z\x1b[?1;!!z'); - chai.expect(callstack).eql([['z', [1, 0]], ['!z', [1, 0]], ['!!z', [1, 0]], ['?z', [1, 0]], ['?!z', [1, 0]], ['?!!z', [1, 0]]]); + assert.deepEqual( + callstack, + [['z', [1, 0]], ['!z', [1, 0]], ['!!z', [1, 0]], ['?z', [1, 0]], ['?!z', [1, 0]], ['?!!z', [1, 0]]] + ); }); it('DCS', () => { const callstack: any[] = []; @@ -1659,14 +1662,17 @@ describe('EscapeSequenceParser', function (): void { h5.dispose(); h6.dispose(); parse(parser, '\x1bP1;zAB\x1b\\\x1bP1;!zAB\x1b\\\x1bP1;!!zAB\x1b\\\x1bP?1;zAB\x1b\\\x1bP?1;!zAB\x1b\\\x1bP?1;!!zAB\x1b\\'); - chai.expect(callstack).eql([ - ['z', [1, 0], 'AB'], - ['!z', [1, 0], 'AB'], - ['!!z', [1, 0], 'AB'], - ['?z', [1, 0], 'AB'], - ['?!z', [1, 0], 'AB'], - ['?!!z', [1, 0], 'AB'] - ]); + assert.deepEqual( + callstack, + [ + ['z', [1, 0], 'AB'], + ['!z', [1, 0], 'AB'], + ['!!z', [1, 0], 'AB'], + ['?z', [1, 0], 'AB'], + ['?!z', [1, 0], 'AB'], + ['?!!z', [1, 0], 'AB'] + ] + ); }); }); });