Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
node_modules/
lib/
.tui-test/
*.tgz
t*.md
Expand Down
Empty file modified index.js
100644 → 100755
Empty file.
2 changes: 2 additions & 0 deletions lib/cli/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { Command } from "commander";
export declare const program: Command;
26 changes: 26 additions & 0 deletions lib/cli/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { Command } from "commander";
import { getVersion } from "./version.js";
import { executableName } from "../utils/constants.js";
import { run } from "../runner/runner.js";
import showTrace from "./show-trace.js";
const action = async (testFilter, options) => {
const { updateSnapshot, trace } = options;
await run({ updateSnapshot: updateSnapshot ?? false, testFilter, trace });
};
export const program = new Command();
program
.name(executableName)
.description(`a fast and precise end-to-end terminal testing framework

Examples:
\`npx @microsoft/tui-test my.spec.ts\`
\`npx @microsoft/tui-test some.spec.ts:42\``)
.argument("[test-filter...]", "Pass an argument to filter test files. Each argument is treated as a regular expression. Matching is performed against the absolute file paths")
.option("-u, --updateSnapshot", `use this flag to re-record snapshots`)
.option("-t, --trace", `enable traces for test execution`)
.version(await getVersion(), "-v, --version", "output the current version")
.action(action)
.showHelpAfterError("(add --help for additional information)");
program.addCommand(showTrace);
3 changes: 3 additions & 0 deletions lib/cli/show-trace.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Command } from "commander";
declare const cmd: Command;
export default cmd;
14 changes: 14 additions & 0 deletions lib/cli/show-trace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import { Command } from "commander";
import { loadTrace } from "../trace/tracer.js";
import { play } from "../trace/viewer.js";
const action = async (traceFile) => {
const trace = await loadTrace(traceFile);
await play(trace);
};
const cmd = new Command("show-trace")
.description(`view traces in the console`)
.argument("<trace-file>", "the trace to replay in the terminal");
cmd.action(action);
export default cmd;
1 change: 1 addition & 0 deletions lib/cli/version.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export declare const getVersion: () => Promise<string>;
15 changes: 15 additions & 0 deletions lib/cli/version.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import url from "node:url";
import path from "node:path";
import fsAsync from "node:fs/promises";
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export const getVersion = async () => {
const packageJsonPath = path.join(__dirname, "..", "..", "package.json");
const packageJson = await fsAsync.readFile(packageJsonPath, {
encoding: "utf-8",
});
const packageJsonParsed = JSON.parse(packageJson);
return packageJsonParsed.version;
};
219 changes: 219 additions & 0 deletions lib/config/config.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
import { TestOptions } from "../test/option.js";
export declare const loadConfig: () => Promise<Required<TestConfig>>;
export declare const getExpectTimeout: () => number;
export declare const getTimeout: () => number;
export declare const getRetries: () => number;
declare type TestProject = {
/**
* Project name is visible in the report and during test execution.
*/
name?: string;
};
declare type WorkerOptions = {
/**
* Only the files matching one of these patterns are executed as test files. Matching is performed against the
* absolute file path. Strings are treated as glob patterns.
*
* By default, TUI Test looks for files matching the following glob pattern: `**\/*.@(spec|test).?(c|m)[jt]s?(x)`.
* This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example
* `bash.integration.spec.ts`.
*
* Use testConfig.testMatch to change this option for all projects.
*/
testMatch?: string;
};
export declare type ProjectConfig = TestOptions & Required<WorkerOptions> & TestProject;
export declare type TestConfig = {
/**
* Configuration for the `expect` assertion library.
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig } from '@microsoft/tui-test';
*
* export default defineConfig({
* expect: {
* timeout: 10000,
* },
* });
* ```
*
*/
expect?: {
/**
* Default timeout for async expect matchers in milliseconds, defaults to 5000ms.
*/
timeout?: number;
};
/**
* Timeout for each test in milliseconds. Defaults to 30 seconds.
*
* This is a base timeout for all tests.
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig } from '@microsoft/tui-test';
*
* export default defineConfig({
* timeout: 5 * 60 * 1000,
* });
* ```
*
*/
timeout?: number;
/**
* Only the files matching one of these patterns are executed as test files. Matching is performed against the
* absolute file path. Strings are treated as glob patterns.
*
* By default, TUI Test looks for files matching the following glob pattern: `**\/*.@(spec|test).?(c|m)[jt]s?(x)`.
* This means JavaScript or TypeScript files with `".test"` or `".spec"` suffix, for example
* `bash.integration.spec.ts`.
*
* Use testConfig.testMatch to change this option for all projects.
*/
testMatch?: string;
/**
* Maximum time in milliseconds the whole test suite can run. Zero timeout (default) disables this behavior. Useful on
* CI to prevent broken setup from running too long and wasting resources.
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig } from '@microsoft/tui-test';
*
* export default defineConfig({
* globalTimeout: process.env.CI ? 60 * 60 * 1000 : undefined,
* });
* ```
*
*/
globalTimeout?: number;
/**
* The maximum number of retry attempts given to failed tests. By default failing tests are not retried.
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig } from '@microsoft/tui-test';
*
* export default defineConfig({
* retries: 2,
* });
* ```
*
*/
retries?: number;
/**
* The list of builtin reporters to use.
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig } from '@microsoft/tui-test';
*
* export default defineConfig({
* reporter: 'list',
* });
* ```
*
*/
reporter?: "list";
/**
* Options for all tests in this project
*
* ```js
* // tui-test.config.ts
* import { defineConfig, Shell } from '@microsoft/tui-test';
*
* export default defineConfig({
* projects: [
* {
* name: 'bash',
* use: {
* shell: Shell.Bash,
* },
* },
* ],
* });
* ```
*
* Use testConfig.use to change this option for
* all projects.
*/
use?: TestOptions;
/**
* The number of workers to use. Defaults to 50% of the logical cpu cores. If
* there are less tests than requested workers, there will be 1 worker used per test.
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig } from '@microsoft/tui-test';
*
* export default defineConfig({
* workers: 4,
* });
* ```
*
*/
workers?: number;
/**
* Record each test run for replay.
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig } from '@microsoft/tui-test';
*
* export default defineConfig({
* trace: true,
* });
* ```
*
*/
trace?: boolean;
/**
* Folder to store the traces in. Defaults to `tui-traces`
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig } from '@microsoft/tui-test';
*
* export default defineConfig({
* traceFolder: "tui-traces",
* });
* ```
*
*/
traceFolder?: string;
/**
* TUI Test supports running multiple test projects at the same time.
*
* **Usage**
*
* ```js
* // tui-test.config.ts
* import { defineConfig, Shell } from '@microsoft/tui-test';
*
* export default defineConfig({
* projects: [
* { name: 'bash', use: Shell.Bash }
* ]
* });
* ```
*
*/
projects?: ProjectConfig[];
};
export {};
39 changes: 39 additions & 0 deletions lib/config/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import fs from "node:fs";
import path from "node:path";
import process from "node:process";
import os from "node:os";
import { defaultShell } from "../terminal/shell.js";
import { cacheFolderName, configFileName } from "../utils/constants.js";
const configPath = path.join(process.cwd(), cacheFolderName, configFileName);
let loadedConfig;
export const loadConfig = async () => {
const userConfig = !fs.existsSync(configPath)
? {}
: (await import(`file://${configPath}`)).default ?? {};
loadedConfig = {
testMatch: userConfig.testMatch ?? "**/*.@(spec|test).?(c|m)[jt]s?(x)",
expect: {
timeout: userConfig.timeout ?? 5000,
},
globalTimeout: userConfig.globalTimeout ?? 0,
retries: userConfig.retries ?? 0,
projects: userConfig.projects ?? [],
timeout: userConfig.timeout ?? 30000,
reporter: userConfig.reporter ?? "list",
workers: Math.max(userConfig.workers ?? Math.max(Math.floor(os.cpus().length / 2), 1), 1),
trace: userConfig.trace ?? false,
traceFolder: userConfig.traceFolder ?? path.join(process.cwd(), "tui-traces"),
use: {
shell: userConfig.use?.shell ?? defaultShell,
rows: userConfig.use?.rows ?? 30,
columns: userConfig.use?.columns ?? 80,
program: userConfig.use?.program,
},
};
return loadedConfig;
};
export const getExpectTimeout = () => loadedConfig?.expect.timeout ?? 5000;
export const getTimeout = () => loadedConfig?.timeout ?? 30000;
export const getRetries = () => loadedConfig?.retries ?? 0;
24 changes: 24 additions & 0 deletions lib/reporter/base.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { TestCase, TestResult, TestStatus } from "../test/testcase.js";
import { Shell } from "../terminal/shell.js";
import { Suite } from "../test/suite.js";
export type StaleSnapshotSummary = {
obsolete: number;
removed: number;
};
export declare class BaseReporter {
protected currentTest: number;
protected isTTY: boolean;
constructor();
private _plural;
start(testCount: number, shells: Shell[], maxWorkers: number): Promise<void>;
startTest(test: TestCase, result: TestResult): void;
endTest(test: TestCase, result: TestResult): void;
end(rootSuite: Suite, staleSnapshotSummary: StaleSnapshotSummary): number;
private _generateSummary;
private _printSummary;
private _header;
private _retryHeader;
private _stdStreamHeader;
private _printFailures;
protected _resultColor(status: TestStatus): (str: string) => string;
}
Loading