diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f6acee37..032aeef6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -22,11 +22,6 @@ jobs: - script: | yarn mocha displayName: 'Unit tests' - - script: | - yarn start & - sleep 5 - yarn test-api --headless - displayName: 'Integration tests' - script: | yarn lint displayName: 'Lint' @@ -71,13 +66,35 @@ jobs: - script: | yarn lint displayName: 'Lint' + +- job: IntegrationTests + pool: + vmImage: 'ubuntu-16.04' + steps: + - task: NodeTool@0 + inputs: + versionSpec: '8.x' + displayName: 'Install Node.js' + - task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2 + inputs: + versionSpec: "1.9.4" + displayName: 'Install Yarn' + - script: | + yarn + displayName: 'Install dependencies and build' + - script: | + yarn start & + sleep 10 + yarn test-api --headless + displayName: 'Integration tests' - job: Release dependsOn: - Linux - macOS - Windows - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master')) + - IntegrationTests + condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['Build.SourceBranch'], 'refs/heads/release/*'))) pool: vmImage: 'ubuntu-16.04' steps: diff --git a/src/InputHandler.api.ts b/src/InputHandler.api.ts new file mode 100644 index 00000000..ef92f29d --- /dev/null +++ b/src/InputHandler.api.ts @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import * as puppeteer from 'puppeteer'; +import { assert } from 'chai'; +import { ITerminalOptions } from './Types'; + +const APP = 'http://127.0.0.1:3000/test'; + +let browser: puppeteer.Browser; +let page: puppeteer.Page; +const width = 800; +const height = 600; + +describe('InputHandler Integration Tests', function(): void { + this.timeout(10000); + + 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 }); + }); + + after(() => { + browser.close(); + }); + + beforeEach(async () => { + await page.goto(APP); + }); + + describe('Device Status Report (DSR)', () => { + it('Status Report - CSI 5 n', async function(): Promise { + await openTerminal(); + await page.evaluate(` + window.term.onData(e => window.result = e); + window.term.write('\\x1b[5n'); + `); + assert.equal(await page.evaluate(`window.result`), '\x1b[0n'); + }); + + it('Report Cursor Position (CPR) - CSI 6 n', async function(): Promise { + await openTerminal(); + await page.evaluate(`window.term.write('\\n\\nfoo')`); + assert.deepEqual(await page.evaluate(` + [window.term.buffer.cursorY, window.term.buffer.cursorX] + `), [2, 3]); + await page.evaluate(` + window.term.onData(e => window.result = e); + window.term.write('\\x1b[6n'); + `); + assert.equal(await page.evaluate(`window.result`), '\x1b[3;4R'); + }); + + it('Report Cursor Position (DECXCPR) - CSI ? 6 n', async function(): Promise { + await openTerminal(); + await page.evaluate(`window.term.write('\\n\\nfoo')`); + assert.deepEqual(await page.evaluate(` + [window.term.buffer.cursorY, window.term.buffer.cursorX] + `), [2, 3]); + await page.evaluate(` + window.term.onData(e => window.result = e); + window.term.write('\\x1b[?6n'); + `); + assert.equal(await page.evaluate(`window.result`), '\x1b[?3;4R'); + }); + }); +}); + +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'); + } +} diff --git a/src/Terminal.ts b/src/Terminal.ts index 8517fabc..04c9a47e 100644 --- a/src/Terminal.ts +++ b/src/Terminal.ts @@ -353,6 +353,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II this._inputHandler = new InputHandler(this); this._inputHandler.onCursorMove(() => this._onCursorMove.fire()); this._inputHandler.onLineFeed(() => this._onLineFeed.fire()); + this._inputHandler.onData(e => this._onData.fire(e)); this.register(this._inputHandler); this.selectionManager = this.selectionManager || null; diff --git a/src/public/Terminal.api.ts b/src/public/Terminal.api.ts index d8325229..afad2699 100644 --- a/src/public/Terminal.api.ts +++ b/src/public/Terminal.api.ts @@ -14,9 +14,10 @@ let page: puppeteer.Page; const width = 800; const height = 600; -describe('API Integration Tests', () => { +describe('API Integration Tests', function(): void { + this.timeout(10000); + before(async function(): Promise { - this.timeout(10000); browser = await puppeteer.launch({ headless: process.argv.indexOf('--headless') !== -1, slowMo: 80, @@ -35,14 +36,12 @@ describe('API Integration Tests', () => { }); it('Default options', async function(): Promise { - this.timeout(10000); await openTerminal(); assert.equal(await page.evaluate(`window.term.cols`), 80); assert.equal(await page.evaluate(`window.term.rows`), 24); }); it('write', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.term.write('foo'); @@ -53,7 +52,6 @@ describe('API Integration Tests', () => { }); it('writeln', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.term.writeln('foo'); @@ -66,7 +64,6 @@ describe('API Integration Tests', () => { }); it('writeUtf8', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` // foo @@ -80,7 +77,6 @@ describe('API Integration Tests', () => { }); it('clear', async function(): Promise { - this.timeout(10000); await openTerminal({ rows: 5 }); await page.evaluate(` window.term.write('test0'); @@ -97,7 +93,6 @@ describe('API Integration Tests', () => { }); it('getOption, setOption', async function(): Promise { - this.timeout(10000); await openTerminal(); assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'canvas'); await page.evaluate(`window.term.setOption('rendererType', 'dom')`); @@ -106,7 +101,6 @@ describe('API Integration Tests', () => { describe('renderer', () => { it('foreground', async function(): Promise { - this.timeout(10000); await openTerminal({ rendererType: 'dom' }); await page.evaluate(`window.term.write('\\x1b[30m0\\x1b[31m1\\x1b[32m2\\x1b[33m3\\x1b[34m4\\x1b[35m5\\x1b[36m6\\x1b[37m7')`); assert.deepEqual(await page.evaluate(` @@ -131,7 +125,6 @@ describe('API Integration Tests', () => { }); it('background', async function(): Promise { - this.timeout(10000); await openTerminal({ rendererType: 'dom' }); await page.evaluate(`window.term.write('\\x1b[40m0\\x1b[41m1\\x1b[42m2\\x1b[43m3\\x1b[44m4\\x1b[45m5\\x1b[46m6\\x1b[47m7')`); assert.deepEqual(await page.evaluate(` @@ -157,7 +150,6 @@ describe('API Integration Tests', () => { }); it('selection', async function(): Promise { - this.timeout(10000); await openTerminal({ rows: 5, cols: 5 }); await page.evaluate(`window.term.write('\\n\\nfoo\\n\\n\\rbar\\n\\n\\rbaz')`); assert.equal(await page.evaluate(`window.term.hasSelection()`), false); @@ -178,7 +170,6 @@ describe('API Integration Tests', () => { }); it('focus, blur', async function(): Promise { - this.timeout(10000); await openTerminal(); assert.equal(await page.evaluate(`document.activeElement.className`), ''); await page.evaluate(`window.term.focus()`); @@ -189,7 +180,6 @@ describe('API Integration Tests', () => { describe('loadAddon', () => { it('constructor', async function(): Promise { - this.timeout(10000); await openTerminal({ cols: 5 }); await page.evaluate(` window.cols = 0; @@ -202,7 +192,6 @@ describe('API Integration Tests', () => { }); it('dispose (addon)', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.disposeCalled = false @@ -218,7 +207,6 @@ describe('API Integration Tests', () => { }); it('dispose (terminal)', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.disposeCalled = false @@ -235,7 +223,6 @@ describe('API Integration Tests', () => { describe('Events', () => { it('onCursorMove', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.callCount = 0; @@ -248,7 +235,6 @@ describe('API Integration Tests', () => { }); it('onData', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.calls = []; @@ -259,7 +245,6 @@ describe('API Integration Tests', () => { }); it('onKey', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.calls = []; @@ -270,7 +255,6 @@ describe('API Integration Tests', () => { }); it('onLineFeed', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.callCount = 0; @@ -283,7 +267,6 @@ describe('API Integration Tests', () => { }); it('onScroll', async function(): Promise { - this.timeout(10000); await openTerminal({ rows: 5 }); await page.evaluate(` window.calls = []; @@ -300,7 +283,6 @@ describe('API Integration Tests', () => { }); it('onSelectionChange', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.callCount = 0; @@ -314,7 +296,6 @@ describe('API Integration Tests', () => { }); it('onRender', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.calls = []; @@ -328,7 +309,6 @@ describe('API Integration Tests', () => { }); it('onResize', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.calls = []; @@ -342,7 +322,6 @@ describe('API Integration Tests', () => { }); it('onTitleChange', async function(): Promise { - this.timeout(10000); await openTerminal(); await page.evaluate(` window.calls = []; @@ -356,7 +335,6 @@ describe('API Integration Tests', () => { describe('buffer', () => { it('cursorX, cursorY', async function(): Promise { - this.timeout(10000); await openTerminal({ rows: 5, cols: 5 }); assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 0); assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 0); @@ -378,7 +356,6 @@ describe('API Integration Tests', () => { }); it('viewportY', async function(): Promise { - this.timeout(10000); await openTerminal({ rows: 5 }); assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 0); await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); @@ -394,7 +371,6 @@ describe('API Integration Tests', () => { }); it('baseY', async function(): Promise { - this.timeout(10000); await openTerminal({ rows: 5 }); assert.equal(await page.evaluate(`window.term.buffer.baseY`), 0); await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); @@ -410,7 +386,6 @@ describe('API Integration Tests', () => { }); it('length', async function(): Promise { - this.timeout(10000); await openTerminal({ rows: 5 }); assert.equal(await page.evaluate(`window.term.buffer.length`), 5); await page.evaluate(`window.term.write('\\n\\n\\n\\n')`); @@ -423,14 +398,12 @@ describe('API Integration Tests', () => { describe('getLine', () => { it('invalid index', async function(): Promise { - this.timeout(10000); await openTerminal({ rows: 5 }); assert.equal(await page.evaluate(`window.term.buffer.getLine(-1)`), undefined); assert.equal(await page.evaluate(`window.term.buffer.getLine(5)`), undefined); }); it('isWrapped', async function(): Promise { - this.timeout(10000); await openTerminal({ cols: 5 }); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).isWrapped`), false); assert.equal(await page.evaluate(`window.term.buffer.getLine(1).isWrapped`), false); @@ -443,7 +416,6 @@ describe('API Integration Tests', () => { }); it('translateToString', async function(): Promise { - this.timeout(10000); await openTerminal({ cols: 5 }); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString()`), ' '); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), ''); @@ -459,7 +431,6 @@ describe('API Integration Tests', () => { }); it('getCell', async function(): Promise { - this.timeout(10000); await openTerminal({ cols: 5 }); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(-1)`), undefined); assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(5)`), undefined);