Initial attempt for e2e tests

- Add node code for e2e tests. This is a first take to try running
  these tests via actions.
- Relates to #401
This commit is contained in:
Janne Valkealahti
2022-05-06 13:41:10 +01:00
parent 8a23518b84
commit 3743a7f32d
17 changed files with 6844 additions and 0 deletions

2
e2e/spring-shell-e2e/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
node_modules
lib

View File

@@ -0,0 +1,11 @@
{
"printWidth": 120,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "none",
"bracketSpacing": true,
"arrowParens": "avoid",
"parser": "typescript"
}

38
e2e/spring-shell-e2e/package-lock.json generated Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "spring-shell-e2e",
"version": "2.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"nan": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz",
"integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
},
"node-pty": {
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.10.1.tgz",
"integrity": "sha512-JTdtUS0Im/yRsWJSx7yiW9rtpfmxqxolrtnyKwPLI+6XqTAPW/O2MjS8FYL4I5TsMbH2lVgDb2VMjp+9LoQGNg==",
"requires": {
"nan": "^2.14.0"
}
},
"prettier": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.6.2.tgz",
"integrity": "sha512-PkUpF+qoXTqhOeWL9fu7As8LXsIUZ1WYaJiY/a7McAQzxjk82OF0tibkFXVCDImZtWxbvojFjerkiLb0/q8mew==",
"dev": true
},
"typescript": {
"version": "4.6.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.6.4.tgz",
"integrity": "sha512-9ia/jWHIEbo49HfjrLGfKbZSuWo9iTMwXO+Ca3pRsSpbsMbc7/IU8NKdCZVRRBafVPGnoJeFL76ZOAA84I9fEg==",
"dev": true
},
"xterm-headless": {
"version": "4.18.0",
"resolved": "https://registry.npmjs.org/xterm-headless/-/xterm-headless-4.18.0.tgz",
"integrity": "sha512-VwSPG2cyVOwesVVLBVrybYNY6cUMiVkD2fLnIXcBs/1iJ3M7bhD3lP9HdQOHGtOkbxV2Duf7MZNmEfngBXL/iQ=="
}
}
}

View File

@@ -0,0 +1,41 @@
{
"name": "spring-shell-e2e",
"version": "2.1.0",
"description": "spring-shell e2e test framework",
"main": "./lib/cli.js",
"exports": {
".": "./lib/cli.js"
},
"typesVersions": {
"*": {
"cli.d.ts": [
"lib/cli.d.ts"
]
}
},
"type": "module",
"scripts": {
"build": "tsc",
"test": "jest",
"format": "prettier --write **/*.ts",
"format-check": "prettier --check **/*.ts"
},
"repository": {
"type": "git",
"url": "git+https://github.com/spring-projects/spring-shell.git"
},
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/spring-projects/spring-shell/issues"
},
"homepage": "https://github.com/spring-projects/spring-shell#readme",
"devDependencies": {
"prettier": "^2.6.2",
"typescript": "^4.6.4"
},
"dependencies": {
"node-pty": "^0.10.1",
"xterm-headless": "^4.18.0"
}
}

View File

@@ -0,0 +1,128 @@
import * as pty from 'node-pty';
import { Terminal } from 'xterm-headless';
export interface CliOptions {
command: string;
options?: string[];
keyWait?: number;
cols?: number;
rows?: number;
}
export class Cli {
private isDisposed: boolean = false;
private pty: pty.IPty | undefined;
private term: Terminal | undefined;
private keyWait: number = 500;
private cols: number = 80;
private rows: number = 20;
private exit: Promise<number> | undefined;
constructor(private options: CliOptions) {
if (options.keyWait) {
this.keyWait = options.keyWait;
}
if (options.cols) {
this.cols = options.cols;
}
if (options.rows) {
this.rows = options.rows;
}
}
public run(): void {
this.pty = pty.spawn(this.options.command, this.options.options || [], {
name: 'xterm-256color',
cols: this.cols,
rows: this.rows
});
this.term = new Terminal({
cols: this.cols,
rows: this.rows
});
this.pty.onData(data => {
this.term?.write(data);
});
this.exit = new Promise(resolve => {
this.pty?.onExit(data => {
resolve(data.exitCode);
});
});
}
public exitCode(): Promise<number> {
if (!this.exit) {
return Promise.reject('cli has not been started');
}
return this.exit;
}
public screen(): string[] {
const l = this.term?.buffer.active.length || 0;
const ret: string[] = [];
for (let index = 0; index < l; index++) {
const line = this.term?.buffer.active.getLine(index)?.translateToString();
if (line) {
ret.push(line);
}
}
return ret;
}
public async keyText(data: string, wait: number): Promise<Cli> {
if (!this.pty) {
return Promise.reject('cli has not been started');
}
this.pty.write(data);
await this.doWait(wait);
return this;
}
public async keyUp(wait?: number): Promise<Cli> {
if (!this.pty) {
return Promise.reject('cli has not been started');
}
this.pty.write('\x1BOA');
await this.doWait(wait);
return this;
}
public async keyDown(wait?: number): Promise<Cli> {
if (!this.pty) {
return Promise.reject('cli has not been started');
}
this.pty.write('\x1BOB');
await this.doWait(wait);
return this;
}
public async keyEnter(wait?: number): Promise<Cli> {
if (!this.pty) {
return Promise.reject('cli has not been started');
}
this.pty.write('\x0D');
await this.doWait(wait);
return this;
}
public dispose(): void {
if (this.isDisposed) {
return;
}
if (this.pty) {
this.pty.kill();
}
if (this.term) {
this.term.dispose();
}
this.isDisposed = true;
}
private async doWait(wait?: number): Promise<void> {
await this.sleep(wait || this.keyWait);
}
private async sleep(ms: number): Promise<void> {
return new Promise(r => setTimeout(r, ms));
}
}

View File

@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
"module": "commonjs", /* Specify what module code is generated. */
"moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"outDir": "./lib", /* Specify an output folder for all emitted files. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
"strict": true, /* Enable all strict type-checking options. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
}
}