@scalvert/bin-tester 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # @scalvert/bin-tester
2
+
3
+ ![CI Build](https://github.com/scalvert/bin-tester/workflows/CI%20Build/badge.svg)
4
+ [![npm version](https://badge.fury.io/js/%40scalvert%2Fbin-tester.svg)](https://badge.fury.io/js/%40scalvert%2Fbin-tester)
5
+ [![License](https://img.shields.io/npm/l/@scalvert/bin-tester.svg)](https://github.com/scalvert/bin-tester/blob/master/package.json)
6
+ ![Dependabot](https://badgen.net/badge/icon/dependabot?icon=dependabot&label)
7
+ ![Volta Managed](https://img.shields.io/static/v1?label=volta&message=managed&color=yellow&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QAeQC6AMEpK7AhAAAACXBIWXMAAAsSAAALEgHS3X78AAAAB3RJTUUH5AMGFS07qAYEaAAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAAFmSURBVDjLY2CgB/g/j0H5/2wGW2xyTAQ1r2DQYOBgm8nwh+EY6TYvZtD7f9rn5e81fAGka17GYPL/esObP+dyj5Cs+edqZsv/V8o//H+z7P+XHarW+NSyoAv8WsFszyKTtoVBM5Tn7/Xys+zf7v76vYrJlPEvAwPjH0YGxp//3jGl/L8LU8+IrPnPUkY3ZomoDQwOpZwMv14zMHy8yMDwh4mB4Q8jA8OTgwz/L299wMDyx4Mp9f9NDAP+bWVwY3jGsJpB3JaDQVCEgYHlLwPDfwYWRqVQJgZmHoZ/+3PPfWP+68Mb/Pw5sqUoLni9ipuRnekrAwMjA8Ofb6K8/PKBF5nU7RX+Hize8Y2DOZTP7+kXogPy1zrH+f/vT/j/Z5nUvGcr5VhJioUf88UC/59L+/97gUgDyVH4YzqXxL8dOs/+zuFLJivd/53HseLPPHZPsjT/nsHi93cqozHZue7rLDYhUvUAADjCgneouzo/AAAAAElFTkSuQmCC&link=https://volta.sh)
8
+ [![Code Style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](#badge)
9
+
10
+ > Provides a test harness for node CLIs that allow you to run tests against a real project.
11
+
12
+ ## Install
13
+
14
+ ```shell
15
+ npm add @scalvert/bin-tester --save-dev
16
+
17
+ # or
18
+
19
+ yarn add @scalvert/bin-tester --dev
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ `@scalvert/bin-tester` uses two libraries to provide the test harness:
25
+
26
+ - [`fixturify-project`](https://github.com/stefanpenner/node-fixturify-project): Allows you to dynamically create test fixtures using real directories and files in a tmp directory
27
+ - [`execa`](https://github.com/sindresorhus/execa): A better replacement for `child_process.exec`
28
+
29
+ It combines the above and provides an API for running a binary with a set of arguments against a real project structure, thus mimicking testing a real environment.
30
+
31
+ ```js
32
+ import { createBinTester } from '@scalvert/bin-tester';
33
+
34
+ describe('Some tests', () => {
35
+ let project;
36
+ let { setupProject, teardownProject, runBin } = createBinTester({
37
+ binPath: 'node_modules/.bin/someBin',
38
+ staticArgs: ['--some-arg'], // pass some args to the bin that will be used for each invocation
39
+ });
40
+
41
+ beforeEach(() => {
42
+ project = await setupProject();
43
+ });
44
+
45
+ afterEach(() => {
46
+ await teardownProject();
47
+ });
48
+
49
+ // Run the bin and do something with the result
50
+ test('a test', () => {
51
+ const result = await runBin();
52
+
53
+ expect(result.stdout).toBe('Did some stuff');
54
+ });
55
+
56
+ // Write a file with contents to the tmp directory
57
+ test('another test', () => {
58
+ project.write({
59
+ 'some/file.txt': 'some content',
60
+ });
61
+
62
+ const result = await runBin({
63
+ args: ['--path', 'some/file.txt'], // pass some args to the bin that will be used for only this invocation
64
+ });
65
+
66
+ expect(result.stdout).toBe('Read "some/file.txt"');
67
+ });
68
+ });
69
+ ```
70
+
71
+ ## API
72
+
73
+ ```ts
74
+ class BinTesterProject extends Project {
75
+ private _dirChanged;
76
+ constructor(name: string, version?: string, cb?: (project: Project) => void);
77
+ gitInit(): execa.ExecaChildProcess<string>;
78
+ chdir(): void;
79
+ dispose(): void;
80
+ }
81
+
82
+ interface BinTesterOptions {
83
+ binPath: string;
84
+ projectConstructor?: any;
85
+ }
86
+
87
+ interface RunOptions {
88
+ args?: string[];
89
+ execaOptions?: execa.Options<string>;
90
+ }
91
+
92
+ interface createBinTesterResult<TProject extends BinTesterProject> {
93
+ runBin: (runOptions?: RunOptions) => execa.ExecaChildProcess<string>;
94
+ setupProject: () => Promise<TProject>;
95
+ setupTmpDir: () => Promise<string>;
96
+ teardownProject: () => void;
97
+ }
98
+
99
+ function createBinTester<TProject extends BinTesterProject>(options: BinTesterOptions): createBinTesterResult<TProject>;
100
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,126 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
9
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
10
+ var __spreadValues = (a, b) => {
11
+ for (var prop in b || (b = {}))
12
+ if (__hasOwnProp.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ if (__getOwnPropSymbols)
15
+ for (var prop of __getOwnPropSymbols(b)) {
16
+ if (__propIsEnum.call(b, prop))
17
+ __defNormalProp(a, prop, b[prop]);
18
+ }
19
+ return a;
20
+ };
21
+ var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
22
+ var __export = (target, all) => {
23
+ for (var name in all)
24
+ __defProp(target, name, { get: all[name], enumerable: true });
25
+ };
26
+ var __reExport = (target, module2, copyDefault, desc) => {
27
+ if (module2 && typeof module2 === "object" || typeof module2 === "function") {
28
+ for (let key of __getOwnPropNames(module2))
29
+ if (!__hasOwnProp.call(target, key) && (copyDefault || key !== "default"))
30
+ __defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
31
+ }
32
+ return target;
33
+ };
34
+ var __toESM = (module2, isNodeMode) => {
35
+ return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", !isNodeMode && module2 && module2.__esModule ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
36
+ };
37
+ var __toCommonJS = /* @__PURE__ */ ((cache) => {
38
+ return (module2, temp) => {
39
+ return cache && cache.get(module2) || (temp = __reExport(__markAsModule({}), module2, 1), cache && cache.set(module2, temp), temp);
40
+ };
41
+ })(typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : 0);
42
+
43
+ // src/index.ts
44
+ var src_exports = {};
45
+ __export(src_exports, {
46
+ BinTesterProject: () => BinTesterProject,
47
+ createBinTester: () => createBinTester
48
+ });
49
+
50
+ // src/create-bin-tester.ts
51
+ var import_execa2 = __toESM(require("execa"), 1);
52
+
53
+ // src/project.ts
54
+ var import_execa = __toESM(require("execa"), 1);
55
+ var import_fixturify_project = require("fixturify-project");
56
+ var ROOT = process.cwd();
57
+ var BinTesterProject = class extends import_fixturify_project.Project {
58
+ constructor(name = "fake-project", version, cb) {
59
+ super(name, version, cb);
60
+ this._dirChanged = false;
61
+ this.pkg = Object.assign({}, this.pkg, {
62
+ license: "MIT",
63
+ description: "Fake project",
64
+ repository: "http://fakerepo.com"
65
+ });
66
+ }
67
+ gitInit() {
68
+ return (0, import_execa.default)(`git init -q ${this.baseDir}`);
69
+ }
70
+ chdir() {
71
+ this._dirChanged = true;
72
+ process.chdir(this.baseDir);
73
+ }
74
+ dispose() {
75
+ if (this._dirChanged) {
76
+ process.chdir(ROOT);
77
+ }
78
+ return super.dispose();
79
+ }
80
+ };
81
+
82
+ // src/create-bin-tester.ts
83
+ var DEFAULT_BIN_TESTER_OPTIONS = {
84
+ staticArgs: [],
85
+ projectConstructor: BinTesterProject
86
+ };
87
+ var DEFAULT_RUN_OPTIONS = {
88
+ args: [],
89
+ execaOptions: {}
90
+ };
91
+ function createBinTester(options) {
92
+ let project;
93
+ const mergedOptions = __spreadValues(__spreadValues({}, DEFAULT_BIN_TESTER_OPTIONS), options);
94
+ function runBin(runOptions = DEFAULT_RUN_OPTIONS) {
95
+ return (0, import_execa2.default)(process.execPath, [mergedOptions.binPath, ...mergedOptions.staticArgs, ...runOptions.args], __spreadValues({
96
+ reject: false,
97
+ cwd: project.baseDir
98
+ }, runOptions.execaOptions));
99
+ }
100
+ async function setupProject() {
101
+ project = new mergedOptions.projectConstructor();
102
+ await project.write();
103
+ return project;
104
+ }
105
+ async function setupTmpDir() {
106
+ if (typeof project === "undefined") {
107
+ await setupProject();
108
+ }
109
+ return project.baseDir;
110
+ }
111
+ function teardownProject() {
112
+ project.dispose();
113
+ }
114
+ return {
115
+ runBin,
116
+ setupProject,
117
+ teardownProject,
118
+ setupTmpDir
119
+ };
120
+ }
121
+ module.exports = __toCommonJS(src_exports);
122
+ // Annotate the CommonJS export names for ESM import in node:
123
+ 0 && (module.exports = {
124
+ BinTesterProject,
125
+ createBinTester
126
+ });
@@ -0,0 +1,50 @@
1
+ import execa from 'execa';
2
+ import { Project } from 'fixturify-project';
3
+
4
+ declare class BinTesterProject extends Project {
5
+ private _dirChanged;
6
+ constructor(name?: string, version?: string, cb?: (project: Project) => void);
7
+ gitInit(): execa.ExecaChildProcess<string>;
8
+ chdir(): void;
9
+ dispose(): void;
10
+ }
11
+
12
+ interface BinTesterOptions {
13
+ /**
14
+ * The absolute path to the bin to invoke
15
+ */
16
+ binPath: string;
17
+ /**
18
+ * An array of static arguments that will be used every time when running the bin
19
+ */
20
+ staticArgs?: string[];
21
+ /**
22
+ * An optional class to use to create the project
23
+ */
24
+ projectConstructor?: any;
25
+ }
26
+ interface RunOptions {
27
+ /**
28
+ * Arguments to provide to the bin script.
29
+ */
30
+ args: string[];
31
+ /**
32
+ * Options to provide to execa. @see https://github.com/sindresorhus/execa#options
33
+ */
34
+ execaOptions?: execa.Options<string>;
35
+ }
36
+ interface CreateBinTesterResult<TProject extends BinTesterProject> {
37
+ runBin: (runOptions?: RunOptions) => execa.ExecaChildProcess<string>;
38
+ setupProject: () => Promise<TProject>;
39
+ setupTmpDir: () => Promise<string>;
40
+ teardownProject: () => void;
41
+ }
42
+ /**
43
+ * Creates the bin tester API functions to use within tests.
44
+ *
45
+ * @param options - An object of bin tester options
46
+ * @returns - A project instance.
47
+ */
48
+ declare function createBinTester<TProject extends BinTesterProject>(options: BinTesterOptions): CreateBinTesterResult<TProject>;
49
+
50
+ export { BinTesterProject, createBinTester };
package/dist/index.js ADDED
@@ -0,0 +1,92 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
4
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __spreadValues = (a, b) => {
7
+ for (var prop in b || (b = {}))
8
+ if (__hasOwnProp.call(b, prop))
9
+ __defNormalProp(a, prop, b[prop]);
10
+ if (__getOwnPropSymbols)
11
+ for (var prop of __getOwnPropSymbols(b)) {
12
+ if (__propIsEnum.call(b, prop))
13
+ __defNormalProp(a, prop, b[prop]);
14
+ }
15
+ return a;
16
+ };
17
+
18
+ // src/create-bin-tester.ts
19
+ import execa2 from "execa";
20
+
21
+ // src/project.ts
22
+ import execa from "execa";
23
+ import { Project } from "fixturify-project";
24
+ var ROOT = process.cwd();
25
+ var BinTesterProject = class extends Project {
26
+ constructor(name = "fake-project", version, cb) {
27
+ super(name, version, cb);
28
+ this._dirChanged = false;
29
+ this.pkg = Object.assign({}, this.pkg, {
30
+ license: "MIT",
31
+ description: "Fake project",
32
+ repository: "http://fakerepo.com"
33
+ });
34
+ }
35
+ gitInit() {
36
+ return execa(`git init -q ${this.baseDir}`);
37
+ }
38
+ chdir() {
39
+ this._dirChanged = true;
40
+ process.chdir(this.baseDir);
41
+ }
42
+ dispose() {
43
+ if (this._dirChanged) {
44
+ process.chdir(ROOT);
45
+ }
46
+ return super.dispose();
47
+ }
48
+ };
49
+
50
+ // src/create-bin-tester.ts
51
+ var DEFAULT_BIN_TESTER_OPTIONS = {
52
+ staticArgs: [],
53
+ projectConstructor: BinTesterProject
54
+ };
55
+ var DEFAULT_RUN_OPTIONS = {
56
+ args: [],
57
+ execaOptions: {}
58
+ };
59
+ function createBinTester(options) {
60
+ let project;
61
+ const mergedOptions = __spreadValues(__spreadValues({}, DEFAULT_BIN_TESTER_OPTIONS), options);
62
+ function runBin(runOptions = DEFAULT_RUN_OPTIONS) {
63
+ return execa2(process.execPath, [mergedOptions.binPath, ...mergedOptions.staticArgs, ...runOptions.args], __spreadValues({
64
+ reject: false,
65
+ cwd: project.baseDir
66
+ }, runOptions.execaOptions));
67
+ }
68
+ async function setupProject() {
69
+ project = new mergedOptions.projectConstructor();
70
+ await project.write();
71
+ return project;
72
+ }
73
+ async function setupTmpDir() {
74
+ if (typeof project === "undefined") {
75
+ await setupProject();
76
+ }
77
+ return project.baseDir;
78
+ }
79
+ function teardownProject() {
80
+ project.dispose();
81
+ }
82
+ return {
83
+ runBin,
84
+ setupProject,
85
+ teardownProject,
86
+ setupTmpDir
87
+ };
88
+ }
89
+ export {
90
+ BinTesterProject,
91
+ createBinTester
92
+ };
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "@scalvert/bin-tester",
3
+ "version": "0.0.4",
4
+ "description": "A test harness to invoke a CLI in a tmp directory",
5
+ "keywords": [
6
+ "cli",
7
+ "fake",
8
+ "project",
9
+ "test",
10
+ "harness"
11
+ ],
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/scalvert/bin-tester.git"
15
+ },
16
+ "license": "MIT",
17
+ "author": "Steve Calvert <steve.calvert@gmail.com>",
18
+ "type": "module",
19
+ "exports": {
20
+ ".": {
21
+ "require": "./dist/index.cjs",
22
+ "import": "./dist/index.js",
23
+ "types": "./dist/index.d.ts"
24
+ }
25
+ },
26
+ "main": "./dist/index.cjs",
27
+ "module": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "scripts": {
30
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
31
+ "docs:generate": "npm run build && readme-api-generator dist/index.js",
32
+ "lint": "eslint .",
33
+ "prepublishOnly": "npm run build",
34
+ "test": "npm run lint && vitest run",
35
+ "test:watch": "vitest",
36
+ "watch": "npm run build -- --watch src"
37
+ },
38
+ "files": [
39
+ "dist"
40
+ ],
41
+ "dependencies": {
42
+ "execa": "^5.1.1",
43
+ "fixturify-project": "^4.1.0"
44
+ },
45
+ "devDependencies": {
46
+ "@scalvert/readme-api-generator": "^0.1.0",
47
+ "@typescript-eslint/eslint-plugin": "^5.14.0",
48
+ "@typescript-eslint/parser": "^5.14.0",
49
+ "eslint": "^8.10.0",
50
+ "eslint-config-prettier": "^8.5.0",
51
+ "eslint-plugin-node": "^11.1.0",
52
+ "eslint-plugin-prettier": "^4.0.0",
53
+ "eslint-plugin-tsdoc": "^0.2.14",
54
+ "eslint-plugin-unicorn": "^41.0.0",
55
+ "prettier": "^2.5.1",
56
+ "release-it": "^14.2.1",
57
+ "release-it-lerna-changelog": "^3.1.0",
58
+ "tsup": "^5.12.0",
59
+ "type-fest": "^2.12.0",
60
+ "typescript": "^4.6.2",
61
+ "vite": "^2.8.6",
62
+ "vitest": "^0.6.0"
63
+ },
64
+ "engines": {
65
+ "node": ">=14"
66
+ },
67
+ "publishConfig": {
68
+ "registry": "https://registry.npmjs.org",
69
+ "access": "public"
70
+ },
71
+ "release-it": {
72
+ "plugins": {
73
+ "release-it-lerna-changelog": {
74
+ "infile": "CHANGELOG.md",
75
+ "launchEditor": true
76
+ }
77
+ },
78
+ "git": {
79
+ "tagName": "v${version}"
80
+ },
81
+ "github": {
82
+ "release": true,
83
+ "tokenRef": "GITHUB_AUTH"
84
+ }
85
+ },
86
+ "volta": {
87
+ "node": "14.19.0",
88
+ "npm": "8.5.3"
89
+ }
90
+ }