diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index db74a4ea..fd65a6a8 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -21,7 +21,12 @@ jobs:
displayName: 'Install dependencies and build'
- script: |
yarn mocha
- displayName: 'Test'
+ displayName: 'Unit tests'
+ - script: |
+ yarn start &
+ sleep 5
+ yarn test-api --headless
+ displayName: 'Integration tests'
- script: |
yarn lint
displayName: 'Lint'
@@ -39,7 +44,7 @@ jobs:
displayName: 'Install dependencies and build'
- script: |
yarn mocha
- displayName: 'Test'
+ displayName: 'Unit tests'
- script: |
yarn lint
displayName: 'Lint'
@@ -62,7 +67,7 @@ jobs:
displayName: 'Install dependencies and build'
- script: |
yarn mocha
- displayName: 'Test'
+ displayName: 'Unit tests'
- script: |
yarn lint
displayName: 'Lint'
diff --git a/demo/client.ts b/demo/client.ts
index 90a913d7..b7409109 100644
--- a/demo/client.ts
+++ b/demo/client.ts
@@ -21,6 +21,7 @@ import { Terminal as TerminalType, ITerminalOptions } from 'xterm';
export interface IWindowWithTerminal extends Window {
term: TerminalType;
+ Terminal?: typeof TerminalType;
}
declare let window: IWindowWithTerminal;
@@ -57,8 +58,6 @@ function getSearchOptions(): ISearchOptions {
};
}
-createTerminal();
-
const disposeRecreateButtonHandler = () => {
// If the terminal exists dispose of it, otherwise recreate it
if (term) {
@@ -74,7 +73,12 @@ const disposeRecreateButtonHandler = () => {
}
};
-document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler);
+if (document.location.pathname === '/test') {
+ window.Terminal = Terminal;
+} else {
+ createTerminal();
+ document.getElementById('dispose').addEventListener('click', disposeRecreateButtonHandler);
+}
function createTerminal(): void {
// Clean terminal
diff --git a/demo/server.js b/demo/server.js
index 758023c7..8270a398 100644
--- a/demo/server.js
+++ b/demo/server.js
@@ -16,6 +16,10 @@ function startServer() {
res.sendFile(__dirname + '/index.html');
});
+ app.get('/test', function(req, res){
+ res.sendFile(__dirname + '/test.html');
+ });
+
app.get('/style.css', function(req, res){
res.sendFile(__dirname + '/style.css');
});
diff --git a/demo/test.html b/demo/test.html
new file mode 100644
index 00000000..275c542d
--- /dev/null
+++ b/demo/test.html
@@ -0,0 +1,12 @@
+
+
+
+ xterm.js integration test fixture
+
+
+
+
+
+
+
+
diff --git a/package.json b/package.json
index 1727af82..615962c4 100644
--- a/package.json
+++ b/package.json
@@ -12,6 +12,7 @@
"@types/jsdom": "11.0.1",
"@types/mocha": "^2.2.33",
"@types/node": "6.0.108",
+ "@types/puppeteer": "^1.12.4",
"@types/webpack": "^4.4.11",
"browserify": "^13.3.0",
"chai": "3.5.0",
@@ -54,6 +55,7 @@
"test-debug": "node --inspect-brk node_modules/.bin/gulp test",
"test-suite": "gulp mocha-suite --test",
"test-coverage": "nyc -x gulpfile.js -x '**/*test*' npm run mocha",
+ "test-api": "mocha \"**/*.api.js\"",
"mocha": "gulp test",
"prebuild": "tsc -b ./src/tsconfig.all.json",
"build": "gulp build",
@@ -61,5 +63,8 @@
"prepublishOnly": "npm run build",
"coveralls": "nyc report --reporter=text-lcov | coveralls",
"watch": "tsc -b -w ./src/tsconfig.all.json --preserveWatchOutput"
+ },
+ "dependencies": {
+ "puppeteer": "^1.15.0"
}
}
diff --git a/src/public/Terminal.api.ts b/src/public/Terminal.api.ts
new file mode 100644
index 00000000..168a9407
--- /dev/null
+++ b/src/public/Terminal.api.ts
@@ -0,0 +1,99 @@
+/**
+ * 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('API Integration Tests', () => {
+ before(async function(): Promise {
+ this.timeout(10000);
+ 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);
+ });
+
+ 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');
+ window.term.write('bar');
+ `);
+ assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(0, true)`), 'foobar');
+ });
+
+ it('writeln', async function(): Promise {
+ this.timeout(10000);
+ await openTerminal();
+ await page.evaluate(`
+ window.term.writeln('foo');
+ window.term.writeln('bar');
+ `);
+ assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(0, true)`), 'foo');
+ assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(1, true)`), 'bar');
+ });
+
+ it('clear', async function(): Promise {
+ this.timeout(10000);
+ await openTerminal({ rows: 5 });
+ await page.evaluate(`
+ window.term.write('test0');
+ for (let i = 1; i < 10; i++) {
+ window.term.write('\\n\\rtest' + i);
+ }
+ `);
+ await page.evaluate(`window.term.clear()`);
+ assert.equal(await page.evaluate(`window.term._core.buffer.lines.length`), '5');
+ assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(0, true)`), 'test9');
+ for (let i = 1; i < 5; i++) {
+ assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(${i}, true)`), '');
+ }
+ });
+
+ 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')`);
+ assert.equal(await page.evaluate(`window.term.getOption('rendererType')`), 'dom');
+ });
+});
+
+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');
+ }
+}