Merge branch '2444' of https://github.com/glneto/xterm.js into 2444

This commit is contained in:
Geraldo Neto
2019-11-02 19:22:09 -03:00
60 changed files with 1534 additions and 1426 deletions
+9 -2
View File
@@ -8,12 +8,19 @@ RUN apt-get update \
# Verify git and process tools are installed
RUN apt-get install -y git procps
# Install yarn
# Install yarn, puppeteer deps
RUN apt-get install -y curl apt-transport-https lsb-release \
&& curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
&& echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update \
&& apt-get -y install --no-install-recommends yarn
&& apt-get -y install --no-install-recommends \
yarn fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
# https://github.com/Googlechrome/puppeteer/issues/290#issuecomment-322921352
gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
# Clean up
RUN apt-get autoremove -y \
+1
View File
@@ -4,6 +4,7 @@ node_modules/
lib/
out/
out-test/
.nyc_output/
Makefile.gyp
*.Makefile
*.target.gyp.mk
+4
View File
@@ -52,3 +52,7 @@ By contributing code to xterm.js you:
holder has explicitly granted the right to use it like this,
through a compatible open source license or through a direct
agreement with you.)
### Test coverage
One area that always needs attention is improving out unit test coverage, you can view the code coverage report on [Azure Pipelines](https://dev.azure.com/xtermjs/xterm.js/_build/latest?definitionId=3) by clicking the Code Coverage tab.
@@ -21,7 +21,7 @@ describe('AttachAddon', () => {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-fit",
"version": "0.2.1",
"version": "0.3.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
+119
View File
@@ -0,0 +1,119 @@
/**
* Copyright (c) 2019 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 = 1024;
const height = 768;
describe('FitAddon', () => {
before(async function(): Promise<any> {
this.timeout(20000);
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
await page.goto(APP);
await openTerminal();
});
after(async () => {
await browser.close();
});
it('no terminal', async function(): Promise<any> {
await page.evaluate(`window.fit = new FitAddon();`);
assert.equal(await page.evaluate(`window.fit.proposeDimensions()`), undefined);
});
describe('proposeDimensions', () => {
afterEach(async () => {
return unloadFit();
});
it('default', async function(): Promise<any> {
await loadFit();
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
cols: 87,
rows: 26
});
});
it('width', async function(): Promise<any> {
await loadFit(1008);
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
cols: 110,
rows: 26
});
});
it('small', async function(): Promise<any> {
await loadFit(1, 1);
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
cols: 2,
rows: 1
});
});
});
describe('fit', () => {
afterEach(async () => {
return unloadFit();
});
it('default', async function(): Promise<any> {
await loadFit();
await page.evaluate(`window.fit.fit()`);
assert.equal(await page.evaluate(`window.term.cols`), 87);
assert.equal(await page.evaluate(`window.term.rows`), 26);
});
it('width', async function(): Promise<any> {
await loadFit(1008);
await page.evaluate(`window.fit.fit()`);
assert.equal(await page.evaluate(`window.term.cols`), 110);
assert.equal(await page.evaluate(`window.term.rows`), 26);
});
it('small', async function(): Promise<any> {
await loadFit(1, 1);
await page.evaluate(`window.fit.fit()`);
assert.equal(await page.evaluate(`window.term.cols`), 2);
assert.equal(await page.evaluate(`window.term.rows`), 1);
});
});
});
async function loadFit(width: number = 800, height: number = 450): Promise<void> {
await page.evaluate(`
window.fit = new FitAddon();
window.term.loadAddon(window.fit);
document.querySelector('#terminal-container').style.width='${width}px';
document.querySelector('#terminal-container').style.height='${height}px';
`);
}
async function unloadFit(): Promise<void> {
await page.evaluate(`window.fit.dispose();`);
}
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
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');
}
}
+5 -2
View File
@@ -17,6 +17,9 @@ interface ITerminalDimensions {
cols: number;
}
const MINIMUM_COLS = 2;
const MINIMUM_ROWS = 1;
export class FitAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
@@ -71,8 +74,8 @@ export class FitAddon implements ITerminalAddon {
const availableHeight = parentElementHeight - elementPaddingVer;
const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth;
const geometry = {
cols: Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth),
rows: Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight)
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)),
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight))
};
return geometry;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-search",
"version": "0.2.1",
"version": "0.3.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -15,13 +15,13 @@ const width = 800;
const height = 600;
describe('Search Tests', function (): void {
this.timeout(200000);
this.timeout(20000);
before(async function (): Promise<any> {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
@@ -98,6 +98,14 @@ describe('Search Tests', function (): void {
await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD');
});
it('Search for single result twice should not unselect it', async () => {
await writeSync('abc def');
assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc');
assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc');
});
});
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
+12 -5
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { Terminal, IDisposable, ITerminalAddon } from 'xterm';
import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm';
export interface ISearchOptions {
regex?: boolean;
@@ -64,12 +64,12 @@ export class SearchAddon implements ITerminalAddon {
let startCol = 0;
let startRow = 0;
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
const incremental = searchOptions ? searchOptions.incremental : false;
// Start from the selection end if there is a selection
// For incremental search, use existing row
const currentSelection = this._terminal.getSelectionPosition()!;
currentSelection = this._terminal.getSelectionPosition()!;
startRow = incremental ? currentSelection.startRow : currentSelection.endRow;
startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn;
}
@@ -110,6 +110,9 @@ export class SearchAddon implements ITerminalAddon {
}
}
// If there is only one result, return true.
if (!result && currentSelection) return true;
// Set selection and scroll if a result was found
return this._selectResult(result);
}
@@ -134,10 +137,11 @@ export class SearchAddon implements ITerminalAddon {
const isReverseSearch = true;
let startRow = this._terminal.buffer.baseY + this._terminal.rows;
let startCol = this._terminal.cols;
let result: ISearchResult | undefined = undefined;
let result: ISearchResult | undefined;
const incremental = searchOptions ? searchOptions.incremental : false;
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
const currentSelection = this._terminal.getSelectionPosition()!;
currentSelection = this._terminal.getSelectionPosition()!;
// Start from selection start if there is a selection
startRow = currentSelection.startRow;
startCol = currentSelection.startColumn;
@@ -180,6 +184,9 @@ export class SearchAddon implements ITerminalAddon {
}
}
// If there is only one result, return true.
if (!result && currentSelection) return true;
// Set selection and scroll if a result was found
return this._selectResult(result);
}
+2 -2
View File
@@ -17,7 +17,7 @@ declare module 'xterm-addon-search' {
/**
* Whether to search for a whole word, the result is only valid if it's
* suppounded in "non-word" characters such as `_`, `(`, `)` or space.
* surrounded in "non-word" characters such as `_`, `(`, `)` or space.
*/
wholeWord?: boolean;
@@ -27,7 +27,7 @@ declare module 'xterm-addon-search' {
caseSensitive?: boolean;
/**
* Whether to do an indcremental search, this will expand the selection if it
* Whether to do an incremental search, this will expand the selection if it
* still matches the term the user typed. Note that this only affects
* `findNext`, not `findPrevious`.
*/
@@ -20,7 +20,7 @@ describe('WebLinksAddon', () => {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-webgl",
"version": "0.2.1",
"version": "0.3.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -105,9 +105,6 @@ export class GlyphRenderer {
const gl = this._gl;
const program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource));
if (program === undefined) {
throw new Error('Could not create WebGL program');
}
this._program = program;
// Uniform locations
@@ -22,7 +22,7 @@ describe('WebGL Renderer Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
@@ -14,7 +14,7 @@ import { IWebGL2RenderingContext } from './Types';
import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { RenderModel, COMBINED_CHAR_BIT_MASK } from './RenderModel';
import { Disposable } from 'common/Lifecycle';
import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants';
import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants';
import { Terminal } from 'xterm';
import { getLuminance } from './ColorUtils';
import { IRenderLayer } from './renderLayer/Types';
+38 -35
View File
@@ -12,19 +12,32 @@ jobs:
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
- task: YarnInstaller@3
inputs:
versionSpec: "1.9.4"
versionSpec: '1.x'
displayName: 'Install Yarn'
- script: |
yarn
- script: yarn
displayName: 'Install dependencies and build'
- script: |
yarn test-unit
- script: yarn test-unit --forbid-only
displayName: 'Unit tests'
- script: |
yarn lint
- script: yarn lint
displayName: 'Lint'
- script: |
NODE_PATH=$(pwd)/out ./node_modules/.bin/nyc ./node_modules/.bin/mocha './out/*test.js' './out/**/*test.js'
./node_modules/.bin/nyc report --reporter=cobertura
displayName: 'Coverage report'
- task: PublishCodeCoverageResults@1
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/*coverage.xml'
displayName: 'Publish coverage'
- task: mspremier.BuildQualityChecks.QualityChecks-task.BuildQualityChecks@6
displayName: 'Check build quality'
inputs:
checkCoverage: true
coverageType: lines
coverageThreshold: 60
coverageFailOption: fixed
- job: macOS
pool:
@@ -34,14 +47,11 @@ jobs:
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- script: |
yarn
- script: yarn
displayName: 'Install dependencies and build'
- script: |
yarn test-unit
- script: yarn test-unit --forbid-only
displayName: 'Unit tests'
- script: |
yarn lint
- script: yarn lint
displayName: 'Lint'
- job: Windows
@@ -52,14 +62,11 @@ jobs:
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- script: |
yarn
- script: yarn
displayName: 'Install dependencies and build'
- script: |
yarn test-unit
- script: yarn test-unit --forbid-only
displayName: 'Unit tests'
- script: |
yarn lint
- script: yarn lint
displayName: 'Lint'
- job: Linux_IntegrationTests
@@ -70,17 +77,16 @@ jobs:
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
- task: YarnInstaller@3
inputs:
versionSpec: "1.9.4"
versionSpec: '1.x'
displayName: 'Install Yarn'
- script: |
yarn
- script: yarn
displayName: 'Install dependencies and build'
- script: |
yarn start &
sleep 10
yarn test-api --headless
yarn test-api --headless --forbid-only
displayName: 'Linux Integration tests'
- job: macOS_IntegrationTests
@@ -91,13 +97,12 @@ jobs:
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- script: |
yarn
- script: yarn
displayName: 'Install dependencies and build'
- script: |
yarn start &
sleep 10
yarn test-api --headless
yarn test-api --headless --forbid-only
displayName: 'MacOS Integration tests'
- job: Release
@@ -107,7 +112,7 @@ jobs:
- Windows
- Linux_IntegrationTests
- macOS_IntegrationTests
condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['Build.SourceBranch'], 'refs/heads/release/*')))
condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['FORCE_RELEASE'], 'true')))
pool:
vmImage: 'ubuntu-16.04'
steps:
@@ -115,13 +120,11 @@ jobs:
inputs:
versionSpec: '8.x'
displayName: 'Install Node.js'
- task: geeklearningio.gl-vsts-tasks-yarn.yarn-installer-task.YarnInstaller@2
- task: YarnInstaller@3
inputs:
versionSpec: "1.9.4"
versionSpec: '1.x'
displayName: 'Install Yarn'
- script: |
yarn
- script: yarn
displayName: 'Install dependencies and build'
- script: |
NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js
- script: NPM_AUTH_TOKEN="$(NPM_AUTH_TOKEN)" node ./bin/publish.js
displayName: 'Package and publish to npm'
+56
View File
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*
* Script to initialize addon packages under "addons/" with outer deps.
*/
const path = require('path');
const cp = require('child_process');
const fs = require('fs');
const PACKAGE_ROOT = path.join(__dirname, '..');
// install addon deps
const addonsPath = path.join(PACKAGE_ROOT, 'addons');
if (fs.existsSync(addonsPath)) {
console.log('pulling addon dependencies...');
// whether to use yarn or npm
let hasYarn = false;
try {
cp.execSync('yarn --version').toString();
hasYarn = true;
} catch(e) {}
// walk all addon folders
fs.readdir(addonsPath, (err, files) => {
files.forEach(folder => {
const addonPath = path.join(addonsPath, folder);
// install only if there are dependencies listed
let packageJson;
try {
packageJson = require(path.join(addonPath, 'package.json'));
} catch (e) {
// swallow as changing branches can leave folders around
}
if (packageJson
&& (
(packageJson.devDependencies && Object.keys(packageJson.devDependencies).length)
|| (packageJson.dependencies && Object.keys(packageJson.dependencies).length)
)
)
{
console.log('Preparing', folder);
if (hasYarn) {
cp.execSync('yarn', {cwd: addonPath});
} else {
cp.execSync('npm install', {cwd: addonPath});
}
} else {
console.log('Skipped', folder);
}
});
});
}
+19 -1
View File
@@ -5,6 +5,7 @@
const cp = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
// Setup auth
@@ -18,8 +19,9 @@ if (isDryRun) {
const changedFiles = getChangedFilesInCommit('HEAD');
// Publish xterm if any files were changed outside of the addons directory
let isStableRelease = false;
if (changedFiles.some(e => e.search(/^addons\//) === -1)) {
checkAndPublishPackage(path.resolve(__dirname, '..'));
isStableRelease = checkAndPublishPackage(path.resolve(__dirname, '..'));
}
// Publish addons if any files were changed inside of the addon
@@ -39,6 +41,11 @@ addonPackageDirs.forEach(p => {
}
});
// Publish website if it's a stable release
if (isStableRelease) {
updateWebsite();
}
function checkAndPublishPackage(packageDir) {
const packageJson = require(path.join(packageDir, 'package.json'));
@@ -76,6 +83,8 @@ function checkAndPublishPackage(packageDir) {
}
console.groupEnd();
return isStableRelease;
}
function getNextBetaVersion(packageJson) {
@@ -115,3 +124,12 @@ function getChangedFilesInCommit(commit) {
const changedFiles = output.split('\n').filter(e => e.length > 0);
return changedFiles;
}
function updateWebsite() {
console.log('Updating website');
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'website-'));
const packageJson = require(path.join(path.resolve(__dirname, '..'), 'package.json'));
if (!isDryRun) {
cp.spawnSync('sh', [path.join(__dirname, 'update-website.sh'), packageJson.version], { cwd, stdio: [process.stdin, process.stdout, process.stderr] });
}
}
+12 -5
View File
@@ -15,15 +15,22 @@ let testFiles = [
'./out/**/*test.js'
];
// ability to inject particular test files via
// yarn test [testFileA testFileB ...]
let flagArgs = [];
if (process.argv.length > 2) {
testFiles = process.argv.slice(2);
const args = process.argv.slice(2);
flagArgs = args.filter(e => e.startsWith('--'));
// ability to inject particular test files via
// yarn test [testFileA testFileB ...]
files = args.filter(e => !e.startsWith('--'));
if (files.length) {
testFiles = files;
}
}
const run = cp.spawnSync(
path.resolve(__dirname, '../node_modules/.bin/mocha'),
testFiles,
[...testFiles, ...flagArgs],
{
cwd: path.resolve(__dirname, '..'),
env,
@@ -31,4 +38,4 @@ const run = cp.spawnSync(
}
);
process.exit(run.status);
process.exit(run.status);

Some files were not shown because too many files have changed in this diff Show More