Merge branch 'master' into underline-dotted
This commit is contained in:
+3
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
@@ -13,9 +13,11 @@
|
||||
"src/headless/tsconfig.json",
|
||||
"test/api/tsconfig.json",
|
||||
"test/benchmark/tsconfig.json",
|
||||
"test/playwright/tsconfig.json",
|
||||
"addons/xterm-addon-attach/src/tsconfig.json",
|
||||
"addons/xterm-addon-attach/test/tsconfig.json",
|
||||
"addons/xterm-addon-canvas/src/tsconfig.json",
|
||||
"addons/xterm-addon-canvas/test/tsconfig.json",
|
||||
"addons/xterm-addon-fit/src/tsconfig.json",
|
||||
"addons/xterm-addon-fit/test/tsconfig.json",
|
||||
"addons/xterm-addon-image/src/tsconfig.json",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"es2021": true,
|
||||
"node": true
|
||||
},
|
||||
"parser": "@typescript-eslint/parser",
|
||||
|
||||
+92
-40
@@ -90,14 +90,15 @@ jobs:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
- name: Unzip artifacts (Linux, macOS)
|
||||
if: runner.os != 'Windows'
|
||||
run: unzip -o compressed-build.zip
|
||||
- name: Unzip artifacts (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: 7z x compressed-build.zip -aoa -o${{ github.workspace }}
|
||||
- name: Print directory structure
|
||||
run: ls -R
|
||||
- name: Unzip artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
|
||||
else
|
||||
unzip -o compressed-build.zip
|
||||
fi
|
||||
ls -R
|
||||
- name: Unit test coverage
|
||||
run: |
|
||||
yarn test-unit-coverage --forbid-only
|
||||
@@ -131,14 +132,15 @@ jobs:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
- name: Unzip artifacts (Linux, macOS)
|
||||
if: runner.os != 'Windows'
|
||||
run: unzip -o compressed-build.zip
|
||||
- name: Unzip artifacts (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: 7z x compressed-build.zip -aoa -o${{ github.workspace }}
|
||||
- name: Print directory structure
|
||||
run: ls -R
|
||||
- name: Unzip artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
|
||||
else
|
||||
unzip -o compressed-build.zip
|
||||
fi
|
||||
ls -R
|
||||
- name: Unit tests
|
||||
run: yarn test-unit --forbid-only
|
||||
|
||||
@@ -164,14 +166,15 @@ jobs:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
- name: Unzip artifacts (Linux, macOS)
|
||||
if: runner.os != 'Windows'
|
||||
run: unzip -o compressed-build.zip
|
||||
- name: Unzip artifacts (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: 7z x compressed-build.zip -aoa -o${{ github.workspace }}
|
||||
- name: Print directory structure
|
||||
run: ls -R
|
||||
- name: Unzip artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
|
||||
else
|
||||
unzip -o compressed-build.zip
|
||||
fi
|
||||
ls -R
|
||||
- name: Unit tests
|
||||
run: yarn test-unit --forbid-only
|
||||
|
||||
@@ -204,17 +207,65 @@ jobs:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
- name: Unzip artifacts (Linux, macOS)
|
||||
if: runner.os != 'Windows'
|
||||
run: unzip -o compressed-build.zip
|
||||
- name: Unzip artifacts (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: 7z x compressed-build.zip -aoa -o${{ github.workspace }}
|
||||
- name: Print directory structure
|
||||
run: ls -R
|
||||
- name: Unzip artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
|
||||
else
|
||||
unzip -o compressed-build.zip
|
||||
fi
|
||||
ls -R
|
||||
- name: Integration tests (${{ matrix.browser }})
|
||||
run: yarn test-api-${{ matrix.browser }} --headless --forbid-only
|
||||
|
||||
test-playwright-parallel:
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18] # just one as integration tests are about testing in browser
|
||||
runs-on: [ubuntu] # macos is flaky
|
||||
browser: [chromium, firefox, webkit]
|
||||
runs-on: ${{ matrix.runs-on }}-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Use Node.js ${{ matrix.node-version }}.x
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}.x
|
||||
cache: 'yarn'
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
yarn --frozen-lockfile
|
||||
yarn install-addons
|
||||
- name: Install playwright
|
||||
run: npx playwright install --with-deps ${{ matrix.browser }}
|
||||
- name: Wait for build job
|
||||
uses: NathanFirmo/wait-for-other-job@v1.1.1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
job: build
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
- name: Unzip artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
|
||||
else
|
||||
unzip -o compressed-build.zip
|
||||
fi
|
||||
ls -R
|
||||
- name: Build demo
|
||||
run: yarn build-demo
|
||||
- name: Integration tests (core) # Tests use 50% workers to reduce flakiness
|
||||
run: yarn test-playwright-${{ matrix.browser }} --workers=50% --forbid-only --suite=core
|
||||
- name: Integration tests (xterm-addon-canvas)
|
||||
run: yarn test-playwright-${{ matrix.browser }} --workers=50% --forbid-only --suite=xterm-addon-canvas
|
||||
- name: Integration tests (xterm-addon-webgl)
|
||||
run: yarn test-playwright-${{ matrix.browser }} --workers=50% --forbid-only --suite=xterm-addon-webgl
|
||||
|
||||
test-api:
|
||||
needs: build
|
||||
timeout-minutes: 20
|
||||
@@ -240,13 +291,14 @@ jobs:
|
||||
- uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: build-artifacts
|
||||
- name: Unzip artifacts (Linux, macOS)
|
||||
if: runner.os != 'Windows'
|
||||
run: unzip -o compressed-build.zip
|
||||
- name: Unzip artifacts (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
run: 7z x compressed-build.zip -aoa -o${{ github.workspace }}
|
||||
- name: Print directory structure
|
||||
run: ls -R
|
||||
- name: Unzip artifacts
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "$RUNNER_OS" == "Windows" ]; then
|
||||
pwsh -Command "7z x compressed-build.zip -aoa -o${{ github.workspace }}"
|
||||
else
|
||||
unzip -o compressed-build.zip
|
||||
fi
|
||||
ls -R
|
||||
- name: Integration tests (${{ matrix.browser }})
|
||||
run: yarn test-api-${{ matrix.browser }} --headless --forbid-only
|
||||
|
||||
Vendored
+11
-6
@@ -43,7 +43,14 @@
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Demo Client",
|
||||
"name": "Demo Client (Chrome)",
|
||||
"url": "http://127.0.0.1:3000",
|
||||
"webRoot": "${workspaceFolder}/"
|
||||
},
|
||||
{
|
||||
"type": "msedge",
|
||||
"request": "launch",
|
||||
"name": "Demo Client (Edge)",
|
||||
"url": "http://127.0.0.1:3000",
|
||||
"webRoot": "${workspaceFolder}/"
|
||||
},
|
||||
@@ -52,11 +59,9 @@
|
||||
"request": "launch",
|
||||
"name": "Demo Server",
|
||||
"runtimeExecutable": "npm",
|
||||
"runtimeArgs": [
|
||||
"run",
|
||||
"start-debug"
|
||||
],
|
||||
"port": 9229,
|
||||
"runtimeArgs": ["start"],
|
||||
"stopOnEntry": true,
|
||||
"runtimeVersion": "16",
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "App listening to (http://.*?:[0-9]+)"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-attach",
|
||||
"version": "0.8.0",
|
||||
"version": "0.9.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import WebSocket = require('ws');
|
||||
import { openTerminal, pollFor, launchBrowser } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { Browser, Page } from '@playwright/test';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-canvas",
|
||||
"version": "0.4.0",
|
||||
"version": "0.5.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
@@ -19,7 +19,8 @@
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package"
|
||||
"prepublishOnly": "npm run package",
|
||||
"start-server-only": "node ../../demo/start-server-only"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"xterm": "^5.0.0"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es6"
|
||||
"es2021"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out",
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import test from '@playwright/test';
|
||||
import { ISharedRendererTestContext, injectSharedRendererTests } from '../../../out-test/playwright/SharedRendererTests';
|
||||
import { ITestContext, createTestContext, openTerminal } from '../../../out-test/playwright/TestUtils';
|
||||
|
||||
let ctx: ITestContext;
|
||||
const ctxWrapper: ISharedRendererTestContext = {
|
||||
value: undefined,
|
||||
skipCanvasExceptions: true
|
||||
} as any;
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
ctx = await createTestContext(browser);
|
||||
await openTerminal(ctx);
|
||||
ctxWrapper.value = ctx;
|
||||
await ctx.page.evaluate(`
|
||||
window.addon = new window.CanvasAddon(true);
|
||||
window.term.loadAddon(window.addon);
|
||||
`);
|
||||
});
|
||||
test.afterAll(async () => await ctx.page.close());
|
||||
|
||||
test.describe('Canvas Renderer Integration Tests', () => {
|
||||
// HACK: The tests fail for an unknown reason
|
||||
test.skip(({ browserName }) => browserName === 'webkit');
|
||||
|
||||
injectSharedRendererTests(ctxWrapper);
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: '.',
|
||||
timeout: 10000,
|
||||
projects: [
|
||||
{
|
||||
name: 'Chrome Stable',
|
||||
use: {
|
||||
browserName: 'chromium',
|
||||
channel: 'chrome'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Firefox Stable',
|
||||
use: {
|
||||
browserName: 'firefox'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'WebKit',
|
||||
use: {
|
||||
browserName: 'webkit'
|
||||
}
|
||||
}
|
||||
],
|
||||
reporter: 'list',
|
||||
webServer: {
|
||||
command: 'npm run start-server-only',
|
||||
port: 3000,
|
||||
timeout: 120000,
|
||||
reuseExistingServer: !process.env.CI
|
||||
}
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"es6",
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"common/*": [
|
||||
"../../../src/common/*"
|
||||
],
|
||||
"browser/*": [
|
||||
"../../../src/browser/*"
|
||||
]
|
||||
},
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../node_modules/@lunapaint/png-codec",
|
||||
"../../../out-test/playwright/TestUtils",
|
||||
"../../../out-test/playwright/SharedRendererTests"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../../typings/xterm.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../../src/common"
|
||||
},
|
||||
{
|
||||
"path": "../../../src/browser"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{ "path": "./src" }
|
||||
{ "path": "./src" },
|
||||
{ "path": "./test" }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-fit",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, launchBrowser } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { Browser, Page } from '@playwright/test';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-image",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, launchBrowser, pollFor } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { Browser, Page } from '@playwright/test';
|
||||
import { IImageAddonOptions } from '../src/Types';
|
||||
import { FINALIZER, introducer, sixelEncode } from 'sixel';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-ligatures",
|
||||
"version": "0.6.0",
|
||||
"version": "0.7.0",
|
||||
"description": "Add support for programming ligatures to xterm.js",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-search",
|
||||
"version": "0.12.0",
|
||||
"version": "0.13.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es6",
|
||||
"es2021",
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out",
|
||||
|
||||
@@ -7,7 +7,7 @@ import { assert } from 'chai';
|
||||
import { readFile } from 'fs';
|
||||
import { resolve } from 'path';
|
||||
import { openTerminal, writeSync, launchBrowser, timeout } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { Browser, Page } from '@playwright/test';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"es6",
|
||||
"es2021",
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["dom", "es6"],
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2021"
|
||||
],
|
||||
"outDir": "../out-benchmark",
|
||||
"types": ["../../../node_modules/@types/node"],
|
||||
"moduleResolution": "node",
|
||||
"strict": false,
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"module": "commonjs",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-serialize",
|
||||
"version": "0.10.0",
|
||||
"version": "0.11.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, writeSync, launchBrowser } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { Browser, Page } from '@playwright/test';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-unicode11",
|
||||
"version": "0.5.0",
|
||||
"version": "0.6.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, launchBrowser } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { Browser, Page } from '@playwright/test';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-web-links",
|
||||
"version": "0.8.0",
|
||||
"version": "0.9.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { openTerminal, pollFor, writeSync, launchBrowser } from '../../../out-test/api/TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
import { Browser, Page } from '@playwright/test';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"es2015"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "xterm-addon-webgl",
|
||||
"version": "0.15.0",
|
||||
"version": "0.16.0",
|
||||
"author": {
|
||||
"name": "The xterm.js authors",
|
||||
"url": "https://xtermjs.org/"
|
||||
@@ -19,7 +19,8 @@
|
||||
"build": "../../node_modules/.bin/tsc -p .",
|
||||
"prepackage": "npm run build",
|
||||
"package": "../../node_modules/.bin/webpack",
|
||||
"prepublishOnly": "npm run package"
|
||||
"prepublishOnly": "npm run package",
|
||||
"start-server-only": "node ../../demo/start-server-only"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"xterm": "^5.0.0"
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es6"
|
||||
"es2021"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import test from '@playwright/test';
|
||||
import { ISharedRendererTestContext, injectSharedRendererTests } from '../../../out-test/playwright/SharedRendererTests';
|
||||
import { ITestContext, createTestContext, openTerminal } from '../../../out-test/playwright/TestUtils';
|
||||
import { platform } from 'os';
|
||||
|
||||
let ctx: ITestContext;
|
||||
const ctxWrapper: ISharedRendererTestContext = { value: undefined } as any;
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
ctx = await createTestContext(browser);
|
||||
await openTerminal(ctx);
|
||||
ctxWrapper.value = ctx;
|
||||
await ctx.page.evaluate(`
|
||||
window.addon = new window.WebglAddon(true);
|
||||
window.term.loadAddon(window.addon);
|
||||
`);
|
||||
});
|
||||
test.afterAll(async () => await ctx.page.close());
|
||||
|
||||
test.describe('WebGL Renderer Integration Tests', async () => {
|
||||
// HACK: webgl2 is often not supported in headless firefox on Linux
|
||||
// https://github.com/microsoft/playwright/issues/11566
|
||||
if (platform() === 'linux') {
|
||||
test.skip(({ browserName }) => browserName === 'firefox');
|
||||
}
|
||||
|
||||
injectSharedRendererTests(ctxWrapper);
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: '.',
|
||||
timeout: 10000,
|
||||
projects: [
|
||||
{
|
||||
name: 'Chrome Stable',
|
||||
use: {
|
||||
browserName: 'chromium',
|
||||
channel: 'chrome'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Firefox Stable',
|
||||
use: {
|
||||
browserName: 'firefox'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'WebKit',
|
||||
use: {
|
||||
browserName: 'webkit'
|
||||
}
|
||||
}
|
||||
],
|
||||
reporter: 'list',
|
||||
webServer: {
|
||||
command: 'npm run start-server-only',
|
||||
port: 3000,
|
||||
timeout: 120000,
|
||||
reuseExistingServer: !process.env.CI
|
||||
}
|
||||
};
|
||||
export default config;
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"lib": [
|
||||
"es6",
|
||||
"es2021",
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../out-test",
|
||||
@@ -20,9 +20,10 @@
|
||||
},
|
||||
"strict": true,
|
||||
"types": [
|
||||
"../../../node_modules/@types/mocha",
|
||||
"../../../node_modules/@types/node",
|
||||
"../../../out-test/api/TestUtils"
|
||||
"../../../node_modules/@lunapaint/png-codec",
|
||||
"../../../out-test/playwright/TestUtils",
|
||||
"../../../out-test/playwright/SharedRendererTests"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ const addonPackageDirs = [
|
||||
path.resolve(__dirname, '../addons/xterm-addon-attach'),
|
||||
path.resolve(__dirname, '../addons/xterm-addon-canvas'),
|
||||
path.resolve(__dirname, '../addons/xterm-addon-fit'),
|
||||
path.resolve(__dirname, '../addons/xterm-addon-image'),
|
||||
// path.resolve(__dirname, '../addons/xterm-addon-image'),
|
||||
path.resolve(__dirname, '../addons/xterm-addon-ligatures'),
|
||||
path.resolve(__dirname, '../addons/xterm-addon-search'),
|
||||
path.resolve(__dirname, '../addons/xterm-addon-serialize'),
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
const cp = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
let argv = process.argv.slice(2);
|
||||
let suiteFilter = undefined;
|
||||
while (argv.some(e => e.startsWith('--suite='))) {
|
||||
const i = argv.findIndex(e => e.startsWith('--suite='));
|
||||
const match = argv[i].match(/--suite=(?<suitename>.+)/)
|
||||
suiteFilter = match?.groups?.suitename ?? undefined;
|
||||
argv.splice(i, 1);
|
||||
}
|
||||
|
||||
let configs = [
|
||||
{ name: 'core', path: 'out-test/playwright/playwright.config.js' },
|
||||
{ name: 'xterm-addon-canvas', path: 'addons/xterm-addon-canvas/out-test/playwright.config.js' },
|
||||
{ name: 'xterm-addon-webgl', path: 'addons/xterm-addon-webgl/out-test/playwright.config.js' }
|
||||
];
|
||||
|
||||
if (suiteFilter) {
|
||||
configs = configs.filter(e => e.name === suiteFilter);
|
||||
}
|
||||
|
||||
function npmBinScript(script) {
|
||||
return path.resolve(__dirname, `../node_modules/.bin/` + (process.platform === 'win32' ?
|
||||
`${script}.cmd` : script));
|
||||
}
|
||||
|
||||
async function run() {
|
||||
for (const config of configs) {
|
||||
const command = npmBinScript('playwright');
|
||||
const args = ['test', '-c', config.path, ...argv];
|
||||
console.log(`Running suite \x1b[1;34m${config.name}...\x1b[0m`);
|
||||
console.log(`\n\x1b[32m${command}\x1b[0m`, args);
|
||||
const run = cp.spawnSync(command, args, {
|
||||
cwd: path.resolve(__dirname, '..'),
|
||||
stdio: 'inherit'
|
||||
}
|
||||
);
|
||||
if (run.status) {
|
||||
process.exit(run.status);
|
||||
}
|
||||
}
|
||||
}
|
||||
run();
|
||||
+20
-4
@@ -13,7 +13,6 @@ import { Terminal } from '../out/browser/public/Terminal';
|
||||
import { AttachAddon } from '../addons/xterm-addon-attach/out/AttachAddon';
|
||||
import { CanvasAddon } from '../addons/xterm-addon-canvas/out/CanvasAddon';
|
||||
import { FitAddon } from '../addons/xterm-addon-fit/out/FitAddon';
|
||||
import { ImageAddon, IImageAddonOptions } from '../addons/xterm-addon-image/out/ImageAddon';
|
||||
import { SearchAddon, ISearchOptions } from '../addons/xterm-addon-search/out/SearchAddon';
|
||||
import { SerializeAddon } from '../addons/xterm-addon-serialize/out/SerializeAddon';
|
||||
import { WebLinksAddon } from '../addons/xterm-addon-web-links/out/WebLinksAddon';
|
||||
@@ -21,6 +20,14 @@ import { WebglAddon } from '../addons/xterm-addon-webgl/out/WebglAddon';
|
||||
import { Unicode11Addon } from '../addons/xterm-addon-unicode11/out/Unicode11Addon';
|
||||
import { LigaturesAddon } from '../addons/xterm-addon-ligatures/out/LigaturesAddon';
|
||||
|
||||
// Playwright/WebKit on Windows does not support WebAssembly https://stackoverflow.com/q/62311688/1156119
|
||||
import type { ImageAddonType, IImageAddonOptions } from '../addons/xterm-addon-image/out/ImageAddon';
|
||||
let ImageAddon: ImageAddonType | undefined; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
if ('WebAssembly' in window) {
|
||||
const imageAddon = require('../addons/xterm-addon-image/out/ImageAddon');
|
||||
ImageAddon = imageAddon.ImageAddon;
|
||||
}
|
||||
|
||||
// Use webpacked version (yarn package)
|
||||
// import { Terminal } from '../lib/xterm';
|
||||
// import { AttachAddon } from 'xterm-addon-attach';
|
||||
@@ -41,8 +48,9 @@ export interface IWindowWithTerminal extends Window {
|
||||
term: TerminalType;
|
||||
Terminal?: typeof TerminalType; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
AttachAddon?: typeof AttachAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
CanvasAddon?: typeof CanvasAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
FitAddon?: typeof FitAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
ImageAddon?: typeof ImageAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
ImageAddon?: typeof ImageAddonType; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
SearchAddon?: typeof SearchAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
SerializeAddon?: typeof SerializeAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
WebLinksAddon?: typeof WebLinksAddon; // eslint-disable-line @typescript-eslint/naming-convention
|
||||
@@ -68,7 +76,7 @@ interface IDemoAddon<T extends AddonType> {
|
||||
T extends 'attach' ? typeof AttachAddon :
|
||||
T extends 'canvas' ? typeof CanvasAddon :
|
||||
T extends 'fit' ? typeof FitAddon :
|
||||
T extends 'image' ? typeof ImageAddon :
|
||||
T extends 'image' ? typeof ImageAddonType :
|
||||
T extends 'search' ? typeof SearchAddon :
|
||||
T extends 'serialize' ? typeof SerializeAddon :
|
||||
T extends 'webLinks' ? typeof WebLinksAddon :
|
||||
@@ -80,7 +88,7 @@ interface IDemoAddon<T extends AddonType> {
|
||||
T extends 'attach' ? AttachAddon :
|
||||
T extends 'canvas' ? CanvasAddon :
|
||||
T extends 'fit' ? FitAddon :
|
||||
T extends 'image' ? ImageAddon :
|
||||
T extends 'image' ? ImageAddonType :
|
||||
T extends 'search' ? SearchAddon :
|
||||
T extends 'serialize' ? SerializeAddon :
|
||||
T extends 'webLinks' ? WebLinksAddon :
|
||||
@@ -117,6 +125,7 @@ const xtermjsTheme = {
|
||||
foreground: '#F8F8F8',
|
||||
background: '#2D2E2C',
|
||||
selectionBackground: '#5DA5D533',
|
||||
selectionInactiveBackground: '#555555AA',
|
||||
black: '#1E1E1D',
|
||||
brightBlack: '#262625',
|
||||
red: '#CE5C5C',
|
||||
@@ -211,6 +220,7 @@ const createNewWindowButtonHandler: () => void = () => {
|
||||
if (document.location.pathname === '/test') {
|
||||
window.Terminal = Terminal;
|
||||
window.AttachAddon = AttachAddon;
|
||||
window.CanvasAddon = CanvasAddon;
|
||||
window.FitAddon = FitAddon;
|
||||
window.ImageAddon = ImageAddon;
|
||||
window.SearchAddon = SearchAddon;
|
||||
@@ -240,6 +250,7 @@ if (document.location.pathname === '/test') {
|
||||
document.getElementById('bce').addEventListener('click', coloredErase);
|
||||
addVtButtons();
|
||||
initImageAddonExposed();
|
||||
testEvents();
|
||||
}
|
||||
|
||||
function createTerminal(): void {
|
||||
@@ -1328,3 +1339,8 @@ function initImageAddonExposed(): void {
|
||||
canvas?.toBlob(data => window.open(URL.createObjectURL(data), '_blank'));
|
||||
});
|
||||
}
|
||||
|
||||
function testEvents(): void {
|
||||
document.getElementById('event-focus').addEventListener('click', ()=> term.focus());
|
||||
document.getElementById('event-blur').addEventListener('click', ()=> term.blur());
|
||||
}
|
||||
|
||||
+5
-1
@@ -104,11 +104,15 @@
|
||||
|
||||
<dt>Weblinks Addon</dt>
|
||||
<dd><button id="weblinks-test" title="Various url conditions from demo data, hover&click to test">Test URLs</button></dd>
|
||||
|
||||
|
||||
<dt>Image Test</dt>
|
||||
<dd><button id="image-demo1">snake (sixel)</button></dd>
|
||||
<dd><button id="image-demo2">oranges (sixel)</button></dd>
|
||||
<dd><button id="image-demo3">palette (iip)</button></dd>
|
||||
|
||||
<dt>Events Test</dt>
|
||||
<dd><button id="event-focus">focus</button></dd>
|
||||
<dd><button id="event-blur">blur</button></dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
const startServer = require('./server.js');
|
||||
|
||||
startServer();
|
||||
+1
-58
@@ -5,69 +5,12 @@
|
||||
|
||||
// @ts-check
|
||||
|
||||
const path = require('path');
|
||||
const clientConfig = require('./webpack.config');
|
||||
const webpack = require('webpack');
|
||||
const startServer = require('./server.js');
|
||||
|
||||
startServer();
|
||||
|
||||
/**
|
||||
* This webpack config builds and watches the demo project. It works by taking the output from tsc
|
||||
* (via `yarn watch`) which is put into `out/` and then webpacks it into `demo/dist/`. The aliases
|
||||
* are used fix up the absolute paths output by tsc (because of `baseUrl` and `paths` in
|
||||
* `tsconfig.json`.
|
||||
*
|
||||
* For production builds see `webpack.config.js` in the root directory. If that is built the demo
|
||||
* can use that by switching out which `Terminal` is imported in `client.ts`, this is useful for
|
||||
* validating that the packaged version works correctly.
|
||||
*
|
||||
* @type {import('webpack').Configuration}
|
||||
*/
|
||||
const clientConfig = {
|
||||
entry: path.resolve(__dirname, 'client.ts'),
|
||||
devtool: 'inline-source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: ["source-map-loader"],
|
||||
enforce: "pre",
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
modules: [
|
||||
'node_modules',
|
||||
path.resolve(__dirname, '..'),
|
||||
path.resolve(__dirname, '../addons')
|
||||
],
|
||||
extensions: [ '.tsx', '.ts', '.js' ],
|
||||
alias: {
|
||||
common: path.resolve('./out/common'),
|
||||
browser: path.resolve('./out/browser')
|
||||
},
|
||||
fallback: {
|
||||
// The ligature modules contains fallbacks for node environments, we never want to browserify them
|
||||
stream: false,
|
||||
util: false,
|
||||
os: false,
|
||||
path: false,
|
||||
fs: false
|
||||
}
|
||||
},
|
||||
output: {
|
||||
filename: 'client-bundle.js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
mode: 'development',
|
||||
watch: true
|
||||
};
|
||||
const compiler = webpack(clientConfig);
|
||||
|
||||
compiler.watch({
|
||||
|
||||
+1
-1
@@ -7,6 +7,6 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="terminal-container"></div>
|
||||
<script src="dist/client-bundle.js" defer ></script>
|
||||
<script src="/dist/client-bundle.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"rootDir": ".",
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* This webpack config does a production build for xterm.js. It works by taking the output from tsc
|
||||
* (via `yarn watch` or `yarn prebuild`) which are put into `out/` and webpacks them into a
|
||||
* production mode umd library module in `lib/`. The aliases are used fix up the absolute paths
|
||||
* output by tsc (because of `baseUrl` and `paths` in `tsconfig.json`.
|
||||
*
|
||||
* @type {import('webpack').Configuration}
|
||||
*/
|
||||
const config = {
|
||||
entry: path.resolve(__dirname, 'client.ts'),
|
||||
devtool: 'inline-source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
use: ["source-map-loader"],
|
||||
enforce: "pre",
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
modules: [
|
||||
'node_modules',
|
||||
path.resolve(__dirname, '..'),
|
||||
path.resolve(__dirname, '../addons')
|
||||
],
|
||||
extensions: [ '.tsx', '.ts', '.js' ],
|
||||
alias: {
|
||||
common: path.resolve('./out/common'),
|
||||
browser: path.resolve('./out/browser')
|
||||
},
|
||||
fallback: {
|
||||
// The ligature modules contains fallbacks for node environments, we never want to browserify them
|
||||
stream: false,
|
||||
util: false,
|
||||
os: false,
|
||||
path: false,
|
||||
fs: false
|
||||
}
|
||||
},
|
||||
output: {
|
||||
filename: 'client-bundle.js',
|
||||
path: path.resolve(__dirname, 'dist')
|
||||
},
|
||||
mode: 'development'
|
||||
};
|
||||
module.exports = config;
|
||||
+10
-3
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "xterm",
|
||||
"description": "Full xterm terminal, in your browser",
|
||||
"version": "5.2.1",
|
||||
"version": "5.3.0",
|
||||
"main": "lib/xterm.js",
|
||||
"style": "css/xterm.css",
|
||||
"types": "typings/xterm.d.ts",
|
||||
@@ -28,7 +28,8 @@
|
||||
"package-headless": "webpack --config ./webpack.config.headless.js",
|
||||
"postpackage-headless": "node ./bin/package_headless.js",
|
||||
"start": "node demo/start",
|
||||
"start-debug": "node --inspect-brk demo/start",
|
||||
"start-server-only": "node demo/start-server-only",
|
||||
"build-demo": "webpack --config ./demo/webpack.config.js",
|
||||
"lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
|
||||
"lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
|
||||
"test": "npm run test-unit",
|
||||
@@ -37,6 +38,11 @@
|
||||
"test-api-chromium": "node ./bin/test_api.js --browser=chromium --timeout=20000",
|
||||
"test-api-firefox": "node ./bin/test_api.js --browser=firefox --timeout=20000",
|
||||
"test-api-webkit": "node ./bin/test_api.js --browser=webkit --timeout=20000",
|
||||
"test-playwright": "node ./bin/test_playwright.js --workers=75%",
|
||||
"test-playwright-chromium": "node ./bin/test_playwright.js --workers=75% \"--project=Chrome Stable\"",
|
||||
"test-playwright-firefox": "node ./bin/test_playwright.js --workers=75% \"--project=Firefox Stable\"",
|
||||
"test-playwright-webkit": "node ./bin/test_playwright.js --workers=75% \"--project=WebKit\"",
|
||||
"test-playwright-debug": "node ./bin/test_playwright.js --workers=1 --headed --timeout=30000",
|
||||
"test-unit": "node ./bin/test.js",
|
||||
"test-unit-coverage": "node ./bin/test.js --coverage",
|
||||
"test-unit-dev": "cross-env NODE_PATH='./out' mocha",
|
||||
@@ -53,6 +59,8 @@
|
||||
"vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lunapaint/png-codec": "^0.2.0",
|
||||
"@playwright/test": "^1.37.1",
|
||||
"@types/chai": "^4.2.22",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/deep-equal": "^1.0.1",
|
||||
@@ -80,7 +88,6 @@
|
||||
"mustache": "^4.2.0",
|
||||
"node-pty": "^0.10.1",
|
||||
"nyc": "^15.1.0",
|
||||
"playwright": "^1.22.1",
|
||||
"source-map-loader": "^3.0.0",
|
||||
"source-map-support": "^0.5.20",
|
||||
"ts-loader": "^9.3.1",
|
||||
|
||||
@@ -729,10 +729,8 @@ export class Terminal extends CoreTerminal implements ITerminal {
|
||||
|
||||
if (!(events & CoreMouseEventType.UP)) {
|
||||
this._document!.removeEventListener('mouseup', requestedEvents.mouseup!);
|
||||
el.removeEventListener('mouseup', requestedEvents.mouseup!);
|
||||
requestedEvents.mouseup = null;
|
||||
} else if (!requestedEvents.mouseup) {
|
||||
el.addEventListener('mouseup', eventListeners.mouseup);
|
||||
requestedEvents.mouseup = eventListeners.mouseup;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,10 +119,10 @@ export class Viewport extends Disposable implements IViewport {
|
||||
|
||||
private _innerRefresh(): void {
|
||||
if (this._charSizeService.height > 0) {
|
||||
this._currentRowHeight = this._renderService.dimensions.device.cell.height / this._coreBrowserService.dpr;
|
||||
this._currentDeviceCellHeight = this._renderService.dimensions.device.cell.height;
|
||||
this._currentRowHeight = this._renderDimensions.device.cell.height / this._coreBrowserService.dpr;
|
||||
this._currentDeviceCellHeight = this._renderDimensions.device.cell.height;
|
||||
this._lastRecordedViewportHeight = this._viewportElement.offsetHeight;
|
||||
const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._renderService.dimensions.css.canvas.height);
|
||||
const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._renderDimensions.css.canvas.height);
|
||||
if (this._lastRecordedBufferHeight !== newBufferHeight) {
|
||||
this._lastRecordedBufferHeight = newBufferHeight;
|
||||
this._scrollArea.style.height = this._lastRecordedBufferHeight + 'px';
|
||||
|
||||
@@ -205,8 +205,8 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
` animation: blink_block` + `_` + this._terminalClass + ` 1s step-end infinite;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_BLOCK_CLASS} {` +
|
||||
` background-color: ${colors.cursor.css};` +
|
||||
` color: ${colors.cursorAccent.css};` +
|
||||
` background-color: ${colors.cursor.css} !important;` +
|
||||
` color: ${colors.cursorAccent.css} !important;` +
|
||||
`}` +
|
||||
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${RowCss.CURSOR_CLASS}.${RowCss.CURSOR_STYLE_OUTLINE_CLASS} {` +
|
||||
` outline: 1px solid ${colors.cursor.css};` +
|
||||
@@ -299,10 +299,12 @@ export class DomRenderer extends Disposable implements IRenderer {
|
||||
|
||||
public handleBlur(): void {
|
||||
this._rowContainer.classList.remove(FOCUS_CLASS);
|
||||
this.renderRows(0, this._bufferService.rows - 1);
|
||||
}
|
||||
|
||||
public handleFocus(): void {
|
||||
this._rowContainer.classList.add(FOCUS_CLASS);
|
||||
this.renderRows(this._bufferService.buffer.y, this._bufferService.buffer.y);
|
||||
}
|
||||
|
||||
public handleSelectionChanged(start: [number, number] | undefined, end: [number, number] | undefined, columnSelectMode: boolean): void {
|
||||
|
||||
@@ -180,7 +180,11 @@ export class DomRendererRowFactory {
|
||||
&& !isDecorated
|
||||
) {
|
||||
// no span alterations, thus only account chars skipping all code below
|
||||
text += chars;
|
||||
if (cell.isInvisible()) {
|
||||
text += WHITESPACE_CELL_CHAR;
|
||||
} else {
|
||||
text += chars;
|
||||
}
|
||||
cellAmount++;
|
||||
continue;
|
||||
} else {
|
||||
@@ -415,7 +419,7 @@ export class DomRendererRowFactory {
|
||||
break;
|
||||
case Attributes.CM_DEFAULT:
|
||||
default:
|
||||
if (!this._applyMinimumContrast(charElement, resolvedBg, colors.foreground, cell, bgOverride, undefined)) {
|
||||
if (!this._applyMinimumContrast(charElement, resolvedBg, colors.foreground, cell, bgOverride, fgOverride)) {
|
||||
if (isInverse) {
|
||||
classes.push(`xterm-fg-${INVERTED_DEFAULT_COLOR}`);
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ export class TextureAtlas implements ITextureAtlas {
|
||||
}
|
||||
|
||||
private _getForegroundColor(bg: number, bgColorMode: number, bgColor: number, fg: number, fgColorMode: number, fgColor: number, inverse: boolean, dim: boolean, bold: boolean, excludeFromContrastRatioDemands: boolean): IColor {
|
||||
const minimumContrastColor = this._getMinimumContrastColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, false, bold, dim, excludeFromContrastRatioDemands);
|
||||
const minimumContrastColor = this._getMinimumContrastColor(bg, bgColorMode, bgColor, fg, fgColorMode, fgColor, inverse, bold, dim, excludeFromContrastRatioDemands);
|
||||
if (minimumContrastColor) {
|
||||
return minimumContrastColor;
|
||||
}
|
||||
|
||||
@@ -203,11 +203,14 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
|
||||
public setRenderer(renderer: IRenderer): void {
|
||||
this._renderer.value = renderer;
|
||||
this._renderer.value.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
|
||||
// If the value was not set, the terminal is being disposed so ignore it
|
||||
if (this._renderer.value) {
|
||||
this._renderer.value.onRequestRedraw(e => this.refreshRows(e.start, e.end, true));
|
||||
|
||||
// Force a refresh
|
||||
this._needsSelectionRefresh = true;
|
||||
this._fullRefresh();
|
||||
// Force a refresh
|
||||
this._needsSelectionRefresh = true;
|
||||
this._fullRefresh();
|
||||
}
|
||||
}
|
||||
|
||||
public addRefreshCallback(callback: FrameRequestCallback): number {
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015",
|
||||
"es2016.Array.Include"
|
||||
"es2021"
|
||||
],
|
||||
"outDir": "../../out",
|
||||
"types": [
|
||||
|
||||
@@ -57,10 +57,14 @@ export class EventEmitter<T, U = void> implements IEventEmitter<T, U> {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.clearListeners();
|
||||
this._disposed = true;
|
||||
}
|
||||
|
||||
public clearListeners(): void {
|
||||
if (this._listeners) {
|
||||
this._listeners.length = 0;
|
||||
}
|
||||
this._disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"rootDir": ".",
|
||||
|
||||
"sourceMap": true,
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { pollFor, openTerminal, launchBrowser } from './TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
let browser: Browser;
|
||||
let page: Page;
|
||||
const width = 800;
|
||||
const height = 600;
|
||||
|
||||
describe('CharWidth Integration Tests', function(): void {
|
||||
before(async function(): Promise<any> {
|
||||
browser = await launchBrowser();
|
||||
page = await (await browser.newContext()).newPage();
|
||||
await page.setViewportSize({ width, height });
|
||||
await page.goto(APP);
|
||||
await openTerminal(page, { rows: 5, cols: 30 });
|
||||
});
|
||||
|
||||
after(() => {
|
||||
browser.close();
|
||||
});
|
||||
|
||||
describe('getStringCellWidth', () => {
|
||||
beforeEach(async () => {
|
||||
await page.evaluate(`window.term.reset()`);
|
||||
});
|
||||
|
||||
it('ASCII chars', async function(): Promise<void> {
|
||||
const input = 'This is just ASCII text.#';
|
||||
await page.evaluate(`window.term.write('${input}')`);
|
||||
await pollFor(page, () => sumWidths(0, 1, '#'), 25);
|
||||
});
|
||||
|
||||
it('combining chars', async function(): Promise<void> {
|
||||
const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301#';
|
||||
await page.evaluate(`window.term.write('${input}')`);
|
||||
await pollFor(page, () => sumWidths(0, 1, '#'), 10);
|
||||
});
|
||||
|
||||
it('surrogate chars', async function(): Promise<void> {
|
||||
const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞#';
|
||||
await page.evaluate(`window.term.write('${input}')`);
|
||||
await pollFor(page, () => sumWidths(0, 1, '#'), 28);
|
||||
});
|
||||
|
||||
it('surrogate combining chars', async function(): Promise<void> {
|
||||
const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301#';
|
||||
await page.evaluate(`window.term.write('${input}')`);
|
||||
await pollFor(page, () => sumWidths(0, 1, '#'), 12);
|
||||
});
|
||||
|
||||
it('fullwidth chars', async function(): Promise<void> {
|
||||
const input = '1234567890#';
|
||||
await page.evaluate(`window.term.write('${input}')`);
|
||||
await pollFor(page, () => sumWidths(0, 1, '#'), 21);
|
||||
});
|
||||
|
||||
it('fullwidth chars offset 1', async function(): Promise<void> {
|
||||
const input = 'a1234567890#';
|
||||
await page.evaluate(`window.term.write('${input}')`);
|
||||
await pollFor(page, () => sumWidths(0, 1, '#'), 22);
|
||||
});
|
||||
|
||||
// TODO: multiline tests once #1685 is resolved
|
||||
});
|
||||
});
|
||||
|
||||
async function sumWidths(start: number, end: number, sentinel: string): Promise<number> {
|
||||
await page.evaluate(`
|
||||
(function() {
|
||||
window.result = 0;
|
||||
const buffer = window.term.buffer.active;
|
||||
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.getWidth();
|
||||
if (cell.getChars() === '${sentinel}') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
`);
|
||||
return await page.evaluate(`window.result`);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,213 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { assert } from 'chai';
|
||||
import { writeSync, openTerminal, launchBrowser } from './TestUtils';
|
||||
import { Browser, Page } from 'playwright';
|
||||
|
||||
const APP = 'http://127.0.0.1:3001/test';
|
||||
|
||||
let browser: Browser;
|
||||
let page: Page;
|
||||
const width = 800;
|
||||
const height = 600;
|
||||
|
||||
describe('Parser Integration Tests', function (): void {
|
||||
before(async function (): Promise<any> {
|
||||
browser = await launchBrowser();
|
||||
page = await (await browser.newContext()).newPage();
|
||||
await page.setViewportSize({ width, height });
|
||||
await page.goto(APP);
|
||||
await openTerminal(page);
|
||||
});
|
||||
|
||||
after(async () => browser.close());
|
||||
|
||||
describe('registerCsiHandler', () => {
|
||||
it('should call custom CSI handler with js array params', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window._customCsiHandlerParams = [];
|
||||
const _customCsiHandler = window.term.parser.registerCsiHandler({final: 'm'}, (params, collect) => {
|
||||
window._customCsiHandlerParams.push(params);
|
||||
return false;
|
||||
}, '');
|
||||
`);
|
||||
await writeSync(page, '\x1b[38;5;123mparams\x1b[38:2::50:100:150msubparams');
|
||||
assert.deepEqual(await page.evaluate(`window._customCsiHandlerParams`), [
|
||||
[38, 5, 123],
|
||||
[38, [2, -1, 50, 100, 150]]
|
||||
]);
|
||||
});
|
||||
it('async', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window._customCsiHandlerCallStack = [];
|
||||
const _customCsiHandlerA = window.term.parser.registerCsiHandler({intermediates:'+', final: 'Z'}, params => {
|
||||
window._customCsiHandlerCallStack.push(['A', params]);
|
||||
return false;
|
||||
});
|
||||
const _customCsiHandlerB = window.term.parser.registerCsiHandler({intermediates:'+', final: 'Z'}, async params => {
|
||||
await new Promise(res => setTimeout(res, 50));
|
||||
window._customCsiHandlerCallStack.push(['B', params]);
|
||||
return false;
|
||||
});
|
||||
const _customCsiHandlerC = window.term.parser.registerCsiHandler({intermediates:'+', final: 'Z'}, params => {
|
||||
window._customCsiHandlerCallStack.push(['C', params]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1b[1;2+Z');
|
||||
assert.deepEqual(await page.evaluate(`window._customCsiHandlerCallStack`), [
|
||||
['C', [1, 2]],
|
||||
['B', [1, 2]],
|
||||
['A', [1, 2]]
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('registerDcsHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window._customDcsHandlerCallStack = [];
|
||||
const _customDcsHandlerA = window.term.parser.registerDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
window._customDcsHandlerCallStack.push(['A', params, data]);
|
||||
return false;
|
||||
});
|
||||
const _customDcsHandlerB = window.term.parser.registerDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
window._customDcsHandlerCallStack.push(['B', params, data]);
|
||||
return true;
|
||||
});
|
||||
const _customDcsHandlerC = window.term.parser.registerDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
window._customDcsHandlerCallStack.push(['C', params, data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1bP1;2+psome data\x1b\\\\');
|
||||
assert.deepEqual(await page.evaluate(`window._customDcsHandlerCallStack`), [
|
||||
['C', [1, 2], 'some data'],
|
||||
['B', [1, 2], 'some data']
|
||||
]);
|
||||
});
|
||||
it('async', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window._customDcsHandlerCallStack = [];
|
||||
const _customDcsHandlerA = window.term.parser.registerDcsHandler({intermediates:'+', final: 'q'}, (data, params) => {
|
||||
window._customDcsHandlerCallStack.push(['A', params, data]);
|
||||
return false;
|
||||
});
|
||||
const _customDcsHandlerB = window.term.parser.registerDcsHandler({intermediates:'+', final: 'q'}, async (data, params) => {
|
||||
await new Promise(res => setTimeout(res, 50));
|
||||
window._customDcsHandlerCallStack.push(['B', params, data]);
|
||||
return false;
|
||||
});
|
||||
const _customDcsHandlerC = window.term.parser.registerDcsHandler({intermediates:'+', final: 'q'}, (data, params) => {
|
||||
window._customDcsHandlerCallStack.push(['C', params, data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1bP1;2+qsome data\x1b\\\\');
|
||||
assert.deepEqual(await page.evaluate(`window._customDcsHandlerCallStack`), [
|
||||
['C', [1, 2], 'some data'],
|
||||
['B', [1, 2], 'some data'],
|
||||
['A', [1, 2], 'some data']
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('registerEscHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window._customEscHandlerCallStack = [];
|
||||
const _customEscHandlerA = window.term.parser.registerEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
window._customEscHandlerCallStack.push('A');
|
||||
return false;
|
||||
});
|
||||
const _customEscHandlerB = window.term.parser.registerEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
window._customEscHandlerCallStack.push('B');
|
||||
return true;
|
||||
});
|
||||
const _customEscHandlerC = window.term.parser.registerEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
window._customEscHandlerCallStack.push('C');
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1b(B');
|
||||
assert.deepEqual(await page.evaluate(`window._customEscHandlerCallStack`), ['C', 'B']);
|
||||
});
|
||||
it('async', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window._customEscHandlerCallStack = [];
|
||||
const _customEscHandlerA = window.term.parser.registerEscHandler({intermediates:'(', final: 'Z'}, () => {
|
||||
window._customEscHandlerCallStack.push('A');
|
||||
return false;
|
||||
});
|
||||
const _customEscHandlerB = window.term.parser.registerEscHandler({intermediates:'(', final: 'Z'}, async () => {
|
||||
await new Promise(res => setTimeout(res, 50));
|
||||
window._customEscHandlerCallStack.push('B');
|
||||
return false;
|
||||
});
|
||||
const _customEscHandlerC = window.term.parser.registerEscHandler({intermediates:'(', final: 'Z'}, () => {
|
||||
window._customEscHandlerCallStack.push('C');
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1b(Z');
|
||||
assert.deepEqual(await page.evaluate(`window._customEscHandlerCallStack`), ['C', 'B', 'A']);
|
||||
});
|
||||
});
|
||||
describe('registerOscHandler', () => {
|
||||
it('should respects return value', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window._customOscHandlerCallStack = [];
|
||||
const _customOscHandlerA = window.term.parser.registerOscHandler(1234, data => {
|
||||
window._customOscHandlerCallStack.push(['A', data]);
|
||||
return false;
|
||||
});
|
||||
const _customOscHandlerB = window.term.parser.registerOscHandler(1234, data => {
|
||||
window._customOscHandlerCallStack.push(['B', data]);
|
||||
return true;
|
||||
});
|
||||
const _customOscHandlerC = window.term.parser.registerOscHandler(1234, data => {
|
||||
window._customOscHandlerCallStack.push(['C', data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1b]1234;some data\x07');
|
||||
assert.deepEqual(await page.evaluate(`window._customOscHandlerCallStack`), [
|
||||
['C', 'some data'],
|
||||
['B', 'some data']
|
||||
]);
|
||||
});
|
||||
it('async', async () => {
|
||||
await page.evaluate(`
|
||||
window.term.reset();
|
||||
window._customOscHandlerCallStack = [];
|
||||
const _customOscHandlerA = window.term.parser.registerOscHandler(666, data => {
|
||||
window._customOscHandlerCallStack.push(['A', data]);
|
||||
return false;
|
||||
});
|
||||
const _customOscHandlerB = window.term.parser.registerOscHandler(666, async data => {
|
||||
await new Promise(res => setTimeout(res, 50));
|
||||
window._customOscHandlerCallStack.push(['B', data]);
|
||||
return false;
|
||||
});
|
||||
const _customOscHandlerC = window.term.parser.registerOscHandler(666, data => {
|
||||
window._customOscHandlerCallStack.push(['C', data]);
|
||||
return false;
|
||||
});
|
||||
`);
|
||||
await writeSync(page, '\x1b]666;some data\x07');
|
||||
assert.deepEqual(await page.evaluate(`window._customOscHandlerCallStack`), [
|
||||
['C', 'some data'],
|
||||
['B', 'some data'],
|
||||
['A', 'some data']
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
This project contains helpers for the legacy playwright tests. Currently addons use this as a dependency, when all addons have moved over to use `@playright/test` this can be removed.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,10 +3,10 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import * as playwright from 'playwright';
|
||||
import * as playwright from '@playwright/test';
|
||||
import deepEqual = require('deep-equal');
|
||||
import { ITerminalInitOnlyOptions, ITerminalOptions } from 'xterm';
|
||||
import { deepStrictEqual, fail } from 'assert';
|
||||
import { deepStrictEqual } from 'assert';
|
||||
|
||||
export async function pollFor<T>(page: playwright.Page, evalOrFn: string | (() => Promise<T>), val: T, preFn?: () => Promise<void>, maxDuration?: number): Promise<void> {
|
||||
if (preFn) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"es6"
|
||||
"es2021"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../../out-test/api",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"es6",
|
||||
"es2021",
|
||||
],
|
||||
"outDir": "../../out-test/benchmark",
|
||||
"types": [
|
||||
@@ -10,7 +10,7 @@
|
||||
],
|
||||
"moduleResolution": "node",
|
||||
"strict": false,
|
||||
"target": "es2015",
|
||||
"target": "es2021",
|
||||
"module": "commonjs",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/**
|
||||
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { test } from '@playwright/test';
|
||||
import { createTestContext, ITestContext, openTerminal, pollFor } from './TestUtils';
|
||||
|
||||
let ctx: ITestContext;
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
ctx = await createTestContext(browser);
|
||||
await openTerminal(ctx);
|
||||
});
|
||||
test.afterAll(async () => await ctx.page.close());
|
||||
|
||||
test.beforeEach(async () => await ctx.proxy.reset());
|
||||
|
||||
test.describe('CharWidth Integration Tests', () => {
|
||||
test.describe('getStringCellWidth', () => {
|
||||
test('ASCII chars', async () => {
|
||||
await ctx.proxy.write('This is just ASCII text.#');
|
||||
await pollFor(ctx.page, () => sumWidths(0, 1, '#'), 25);
|
||||
});
|
||||
|
||||
test('combining chars', async () => {
|
||||
await ctx.proxy.write('e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301#');
|
||||
await pollFor(ctx.page, () => sumWidths(0, 1, '#'), 10);
|
||||
});
|
||||
|
||||
test('surrogate chars', async () => {
|
||||
await ctx.proxy.write('𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞#');
|
||||
await pollFor(ctx.page, () => sumWidths(0, 1, '#'), 28);
|
||||
});
|
||||
|
||||
test('surrogate combining chars', async () => {
|
||||
await ctx.proxy.write('𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301#');
|
||||
await pollFor(ctx.page, () => sumWidths(0, 1, '#'), 12);
|
||||
});
|
||||
|
||||
test('fullwidth chars', async () => {
|
||||
await ctx.proxy.write('1234567890#');
|
||||
await pollFor(ctx.page, () => sumWidths(0, 1, '#'), 21);
|
||||
});
|
||||
|
||||
test('fullwidth chars offset 1', async () => {
|
||||
await ctx.proxy.write('a1234567890#');
|
||||
await pollFor(ctx.page, () => sumWidths(0, 1, '#'), 22);
|
||||
});
|
||||
|
||||
// TODO: multiline tests once #1685 is resolved
|
||||
});
|
||||
});
|
||||
|
||||
async function sumWidths(start: number, end: number, sentinel: string): Promise<number> {
|
||||
await ctx.page.evaluate(`
|
||||
(function() {
|
||||
window.result = 0;
|
||||
const buffer = window.term.buffer.active;
|
||||
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.getWidth();
|
||||
if (cell.getChars() === '${sentinel}') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
`);
|
||||
return await ctx.page.evaluate(`window.result`);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,244 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
import { test } from '@playwright/test';
|
||||
import { deepStrictEqual, ok, strictEqual } from 'assert';
|
||||
import type { IDisposable } from 'xterm';
|
||||
import { createTestContext, ITestContext, openTerminal, pollFor } from './TestUtils';
|
||||
|
||||
let ctx: ITestContext;
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
ctx = await createTestContext(browser);
|
||||
await openTerminal(ctx);
|
||||
});
|
||||
test.afterAll(async () => await ctx.page.close());
|
||||
|
||||
declare global {
|
||||
interface Window { // eslint-disable-line @typescript-eslint/naming-convention
|
||||
customCsiHandlerParams?: (number | number[])[][];
|
||||
customCsiHandlerCallStack?: string[];
|
||||
customDcsHandlerCallStack?: [string, (number | number[])[], string][];
|
||||
customEscHandlerCallStack?: string[];
|
||||
customOscHandlerCallStack?: string[][];
|
||||
disposable?: IDisposable;
|
||||
disposables?: IDisposable[];
|
||||
}
|
||||
}
|
||||
|
||||
test.describe('Parser Integration Tests', () => {
|
||||
test.beforeEach(async () => await ctx.proxy.reset());
|
||||
test.afterEach(async () => {
|
||||
await ctx.page.evaluate(() => {
|
||||
window.disposable?.dispose();
|
||||
window.disposable = undefined;
|
||||
window.disposables?.forEach(e => e.dispose());
|
||||
window.disposables = undefined;
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('registerCsiHandler', () => {
|
||||
test('should call custom CSI handler with js array params', async () => {
|
||||
await ctx.proxy.evaluate(([term]) => {
|
||||
window.customCsiHandlerParams = [];
|
||||
window.disposable = term.parser.registerCsiHandler({final: 'm'}, (params) => {
|
||||
window.customCsiHandlerParams!.push(params);
|
||||
return false;
|
||||
});
|
||||
});
|
||||
await ctx.proxy.write('\x1b[38;5;123mparams\x1b[38:2::50:100:150msubparams');
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customCsiHandlerParams), [
|
||||
[38, 5, 123],
|
||||
[38, [2, -1, 50, 100, 150]]
|
||||
]);
|
||||
});
|
||||
test('async', async () => {
|
||||
await ctx.proxy.evaluate(([term]) => {
|
||||
window.customCsiHandlerCallStack = [];
|
||||
window.customCsiHandlerParams = [];
|
||||
window.disposables = [
|
||||
term.parser.registerCsiHandler({intermediates:'+', final: 'Z'}, params => {
|
||||
window.customCsiHandlerCallStack!.push('A');
|
||||
window.customCsiHandlerParams!.push(params);
|
||||
return false;
|
||||
}),
|
||||
term.parser.registerCsiHandler({intermediates:'+', final: 'Z'}, params => {
|
||||
return new Promise(res => setTimeout(res, 50)).then(() => {
|
||||
window.customCsiHandlerCallStack!.push('B');
|
||||
window.customCsiHandlerParams!.push(params);
|
||||
return false;
|
||||
});
|
||||
}),
|
||||
term.parser.registerCsiHandler({intermediates:'+', final: 'Z'}, params => {
|
||||
window.customCsiHandlerCallStack!.push('C');
|
||||
window.customCsiHandlerParams!.push(params);
|
||||
return false;
|
||||
})
|
||||
];
|
||||
});
|
||||
await ctx.proxy.write('\x1b[1;2+Z');
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customCsiHandlerCallStack), [
|
||||
'C',
|
||||
'B',
|
||||
'A'
|
||||
]);
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customCsiHandlerParams), [
|
||||
[1, 2],
|
||||
[1, 2],
|
||||
[1, 2]
|
||||
]);
|
||||
});
|
||||
});
|
||||
test.describe('registerDcsHandler', () => {
|
||||
test('should respects return value', async () => {
|
||||
await ctx.proxy.evaluate(([term]) => {
|
||||
window.customDcsHandlerCallStack = [];
|
||||
window.disposables = [
|
||||
term.parser.registerDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
window.customDcsHandlerCallStack!.push(['A', params, data]);
|
||||
return false;
|
||||
}),
|
||||
term.parser.registerDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
window.customDcsHandlerCallStack!.push(['B', params, data]);
|
||||
return true;
|
||||
}),
|
||||
term.parser.registerDcsHandler({intermediates:'+', final: 'p'}, (data, params) => {
|
||||
window.customDcsHandlerCallStack!.push(['C', params, data]);
|
||||
return false;
|
||||
})
|
||||
];
|
||||
});
|
||||
await ctx.proxy.write('\x1bP1;2+psome data\x1b\\');
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customDcsHandlerCallStack), [
|
||||
['C', [1, 2], 'some data'],
|
||||
['B', [1, 2], 'some data']
|
||||
]);
|
||||
});
|
||||
test('async', async () => {
|
||||
await ctx.proxy.evaluate(([term]) => {
|
||||
window.customDcsHandlerCallStack = [];
|
||||
window.disposables = [
|
||||
term.parser.registerDcsHandler({intermediates:'+', final: 'q'}, (data, params) => {
|
||||
window.customDcsHandlerCallStack!.push(['A', params, data]);
|
||||
return false;
|
||||
}),
|
||||
term.parser.registerDcsHandler({intermediates:'+', final: 'q'}, (data, params) => {
|
||||
return new Promise(res => setTimeout(res, 50)).then(() => {
|
||||
window.customDcsHandlerCallStack!.push(['B', params, data]);
|
||||
return false;
|
||||
});
|
||||
}),
|
||||
term.parser.registerDcsHandler({intermediates:'+', final: 'q'}, (data, params) => {
|
||||
window.customDcsHandlerCallStack!.push(['C', params, data]);
|
||||
return false;
|
||||
})
|
||||
];
|
||||
});
|
||||
await ctx.proxy.write('\x1bP1;2+qsome data\x1b\\');
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customDcsHandlerCallStack), [
|
||||
['C', [1, 2], 'some data'],
|
||||
['B', [1, 2], 'some data'],
|
||||
['A', [1, 2], 'some data']
|
||||
]);
|
||||
});
|
||||
});
|
||||
test.describe('registerEscHandler', () => {
|
||||
test('should respects return value', async () => {
|
||||
await ctx.proxy.evaluate(([term]) => {
|
||||
window.customEscHandlerCallStack = [];
|
||||
window.disposables = [
|
||||
term.parser.registerEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
window.customEscHandlerCallStack!.push('A');
|
||||
return false;
|
||||
}),
|
||||
term.parser.registerEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
window.customEscHandlerCallStack!.push('B');
|
||||
return true;
|
||||
}),
|
||||
term.parser.registerEscHandler({intermediates:'(', final: 'B'}, () => {
|
||||
window.customEscHandlerCallStack!.push('C');
|
||||
return false;
|
||||
})
|
||||
];
|
||||
});
|
||||
await ctx.proxy.write('\x1b(B');
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customEscHandlerCallStack), ['C', 'B']);
|
||||
});
|
||||
test('async', async () => {
|
||||
await ctx.proxy.evaluate(([term]) => {
|
||||
window.customEscHandlerCallStack = [];
|
||||
window.disposables = [
|
||||
term.parser.registerEscHandler({intermediates:'(', final: 'Z'}, () => {
|
||||
window.customEscHandlerCallStack!.push('A');
|
||||
return false;
|
||||
}),
|
||||
term.parser.registerEscHandler({intermediates:'(', final: 'Z'}, () => {
|
||||
return new Promise(res => setTimeout(res, 50)).then(() => {
|
||||
window.customEscHandlerCallStack!.push('B');
|
||||
return false;
|
||||
});
|
||||
}),
|
||||
term.parser.registerEscHandler({intermediates:'(', final: 'Z'}, () => {
|
||||
window.customEscHandlerCallStack!.push('C');
|
||||
return false;
|
||||
})
|
||||
];
|
||||
});
|
||||
await ctx.proxy.write('\x1b(Z');
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customEscHandlerCallStack), ['C', 'B', 'A']);
|
||||
});
|
||||
});
|
||||
test.describe('registerOscHandler', () => {
|
||||
test('should respects return value', async () => {
|
||||
await ctx.proxy.evaluate(([term]) => {
|
||||
window.customOscHandlerCallStack = [];
|
||||
window.disposables = [
|
||||
term.parser.registerOscHandler(1234, data => {
|
||||
window.customOscHandlerCallStack!.push(['A', data]);
|
||||
return false;
|
||||
}),
|
||||
term.parser.registerOscHandler(1234, data => {
|
||||
window.customOscHandlerCallStack!.push(['B', data]);
|
||||
return true;
|
||||
}),
|
||||
term.parser.registerOscHandler(1234, data => {
|
||||
window.customOscHandlerCallStack!.push(['C', data]);
|
||||
return false;
|
||||
})
|
||||
];
|
||||
});
|
||||
await ctx.proxy.write('\x1b]1234;some data\x07');
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customOscHandlerCallStack), [
|
||||
['C', 'some data'],
|
||||
['B', 'some data']
|
||||
]);
|
||||
});
|
||||
test('async', async () => {
|
||||
await ctx.proxy.evaluate(([term]) => {
|
||||
window.customOscHandlerCallStack = [];
|
||||
window.disposables = [
|
||||
term.parser.registerOscHandler(666, data => {
|
||||
window.customOscHandlerCallStack!.push(['A', data]);
|
||||
return false;
|
||||
}),
|
||||
term.parser.registerOscHandler(666, data => {
|
||||
return new Promise(res => setTimeout(res, 50)).then(() => {
|
||||
window.customOscHandlerCallStack!.push(['B', data]);
|
||||
return false;
|
||||
});
|
||||
}),
|
||||
term.parser.registerOscHandler(666, data => {
|
||||
window.customOscHandlerCallStack!.push(['C', data]);
|
||||
return false;
|
||||
})
|
||||
];
|
||||
});
|
||||
await ctx.proxy.write('\x1b]666;some data\x07');
|
||||
deepStrictEqual(await ctx.page.evaluate(() => window.customOscHandlerCallStack), [
|
||||
['C', 'some data'],
|
||||
['B', 'some data'],
|
||||
['A', 'some data']
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { test } from '@playwright/test';
|
||||
import { ITestContext, createTestContext, openTerminal } from './TestUtils';
|
||||
import { ISharedRendererTestContext, injectSharedRendererTests } from './SharedRendererTests';
|
||||
|
||||
let ctx: ITestContext;
|
||||
const ctxWrapper: ISharedRendererTestContext = { value: undefined } as any;
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
ctx = await createTestContext(browser);
|
||||
ctxWrapper.value = ctx;
|
||||
await openTerminal(ctx);
|
||||
});
|
||||
test.afterAll(async () => await ctx.page.close());
|
||||
|
||||
test.describe('DOM Renderer Integration Tests', () => {
|
||||
injectSharedRendererTests(ctxWrapper);
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,496 @@
|
||||
/**
|
||||
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { Browser, JSHandle, Page } from '@playwright/test';
|
||||
import { deepStrictEqual, strictEqual } from 'assert';
|
||||
import type { IRenderDimensions } from 'browser/renderer/shared/Types';
|
||||
import type { IRenderService } from 'browser/services/Services';
|
||||
import type { ICoreTerminal, IMarker } from 'common/Types';
|
||||
import * as playwright from '@playwright/test';
|
||||
import { PageFunction } from 'playwright-core/types/structs';
|
||||
import { IBuffer, IBufferCell, IBufferLine, IBufferNamespace, IBufferRange, IDecoration, IDecorationOptions, IModes, ITerminalInitOnlyOptions, ITerminalOptions, Terminal } from 'xterm';
|
||||
import { EventEmitter } from '../../out/common/EventEmitter';
|
||||
|
||||
export interface ITestContext {
|
||||
browser: Browser;
|
||||
page: Page;
|
||||
termHandle: JSHandle<Terminal>;
|
||||
proxy: TerminalProxy;
|
||||
}
|
||||
|
||||
export async function createTestContext(browser: Browser): Promise<ITestContext> {
|
||||
const page = await browser.newPage();
|
||||
page.on('console', e => console.log(`[${browser.browserType().name()}:${e.type()}]`, e));
|
||||
page.on('pageerror', e => console.error(`[${browser.browserType().name()}]`, e));
|
||||
await page.goto('/test');
|
||||
const proxy = new TerminalProxy(page);
|
||||
proxy.initPage();
|
||||
return {
|
||||
browser,
|
||||
page,
|
||||
termHandle: await page.evaluateHandle('window.term'),
|
||||
proxy
|
||||
};
|
||||
}
|
||||
|
||||
type EnsureAsync<T> = T extends PromiseLike<any> ? T : Promise<T>;
|
||||
type EnsureAsyncProperties<T> = {
|
||||
[Key in keyof T]: EnsureAsync<T[Key]>
|
||||
};
|
||||
type EnsureAsyncMethods<T> = {
|
||||
[K in keyof T]: T[K] extends (...args: infer Args) => infer Return
|
||||
? (...args: Args) => Promise<Return>
|
||||
: T[K];
|
||||
};
|
||||
|
||||
/**
|
||||
* A type that proxy objects implement that enables overriding a base interface with a set of types
|
||||
* to override as async (ie. properties that are not synced to the node side), and a set of types to
|
||||
* omit from the interface (ie. properties that map to completely different implementations).
|
||||
*/
|
||||
type PlaywrightApiProxy<TBaseInterface, TAsyncPropOverrides extends keyof TBaseInterface, TAsyncMethodOverrides extends keyof TBaseInterface, TCustomOverrides extends keyof TBaseInterface> = (
|
||||
// Interfaces that the proxy implements as async
|
||||
EnsureAsyncProperties<Pick<TBaseInterface, TAsyncPropOverrides>> &
|
||||
EnsureAsyncMethods<Pick<TBaseInterface, TAsyncMethodOverrides>> &
|
||||
// Interfaces that the proxy implements as is (exclude async/custom)
|
||||
Omit<TBaseInterface, TAsyncPropOverrides | TAsyncMethodOverrides | TCustomOverrides>
|
||||
);
|
||||
|
||||
interface ITerminalProxyCustomMethods {
|
||||
evaluate<T>(pageFunction: PageFunction<JSHandle<Terminal>[], T>): Promise<T>;
|
||||
write(data: string | Uint8Array): Promise<void>;
|
||||
}
|
||||
|
||||
type TerminalProxyAsyncPropOverrides = 'cols' | 'rows' | 'modes';
|
||||
type TerminalProxyAsyncMethodOverrides = 'hasSelection' | 'getSelection' | 'getSelectionPosition' | 'registerMarker' | 'registerDecoration';
|
||||
type TerminalProxyCustomOverrides = 'buffer' | (
|
||||
// The below are not implemented yet
|
||||
'element' |
|
||||
'textarea' |
|
||||
'markers' |
|
||||
'unicode' |
|
||||
'parser' |
|
||||
'options' |
|
||||
'open' |
|
||||
'attachCustomKeyEventHandler' |
|
||||
'registerLinkProvider' |
|
||||
'registerCharacterJoiner' |
|
||||
'deregisterCharacterJoiner' |
|
||||
'loadAddon'
|
||||
);
|
||||
export class TerminalProxy implements ITerminalProxyCustomMethods, PlaywrightApiProxy<Terminal, TerminalProxyAsyncPropOverrides, TerminalProxyAsyncMethodOverrides, TerminalProxyCustomOverrides> {
|
||||
constructor(private readonly _page: Page) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the proxy for a new playwright page.
|
||||
*/
|
||||
public async initPage(): Promise<void> {
|
||||
await this._page.exposeFunction('onBell', () => this._onBell.fire());
|
||||
await this._page.exposeFunction('onBinary', (e: string) => this._onBinary.fire(e));
|
||||
await this._page.exposeFunction('onCursorMove', () => this._onCursorMove.fire());
|
||||
await this._page.exposeFunction('onData', (e: string) => this._onData.fire(e));
|
||||
await this._page.exposeFunction('onKey', (e: { key: string, domEvent: KeyboardEvent }) => this._onKey.fire(e));
|
||||
await this._page.exposeFunction('onLineFeed', () => this._onLineFeed.fire());
|
||||
await this._page.exposeFunction('onRender', (e: { start: number, end: number }) => this._onRender.fire(e));
|
||||
await this._page.exposeFunction('onResize', (e: { cols: number, rows: number }) => this._onResize.fire(e));
|
||||
await this._page.exposeFunction('onScroll', (e: number) => this._onScroll.fire(e));
|
||||
await this._page.exposeFunction('onSelectionChange', () => this._onSelectionChange.fire());
|
||||
await this._page.exposeFunction('onTitleChange', (e: string) => this._onTitleChange.fire(e));
|
||||
await this._page.exposeFunction('onWriteParsed', () => this._onWriteParsed.fire());
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the proxy for a new terminal object.
|
||||
*/
|
||||
public async initTerm(): Promise<void> {
|
||||
for (const emitter of [
|
||||
this._onBell,
|
||||
this._onBinary,
|
||||
this._onCursorMove,
|
||||
this._onData,
|
||||
this._onKey,
|
||||
this._onLineFeed,
|
||||
this._onRender,
|
||||
this._onResize,
|
||||
this._onScroll,
|
||||
this._onSelectionChange,
|
||||
this._onTitleChange,
|
||||
this._onWriteParsed
|
||||
]) {
|
||||
emitter.clearListeners();
|
||||
}
|
||||
await this.evaluate(([term]) => term.onBell((window as any).onBell));
|
||||
await this.evaluate(([term]) => term.onBinary((window as any).onBinary));
|
||||
await this.evaluate(([term]) => term.onCursorMove((window as any).onCursorMove));
|
||||
await this.evaluate(([term]) => term.onData((window as any).onData));
|
||||
await this.evaluate(([term]) => term.onKey((window as any).onKey));
|
||||
await this.evaluate(([term]) => term.onLineFeed((window as any).onLineFeed));
|
||||
await this.evaluate(([term]) => term.onRender((window as any).onRender));
|
||||
await this.evaluate(([term]) => term.onResize((window as any).onResize));
|
||||
await this.evaluate(([term]) => term.onScroll((window as any).onScroll));
|
||||
await this.evaluate(([term]) => term.onSelectionChange((window as any).onSelectionChange));
|
||||
await this.evaluate(([term]) => term.onTitleChange((window as any).onTitleChange));
|
||||
await this.evaluate(([term]) => term.onWriteParsed((window as any).onWriteParsed));
|
||||
}
|
||||
|
||||
// #region Events
|
||||
private _onBell = new EventEmitter<void>();
|
||||
public readonly onBell = this._onBell.event;
|
||||
private _onBinary = new EventEmitter<string>();
|
||||
public readonly onBinary = this._onBinary.event;
|
||||
private _onCursorMove = new EventEmitter<void>();
|
||||
public readonly onCursorMove = this._onCursorMove.event;
|
||||
private _onData = new EventEmitter<string>();
|
||||
public readonly onData = this._onData.event;
|
||||
private _onKey = new EventEmitter<{ key: string, domEvent: KeyboardEvent }>();
|
||||
public readonly onKey = this._onKey.event;
|
||||
private _onLineFeed = new EventEmitter<void>();
|
||||
public readonly onLineFeed = this._onLineFeed.event;
|
||||
private _onRender = new EventEmitter<{ start: number, end: number }>();
|
||||
public readonly onRender = this._onRender.event;
|
||||
private _onResize = new EventEmitter<{ cols: number, rows: number }>();
|
||||
public readonly onResize = this._onResize.event;
|
||||
private _onScroll = new EventEmitter<number>();
|
||||
public readonly onScroll = this._onScroll.event;
|
||||
private _onSelectionChange = new EventEmitter<void>();
|
||||
public readonly onSelectionChange = this._onSelectionChange.event;
|
||||
private _onTitleChange = new EventEmitter<string>();
|
||||
public readonly onTitleChange = this._onTitleChange.event;
|
||||
private _onWriteParsed = new EventEmitter<void>();
|
||||
public readonly onWriteParsed = this._onWriteParsed.event;
|
||||
// #endregion
|
||||
|
||||
// #region Simple properties
|
||||
public get cols(): Promise<number> { return this.evaluate(([term]) => term.cols); }
|
||||
public get rows(): Promise<number> { return this.evaluate(([term]) => term.rows); }
|
||||
public get modes(): Promise<IModes> { return this.evaluate(([term]) => term.modes); }
|
||||
// #endregion
|
||||
|
||||
// #region Complex properties
|
||||
public get buffer(): TerminalBufferNamespaceProxy { return new TerminalBufferNamespaceProxy(this._page, this); }
|
||||
/**
|
||||
* Exposes somewhat unsafe access to internals for testing things difficult to do with the regular
|
||||
* API
|
||||
*/
|
||||
public get core(): TerminalCoreProxy { return new TerminalCoreProxy(this._page, this); }
|
||||
// #endregion
|
||||
|
||||
// #region Proxied methods
|
||||
public async dispose(): Promise<void> { return this.evaluate(([term]) => term.dispose()); }
|
||||
public async reset(): Promise<void> { return this.evaluate(([term]) => term.reset()); }
|
||||
public async clear(): Promise<void> { return this.evaluate(([term]) => term.clear()); }
|
||||
public async focus(): Promise<void> { return this.evaluate(([term]) => term.focus()); }
|
||||
public async blur(): Promise<void> { return this.evaluate(([term]) => term.blur()); }
|
||||
public async hasSelection(): Promise<boolean> { return this.evaluate(([term]) => term.hasSelection()); }
|
||||
public async getSelection(): Promise<string> { return this.evaluate(([term]) => term.getSelection()); }
|
||||
public async getSelectionPosition(): Promise<IBufferRange | undefined> { return this.evaluate(([term]) => term.getSelectionPosition()); }
|
||||
public async selectAll(): Promise<void> { return this.evaluate(([term]) => term.selectAll()); }
|
||||
public async selectLines(start: number, end: number): Promise<void> { return this._page.evaluate(([term, start, end]) => term.selectLines(start, end), [await this.getHandle(), start, end] as const); }
|
||||
public async clearSelection(): Promise<void> { return this.evaluate(([term]) => term.clearSelection()); }
|
||||
public async select(column: number, row: number, length: number): Promise<void> { return this._page.evaluate(([term, column, row, length]) => term.select(column, row, length), [await this.getHandle(), column, row, length] as const); }
|
||||
public async paste(data: string): Promise<void> { return this._page.evaluate(([term, data]) => term.paste(data), [await this.getHandle(), data] as const); }
|
||||
public async refresh(start: number, end: number): Promise<void> { return this._page.evaluate(([term, start, end]) => term.refresh(start, end), [await this.getHandle(), start, end] as const); }
|
||||
public async getOption<T extends keyof ITerminalOptions>(key: T): Promise<ITerminalOptions[T]> { return this._page.evaluate(([term, key]) => term.options[key as T], [await this.getHandle(), key] as const); }
|
||||
public async setOption<T extends keyof ITerminalOptions>(key: T, value: ITerminalOptions[T]): Promise<any> { return this._page.evaluate(([term, key, value]) => term.options[key as T] = (value as ITerminalOptions[T]), [await this.getHandle(), key, value] as const); }
|
||||
public async setOptions(value: Partial<ITerminalOptions>): Promise<any> {
|
||||
return this._page.evaluate(([term, value]) => {
|
||||
term.options = value;
|
||||
}, [await this.getHandle(), value] as const);
|
||||
}
|
||||
public async scrollToTop(): Promise<void> { return this.evaluate(([term]) => term.scrollToTop()); }
|
||||
public async scrollToBottom(): Promise<void> { return this.evaluate(([term]) => term.scrollToBottom()); }
|
||||
public async scrollPages(pageCount: number): Promise<void> { return this._page.evaluate(([term, pageCount]) => term.scrollPages(pageCount), [await this.getHandle(), pageCount] as const); }
|
||||
public async scrollToLine(line: number): Promise<void> { return this._page.evaluate(([term, line]) => term.scrollToLine(line), [await this.getHandle(), line] as const); }
|
||||
public async scrollLines(amount: number): Promise<void> { return this._page.evaluate(([term, amount]) => term.scrollLines(amount), [await this.getHandle(), amount] as const); }
|
||||
public async write(data: string | Uint8Array): Promise<void> {
|
||||
return this._page.evaluate(([term, data]) => {
|
||||
return new Promise(r => term.write(typeof data === 'string' ? data : new Uint8Array(data), r));
|
||||
}, [await this.getHandle(), typeof data === 'string' ? data : Array.from(data)] as const);
|
||||
}
|
||||
public async writeln(data: string | Uint8Array): Promise<void> {
|
||||
return this._page.evaluate(([term, data]) => {
|
||||
return new Promise(r => term.writeln(typeof data === 'string' ? data : new Uint8Array(data), r));
|
||||
}, [await this.getHandle(), typeof data === 'string' ? data : Array.from(data)] as const);
|
||||
}
|
||||
public async resize(cols: number, rows: number): Promise<void> { return this._page.evaluate(([term, cols, rows]) => term.resize(cols, rows), [await this.getHandle(), cols, rows] as const); }
|
||||
public async registerMarker(y?: number | undefined): Promise<IMarker> { return this._page.evaluate(([term, y]) => term.registerMarker(y), [await this.getHandle(), y] as const); }
|
||||
public async registerDecoration(decorationOptions: IDecorationOptions): Promise<IDecoration | undefined> { return this._page.evaluate(([term, decorationOptions]) => term.registerDecoration(decorationOptions), [await this.getHandle(), decorationOptions] as const); }
|
||||
public async clearTextureAtlas(): Promise<void> { return this.evaluate(([term]) => term.clearTextureAtlas()); }
|
||||
// #endregion
|
||||
|
||||
public async evaluate<T>(pageFunction: PageFunction<JSHandle<Terminal>[], T>): Promise<T> {
|
||||
return this._page.evaluate(pageFunction, [await this.getHandle()]);
|
||||
}
|
||||
|
||||
public async evaluateHandle<T>(pageFunction: PageFunction<JSHandle<Terminal>[], T>): Promise<JSHandle<T>> {
|
||||
return this._page.evaluateHandle(pageFunction, [await this.getHandle()]);
|
||||
}
|
||||
|
||||
public async getHandle(): Promise<JSHandle<Terminal>> {
|
||||
// This is async because it must be evaluated each time it is called since term may have changed
|
||||
return this._page.evaluateHandle('window.term');
|
||||
}
|
||||
}
|
||||
|
||||
class TerminalBufferNamespaceProxy implements PlaywrightApiProxy<IBufferNamespace, never, never, 'active' | 'normal' | 'alternate'> {
|
||||
private _onBufferChange = new EventEmitter<IBuffer>();
|
||||
public readonly onBufferChange = this._onBufferChange.event;
|
||||
|
||||
constructor(
|
||||
private readonly _page: Page,
|
||||
private readonly _proxy: TerminalProxy
|
||||
) {
|
||||
|
||||
}
|
||||
|
||||
public get active(): TerminalBufferProxy { return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.active)); }
|
||||
public get normal(): TerminalBufferProxy { return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.normal)); }
|
||||
public get alternate(): TerminalBufferProxy { return new TerminalBufferProxy(this._page, this._proxy, this._proxy.evaluateHandle(([term]) => term.buffer.alternate)); }
|
||||
}
|
||||
|
||||
// TODO: Adopt PlaywrightApiProxy
|
||||
class TerminalBufferProxy /* implements EnsureAsyncProperties<IBuffer>*/ {
|
||||
constructor(
|
||||
private readonly _page: Page,
|
||||
private readonly _proxy: TerminalProxy,
|
||||
private readonly _handle: Promise<JSHandle<IBuffer>>
|
||||
) {
|
||||
}
|
||||
|
||||
public get type(): Promise<'normal' | 'alternate'> { return this.evaluate(([buffer]) => buffer.type); }
|
||||
public get cursorY(): Promise<number> { return this.evaluate(([buffer]) => buffer.cursorY); }
|
||||
public get cursorX(): Promise<number> { return this.evaluate(([buffer]) => buffer.cursorX); }
|
||||
public get viewportY(): Promise<number> { return this.evaluate(([buffer]) => buffer.viewportY); }
|
||||
public get baseY(): Promise<number> { return this.evaluate(([buffer]) => buffer.baseY); }
|
||||
public get length(): Promise<number> { return this.evaluate(([buffer]) => buffer.length); }
|
||||
public async getLine(y: number): Promise<TerminalBufferLine | undefined> {
|
||||
const lineHandle = await this._page.evaluateHandle(([buffer, y]) => buffer.getLine(y), [await this._handle, y] as const);
|
||||
const value = await lineHandle.jsonValue();
|
||||
if (value) {
|
||||
return new TerminalBufferLine(this._page, lineHandle as JSHandle<IBufferLine>);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async evaluate<T>(pageFunction: PageFunction<JSHandle<IBuffer>[], T>): Promise<T> {
|
||||
return this._page.evaluate(pageFunction, [await this._handle]);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Adopt PlaywrightApiProxy
|
||||
class TerminalBufferLine {
|
||||
constructor(
|
||||
private readonly _page: Page,
|
||||
private readonly _handle: JSHandle<IBufferLine>
|
||||
) {
|
||||
}
|
||||
|
||||
public get length(): Promise<number> { return this.evaluate(([bufferLine]) => bufferLine.length); }
|
||||
public get isWrapped(): Promise<boolean> { return this.evaluate(([bufferLine]) => bufferLine.isWrapped); }
|
||||
|
||||
public translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): Promise<string> {
|
||||
return this._page.evaluate(([bufferLine, trimRight, startColumn, endColumn]) => {
|
||||
return bufferLine.translateToString(trimRight, startColumn, endColumn);
|
||||
}, [this._handle, trimRight, startColumn, endColumn] as const);
|
||||
}
|
||||
|
||||
public async getCell(x: number): Promise<TerminalBufferCell | undefined> {
|
||||
const cellHandle = await this._page.evaluateHandle(([bufferLine, x]) => bufferLine.getCell(x), [this._handle, x] as const);
|
||||
const value = await cellHandle.jsonValue();
|
||||
if (value) {
|
||||
return new TerminalBufferCell(this._page, cellHandle as JSHandle<IBufferCell>);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async evaluate<T>(pageFunction: PageFunction<JSHandle<IBufferLine>[], T>): Promise<T> {
|
||||
return this._page.evaluate(pageFunction, [this._handle]);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Adopt PlaywrightApiProxy
|
||||
class TerminalBufferCell {
|
||||
constructor(
|
||||
private readonly _page: Page,
|
||||
private readonly _handle: JSHandle<IBufferCell>
|
||||
) {
|
||||
}
|
||||
|
||||
public getWidth(): Promise<number> { return this.evaluate(([line]) => line.getWidth()); }
|
||||
public getChars(): Promise<string> { return this.evaluate(([line]) => line.getChars()); }
|
||||
|
||||
public async evaluate<T>(pageFunction: PageFunction<JSHandle<IBufferCell>[], T>): Promise<T> {
|
||||
return this._page.evaluate(pageFunction, [this._handle]);
|
||||
}
|
||||
}
|
||||
|
||||
class TerminalCoreProxy {
|
||||
constructor(
|
||||
private readonly _page: Page,
|
||||
private readonly _proxy: TerminalProxy
|
||||
) {
|
||||
}
|
||||
|
||||
public get isDisposed(): Promise<boolean> { return this.evaluate(([core]) => (core as any)._isDisposed); }
|
||||
public get renderDimensions(): Promise<IRenderDimensions> { return this.evaluate(([core]) => ((core as any)._renderService as IRenderService).dimensions); }
|
||||
|
||||
public async triggerBinaryEvent(data: string): Promise<void> {
|
||||
return this._page.evaluate(([core, data]) => core.coreService.triggerBinaryEvent(data), [await this._getCoreHandle(), data] as const);
|
||||
}
|
||||
|
||||
|
||||
private async _getCoreHandle(): Promise<JSHandle<ICoreTerminal>> {
|
||||
return this._proxy.evaluateHandle(([term]) => (term as any)._core as ICoreTerminal);
|
||||
}
|
||||
|
||||
public async evaluate<T>(pageFunction: PageFunction<JSHandle<ICoreTerminal>[], T>): Promise<T> {
|
||||
return this._page.evaluate(pageFunction, [await this._getCoreHandle()]);
|
||||
}
|
||||
}
|
||||
|
||||
export async function openTerminal(ctx: ITestContext, options: ITerminalOptions | ITerminalInitOnlyOptions = {}): Promise<void> {
|
||||
await ctx.page.evaluate(`
|
||||
if ('term' in window) {
|
||||
try {
|
||||
window.term.dispose();
|
||||
} catch {}
|
||||
}
|
||||
`);
|
||||
// HACK: Tests may have side effects that could cause the terminal not to be removed. This
|
||||
// assertion catches this case early.
|
||||
strictEqual(await ctx.page.evaluate(`document.querySelector('#terminal-container').children.length`), 0, 'there must be no terminals on the page');
|
||||
await ctx.page.evaluate(`
|
||||
window.term = new window.Terminal(${JSON.stringify({ allowProposedApi: true, ...options })});
|
||||
window.term.open(document.querySelector('#terminal-container'));
|
||||
`);
|
||||
await ctx.page.waitForSelector('.xterm-rows');
|
||||
ctx.termHandle = await ctx.page.evaluateHandle('window.term');
|
||||
await ctx.proxy.initTerm();
|
||||
}
|
||||
|
||||
|
||||
export type MaybeAsync<T> = Promise<T> | T;
|
||||
|
||||
interface IPollForOptions<T> {
|
||||
equalityFn?: (a: T, b: T) => boolean;
|
||||
maxDuration?: number;
|
||||
stack?: string;
|
||||
}
|
||||
|
||||
export async function pollFor<T>(page: playwright.Page, evalOrFn: string | (() => MaybeAsync<T>), val: T, preFn?: () => Promise<void>, options?: IPollForOptions<T>): Promise<void> {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
options.stack ??= new Error().stack;
|
||||
if (preFn) {
|
||||
await preFn();
|
||||
}
|
||||
const result = typeof evalOrFn === 'string' ? await page.evaluate(evalOrFn) : await evalOrFn();
|
||||
|
||||
if (process.env.DEBUG) {
|
||||
console.log('pollFor\n actual: ', JSON.stringify(result), ' expected: ', JSON.stringify(val));
|
||||
}
|
||||
|
||||
let equalityCheck: boolean;
|
||||
if (options.equalityFn) {
|
||||
equalityCheck = options.equalityFn(result as T, val);
|
||||
} else {
|
||||
equalityCheck = true;
|
||||
try {
|
||||
deepStrictEqual(result, val);
|
||||
} catch (e) {
|
||||
equalityCheck = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!equalityCheck) {
|
||||
if (options.maxDuration === undefined) {
|
||||
options.maxDuration = 2000;
|
||||
}
|
||||
if (options.maxDuration <= 0) {
|
||||
deepStrictEqual(result, val, ([
|
||||
`pollFor max duration exceeded.`,
|
||||
(`Last comparison: ` +
|
||||
`${typeof result === 'object' ? JSON.stringify(result) : result} (actual) !== ` +
|
||||
`${typeof val === 'object' ? JSON.stringify(val) : val} (expected)`),
|
||||
`Stack: ${options.stack}`
|
||||
].join('\n')));
|
||||
}
|
||||
return new Promise<void>(r => {
|
||||
setTimeout(() => r(pollFor(page, evalOrFn, val, preFn, {
|
||||
...options,
|
||||
maxDuration: options!.maxDuration! - 10,
|
||||
stack: options!.stack
|
||||
})), 10);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function pollForApproximate<T>(page: playwright.Page, marginOfError: number, evalOrFn: string | (() => MaybeAsync<T>), val: T, preFn?: () => Promise<void>, maxDuration?: number, stack?: string): Promise<void> {
|
||||
await pollFor(page, evalOrFn, val, preFn, {
|
||||
maxDuration,
|
||||
stack,
|
||||
equalityFn: (a, b) => {
|
||||
if (a === b) {
|
||||
return true;
|
||||
}
|
||||
if (Array.isArray(a) && Array.isArray(b) && a.length === b.length) {
|
||||
let success = true;
|
||||
for (let i = 0 ; i < a.length; i++) {
|
||||
if (Math.abs(a[i] - b[i]) > marginOfError) {
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function writeSync(page: playwright.Page, data: string): Promise<void> {
|
||||
await page.evaluate(`
|
||||
window.ready = false;
|
||||
window.term.write('${data}', () => window.ready = true);
|
||||
`);
|
||||
await pollFor(page, 'window.ready', true);
|
||||
}
|
||||
|
||||
export async function timeout(ms: number): Promise<void> {
|
||||
return new Promise<void>(r => setTimeout(r, ms));
|
||||
}
|
||||
|
||||
export function getBrowserType(): playwright.BrowserType<playwright.WebKitBrowser> | playwright.BrowserType<playwright.ChromiumBrowser> | playwright.BrowserType<playwright.FirefoxBrowser> {
|
||||
// Default to chromium
|
||||
let browserType: playwright.BrowserType<playwright.WebKitBrowser> | playwright.BrowserType<playwright.ChromiumBrowser> | playwright.BrowserType<playwright.FirefoxBrowser> = playwright['chromium'];
|
||||
|
||||
const index = process.argv.indexOf('--browser');
|
||||
if (index !== -1 && process.argv.length > index + 1 && typeof process.argv[index + 1] === 'string') {
|
||||
const string = process.argv[index + 1];
|
||||
if (string === 'firefox' || string === 'webkit') {
|
||||
browserType = playwright[string];
|
||||
}
|
||||
}
|
||||
|
||||
return browserType;
|
||||
}
|
||||
|
||||
export function launchBrowser(): Promise<playwright.Browser> {
|
||||
const browserType = getBrowserType();
|
||||
const options: Record<string, unknown> = {
|
||||
headless: process.argv.includes('--headless')
|
||||
};
|
||||
|
||||
const index = process.argv.indexOf('--executablePath');
|
||||
if (index > 0 && process.argv.length > index + 1 && typeof process.argv[index + 1] === 'string') {
|
||||
options.executablePath = process.argv[index + 1];
|
||||
}
|
||||
|
||||
return browserType.launch(options);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import { PlaywrightTestConfig } from '@playwright/test';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: '.',
|
||||
timeout: 10000,
|
||||
projects: [
|
||||
{
|
||||
name: 'Chrome Stable',
|
||||
use: {
|
||||
browserName: 'chromium',
|
||||
channel: 'chrome'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'Firefox Stable',
|
||||
use: {
|
||||
browserName: 'firefox'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'WebKit',
|
||||
use: {
|
||||
browserName: 'webkit'
|
||||
}
|
||||
}
|
||||
],
|
||||
reporter: 'list',
|
||||
webServer: {
|
||||
command: 'npm start',
|
||||
port: 3000,
|
||||
timeout: 120000,
|
||||
reuseExistingServer: !process.env.CI
|
||||
}
|
||||
};
|
||||
export default config;
|
||||
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": [
|
||||
"dom",
|
||||
"es6"
|
||||
],
|
||||
"rootDir": ".",
|
||||
"outDir": "../../out-test/playwright",
|
||||
"types": [
|
||||
"../../node_modules/@types/node",
|
||||
"../../node_modules/@lunapaint/png-codec"
|
||||
],
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"pretty": true,
|
||||
"strict": true,
|
||||
"declaration": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"browser/*": [
|
||||
"../../src/browser/*"
|
||||
],
|
||||
"common/*": [
|
||||
"../../src/common/*"
|
||||
]
|
||||
},
|
||||
"target": "es2016",
|
||||
"module": "commonjs",
|
||||
},
|
||||
"include": [
|
||||
"./**/*",
|
||||
"../../typings/xterm.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../src/browser"
|
||||
},
|
||||
{
|
||||
"path": "../../src/common"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
{ "path": "./src/headless" },
|
||||
{ "path": "./test/api" },
|
||||
{ "path": "./test/benchmark" },
|
||||
{ "path": "./test/playwright" },
|
||||
{ "path": "./addons/xterm-addon-attach" },
|
||||
{ "path": "./addons/xterm-addon-canvas" },
|
||||
{ "path": "./addons/xterm-addon-fit" },
|
||||
|
||||
@@ -326,6 +326,13 @@
|
||||
"@jridgewell/resolve-uri" "3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "1.4.14"
|
||||
|
||||
"@lunapaint/png-codec@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@lunapaint/png-codec/-/png-codec-0.2.0.tgz#b9fe0a0728889af280c31239b061eb3f8c848816"
|
||||
integrity sha512-S2Fk8+I27j8ZL585PlEK9hhljZcp6j+JWB5ZHAeePdufJMYHxXD2zlatLRaiy5riXRFqkCi/gong7yP9kSsEZg==
|
||||
dependencies:
|
||||
pako "^2.0.4"
|
||||
|
||||
"@nodelib/fs.scandir@2.1.5":
|
||||
version "2.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
|
||||
@@ -347,6 +354,16 @@
|
||||
"@nodelib/fs.scandir" "2.1.5"
|
||||
fastq "^1.6.0"
|
||||
|
||||
"@playwright/test@^1.37.1":
|
||||
version "1.37.1"
|
||||
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.37.1.tgz#e7f44ae0faf1be52d6360c6bbf689fd0057d9b6f"
|
||||
integrity sha512-bq9zTli3vWJo8S3LwB91U0qDNQDpEXnw7knhxLM0nwDvexQAwx9tO8iKDZSqqneVq+URd/WIoz+BALMqUTgdSg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
playwright-core "1.37.1"
|
||||
optionalDependencies:
|
||||
fsevents "2.3.2"
|
||||
|
||||
"@tootallnate/once@2":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||
@@ -1920,7 +1937,7 @@ fs.realpath@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
|
||||
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
|
||||
|
||||
fsevents@~2.3.2:
|
||||
fsevents@2.3.2, fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
@@ -2992,6 +3009,11 @@ package-hash@^4.0.0:
|
||||
lodash.flattendeep "^4.4.0"
|
||||
release-zalgo "^1.0.0"
|
||||
|
||||
pako@^2.0.4:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86"
|
||||
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
|
||||
|
||||
parent-module@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
|
||||
@@ -3061,17 +3083,10 @@ pkg-dir@^4.1.0, pkg-dir@^4.2.0:
|
||||
dependencies:
|
||||
find-up "^4.0.0"
|
||||
|
||||
playwright-core@1.36.2:
|
||||
version "1.36.2"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.36.2.tgz#32382f2d96764c24c65a86ea336cf79721c2e50e"
|
||||
integrity sha512-sQYZt31dwkqxOrP7xy2ggDfEzUxM1lodjhsQ3NMMv5uGTRDsLxU0e4xf4wwMkF2gplIxf17QMBCodSFgm6bFVQ==
|
||||
|
||||
playwright@^1.22.1:
|
||||
version "1.36.2"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.36.2.tgz#4f25272209a31ad1138d7b9868e0325159f003e3"
|
||||
integrity sha512-4Fmlq3KWsl85Bl4InJw1NC21aeQV0iSZuFvTDcy1F8zVmXmgQRe89GxF8zMSRt/KIS+2tUolak7EXVl9aC+JdA==
|
||||
dependencies:
|
||||
playwright-core "1.36.2"
|
||||
playwright-core@1.37.1:
|
||||
version "1.37.1"
|
||||
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.37.1.tgz#cb517d52e2e8cb4fa71957639f1cd105d1683126"
|
||||
integrity sha512-17EuQxlSIYCmEMwzMqusJ2ztDgJePjrbttaefgdsiqeLWidjYz9BxXaTaZWxH1J95SHGk6tjE+dwgWILJoUZfA==
|
||||
|
||||
prelude-ls@^1.2.1:
|
||||
version "1.2.1"
|
||||
|
||||
Reference in New Issue
Block a user