diff --git a/.vscode/launch.json b/.vscode/launch.json index f0bc9be8..5e481d5c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -13,10 +13,13 @@ "runtimeArgs": [ "--colors", "--recursive", - "${workspaceRoot}/lib/**/*.test.js" + "${workspaceRoot}/out/**/*.test.js" ], + "env": { + "NODE_PATH": "${workspaceRoot}/out" + }, "sourceMaps": true, - "outFiles": [ "${workspaceRoot}/lib/**/*.js" ], + "outFiles": [ "${workspaceRoot}/out/**/*.js" ], "internalConsoleOptions": "openOnSessionStart" }, { @@ -31,7 +34,7 @@ "runtimeArgs": [ "--colors", "--recursive", - "${workspaceRoot}/lib/**/*.api.js" + "${workspaceRoot}/out/**/*.api.js" ], "sourceMaps": true, "outFiles": [ "${workspaceRoot}/lib/**/*.js" ], diff --git a/bin/test.js b/bin/test.js index b57f0e50..89f0d61e 100644 --- a/bin/test.js +++ b/bin/test.js @@ -12,9 +12,7 @@ env.NODE_PATH = path.resolve(__dirname, '../out'); let testFiles = [ './out/*test.js', - './out/**/*test.js', - './out/*integration.js', - './out/**/*integration.js' + './out/**/*test.js' ]; // ability to inject particular test files via diff --git a/src/InputHandler.ts b/src/InputHandler.ts index c3344489..354f3962 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -7,7 +7,7 @@ import { IInputHandler, IInputHandlingTerminal } from './Types'; import { C0, C1 } from 'common/data/EscapeSequences'; import { CHARSETS, DEFAULT_CHARSET } from 'core/data/Charsets'; -import { wcwidth } from './CharWidth'; +import { wcwidth } from './common/CharWidth'; import { EscapeSequenceParser } from 'core/parser/EscapeSequenceParser'; import { IDisposable } from 'xterm'; import { Disposable } from 'common/Lifecycle'; diff --git a/src/Linkifier.ts b/src/Linkifier.ts index ae9a0734..a66c1388 100644 --- a/src/Linkifier.ts +++ b/src/Linkifier.ts @@ -5,7 +5,7 @@ import { ILinkifierEvent, ILinkMatcher, LinkMatcherHandler, ILinkMatcherOptions, ILinkifier, ITerminal, IBufferStringIteratorResult, IMouseZoneManager } from './Types'; import { MouseZone } from './MouseZoneManager'; -import { getStringCellWidth } from './CharWidth'; +import { getStringCellWidth } from 'common/CharWidth'; import { EventEmitter2, IEvent } from 'common/EventEmitter2'; /** diff --git a/src/Terminal.integration.ts b/src/Terminal2.test.ts similarity index 100% rename from src/Terminal.integration.ts rename to src/Terminal2.test.ts diff --git a/src/CharWidth.test.ts b/src/common/CharWidth.test.ts similarity index 70% rename from src/CharWidth.test.ts rename to src/common/CharWidth.test.ts index 4aec3995..6b2493ee 100644 --- a/src/CharWidth.test.ts +++ b/src/common/CharWidth.test.ts @@ -3,80 +3,8 @@ * @license MIT */ -import { TestTerminal } from './TestUtils.test'; import { assert } from 'chai'; -import { getStringCellWidth, wcwidth } from './CharWidth'; -import { IBuffer } from './Types'; -import { CellData, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from 'core/buffer/BufferLine'; - - -describe('getStringCellWidth', function(): void { - let terminal: TestTerminal; - - beforeEach(() => { - terminal = new TestTerminal({rows: 5, cols: 30}); - }); - - function sumWidths(buffer: IBuffer, start: number, end: number, sentinel: string): number { - let result = 0; - for (let i = start; i < end; ++i) { - const line = buffer.lines.get(i); - for (let j = 0; j < line.length; ++j) { // TODO: change to trimBorder with multiline - const ch = line.loadCell(j, new CellData()).getAsCharData(); - result += ch[CHAR_DATA_WIDTH_INDEX]; - // return on sentinel - if (ch[CHAR_DATA_CHAR_INDEX] === sentinel) { - return result; - } - } - } - return result; - } - - it('ASCII chars', function(): void { - const input = 'This is just ASCII text.#'; - terminal.writeSync(input); - const s = terminal.buffer.iterator(true).next().content; - assert.equal(input, s); - assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); - }); - it('combining chars', function(): void { - const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301#'; - terminal.writeSync(input); - const s = terminal.buffer.iterator(true).next().content; - assert.equal(input, s); - assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); - }); - it('surrogate chars', function(): void { - const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞#'; - terminal.writeSync(input); - const s = terminal.buffer.iterator(true).next().content; - assert.equal(input, s); - assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); - }); - it('surrogate combining chars', function(): void { - const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301#'; - terminal.writeSync(input); - const s = terminal.buffer.iterator(true).next().content; - assert.equal(input, s); - assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); - }); - it('fullwidth chars', function(): void { - const input = '1234567890#'; - terminal.writeSync(input); - const s = terminal.buffer.iterator(true).next().content; - assert.equal(input, s); - assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); - }); - it('fullwidth chars offset 1', function(): void { - const input = 'a1234567890#'; - terminal.writeSync(input); - const s = terminal.buffer.iterator(true).next().content; - assert.equal(input, s); - assert.equal(getStringCellWidth(s), sumWidths(terminal.buffer, 0, 1, '#')); - }); - // TODO: multiline tests once #1685 is resolved -}); +import { wcwidth } from 'common/CharWidth'; it('wcwidth should match all values from the old implementation', function(): void { // old implementation @@ -197,7 +125,7 @@ it('wcwidth should match all values from the old implementation', function(): vo return 1; } const control = opts.control | 0; - let table: number[] | Uint32Array = null; + let table: number[] | Uint32Array | undefined; function initTable(): number[] | Uint32Array { // lookup table for BMP const CODEPOINTS = 65536; // BMP holds 65536 codepoints diff --git a/src/CharWidth.ts b/src/common/CharWidth.ts similarity index 100% rename from src/CharWidth.ts rename to src/common/CharWidth.ts diff --git a/test/CharWidth.api.ts b/test/CharWidth.api.ts new file mode 100644 index 00000000..035208cc --- /dev/null +++ b/test/CharWidth.api.ts @@ -0,0 +1,113 @@ +/** + * Copyright (c) 2017 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import * as puppeteer from 'puppeteer'; +import { assert } from 'chai'; +import { ITerminalOptions } from 'xterm'; + +const APP = 'http://127.0.0.1:3000/test'; + +let browser: puppeteer.Browser; +let page: puppeteer.Page; +const width = 800; +const height = 600; + +describe('CharWidth Integration Tests', function(): void { + this.timeout(20000); + + before(async function(): Promise { + browser = await puppeteer.launch({ + headless: process.argv.indexOf('--headless') !== -1, + slowMo: 80, + args: [`--window-size=${width},${height}`] + }); + page = (await browser.pages())[0]; + await page.setViewport({ width, height }); + await page.goto(APP); + await openTerminal({ rows: 5, cols: 30 }); + }); + + after(() => { + browser.close(); + }); + + describe('getStringCellWidth', () => { + beforeEach(async () => { + await page.evaluate(`window.term.reset()`); + }); + + it('ASCII chars', async function(): Promise { + const input = 'This is just ASCII text.#'; + await page.evaluate(`window.term.write('${input}')`); + assert.equal(25, await sumWidths(0, 1, '#')); + }); + + it('combining chars', async function(): Promise { + const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301#'; + await page.evaluate(`window.term.write('${input}')`); + assert.equal(10, await sumWidths(0, 1, '#')); + }); + + it('surrogate chars', async function(): Promise { + const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞#'; + await page.evaluate(`window.term.write('${input}')`); + assert.equal(28, await sumWidths(0, 1, '#')); + }); + + it('surrogate combining chars', async function(): Promise { + const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301#'; + await page.evaluate(`window.term.write('${input}')`); + assert.equal(12, await sumWidths(0, 1, '#')); + }); + + it('fullwidth chars', async function(): Promise { + const input = '1234567890#'; + await page.evaluate(`window.term.write('${input}')`); + assert.equal(21, await sumWidths(0, 1, '#')); + }); + + it('fullwidth chars offset 1', async function(): Promise { + const input = 'a1234567890#'; + await page.evaluate(`window.term.write('${input}')`); + assert.equal(22, await sumWidths(0, 1, '#')); + }); + + // TODO: multiline tests once #1685 is resolved + }); +}); + +async function openTerminal(options: ITerminalOptions = {}): Promise { + await page.evaluate(`window.term = new Terminal(${JSON.stringify(options)})`); + await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`); + if (options.rendererType === 'dom') { + await page.waitForSelector('.xterm-rows'); + } else { + await page.waitForSelector('.xterm-text-layer'); + } +} + +async function sumWidths(start: number, end: number, sentinel: string): Promise { + await page.evaluate(` + (function() { + window.result = 0; + const buffer = window.term.buffer; + for (let i = ${start}; i < ${end}; i++) { + const line = buffer.getLine(i); + let j = 0; + while (true) { + const cell = line.getCell(j++); + if (!cell) { + break; + } + window.result += cell.width; + if (cell.char === '${sentinel}') { + return; + } + } + } + })(); + `); + return await page.evaluate(`window.result`); +} diff --git a/src/InputHandler.api.ts b/test/InputHandler.api.ts similarity index 99% rename from src/InputHandler.api.ts rename to test/InputHandler.api.ts index fd2dfaae..3a95feca 100644 --- a/src/InputHandler.api.ts +++ b/test/InputHandler.api.ts @@ -5,7 +5,7 @@ import * as puppeteer from 'puppeteer'; import { assert } from 'chai'; -import { ITerminalOptions } from './Types'; +import { ITerminalOptions } from 'xterm'; const APP = 'http://127.0.0.1:3000/test'; diff --git a/src/public/Terminal.api.ts b/test/Terminal.api.ts similarity index 99% rename from src/public/Terminal.api.ts rename to test/Terminal.api.ts index 75deba5b..e536e88a 100644 --- a/src/public/Terminal.api.ts +++ b/test/Terminal.api.ts @@ -5,7 +5,7 @@ import * as puppeteer from 'puppeteer'; import { assert } from 'chai'; -import { ITerminalOptions } from '../Types'; +import { ITerminalOptions } from 'xterm'; const APP = 'http://127.0.0.1:3000/test'; diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 00000000..fce9a1ba --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "es6", + ], + "rootDir": ".", + "outDir": "../out/test", + "types": [ + "../node_modules/@types/mocha" + ], + "sourceMap": true, + "removeComments": true, + "pretty": true, + "strict": true + }, + "include": [ + "./**/*", + "../typings/xterm.d.ts" + ] +} diff --git a/tsconfig.all.json b/tsconfig.all.json index 4de8e9b5..d2670811 100644 --- a/tsconfig.all.json +++ b/tsconfig.all.json @@ -3,6 +3,7 @@ "include": [], "references": [ { "path": "./src" }, + { "path": "./test" }, { "path": "./addons/xterm-addon-attach/src" }, { "path": "./addons/xterm-addon-fit/src" }, { "path": "./addons/xterm-addon-search/src" },