Skip to content

Commit

Permalink
test(cli): Setup demo test framework
Browse files Browse the repository at this point in the history
  • Loading branch information
grypez committed Aug 14, 2024
1 parent cd6378d commit 9bcb8c7
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 2 deletions.
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-import": "^2.29.1",
"execa": "^9.3.0",
"prettier": "^3.2.5",
"typescript": "5.5.2"
},
Expand Down
18 changes: 18 additions & 0 deletions packages/cli/test/daemon-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/** @import { Context } from './types' */

/**
* Provides test setup and teardown hooks that purge the local endo
* daemon. In the future, we should create isolated daemon instances
* so that tests can be run in parallel.
*
* @type {Context}
*/
export const daemonContext = {
setup: async execa => {
await execa`endo purge -f`;
await execa`endo start`;
},
teardown: async execa => {
await execa`endo purge -f`;
},
};
16 changes: 16 additions & 0 deletions packages/cli/test/demo/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import test from 'ava';
import { $ } from 'execa';
import { makeSectionTest } from '../section.js';
import { withContext } from '../with-context.js';
import { daemonContext } from '../daemon-context.js';

test.serial(
'trivial',
makeSectionTest(
$({ cwd: 'demo' }),
withContext(daemonContext)(async (execa, testLine) => {
const maxim = 'a failing test is better than failure to test';
await testLine(execa`echo ${maxim}`, { stdout: maxim });
}),
),
);
32 changes: 32 additions & 0 deletions packages/cli/test/section.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/** @import {Execa} from 'execa' */
/** @import {t} from 'ava' */
/** @import {TestRoutine} from './types' */

/**
* Transforms a testRoutine into an ava test.
* The testCommand function asserts that a given awaitable command produces the expected stdout and stderr.
*
* @param {Execa} execa - the command execution environment
* @param {TestRoutine} testRoutine - the test logic implementation
* @returns {(t: t) => Promise<void>}
*/
export function makeSectionTest(execa, testRoutine) {
return async t => {
const matchExpecation = (expectation, result, errMsg) => {
(expectation instanceof RegExp ? t.regex : t.is)(
result,
expectation ?? '',
errMsg,
);
};
const testCommand = async (command, expectation) => {
const result = await command;
if (expectation !== undefined) {
const errMsg = JSON.stringify({ expectation, result }, null, 2);
matchExpecation(expectation.stdout, result.stdout, errMsg);
matchExpecation(expectation.stderr, result.stderr, errMsg);
}
};
await testRoutine(execa, testCommand);
};
}
18 changes: 18 additions & 0 deletions packages/cli/test/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { Execa } from 'execa';

export type Expectation = {
stdout: RegExp | string | undefined;
stderr: RegExp | string | undefined;
};
export type TestCommand = (
command: ReturnType<Execa>,
expectation: Expectation,
) => Promise<true>;
export type TestRoutine = (
execa: Execa,
testCommnd: TestCommand,
) => Promise<void>;
export type Context = {
setup: (execa: Execa) => Promise<void>;
teardown?: (execa: Execa) => Promise<void>;
};
50 changes: 50 additions & 0 deletions packages/cli/test/with-context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/** @import {Context, TestRoutine} from '../types' */
/**
* Creates a wrapper which wraps a test routine with an execa-curried setup and teardown routine.
*
* @param {Context} context
* @returns {(testRoutine: TestRoutine) => TestRoutine}
*/
const makeContextWrapper =
context => testRoutine => async (execa, testCommand) => {
await null;
try {
await context.setup(execa);
await testRoutine(execa, testCommand);
} finally {
await context.teardown?.(execa);
}
};

/**
* Creates a wrapper which wraps a test routine with execa-curried setup and teardown routines.
*
* Context args are provided from outermost to inner-most wrapping, e.g.
*
* ```
* await makeContextWrappers(a, b)(c);
* ```
*
* is approximately equivalent to
*
* ```
* for ( var f of [a.setup, b.setup, c, b.teardown, a.teardown] ) {
* await f();
* }
* ```
*
* with the important distinction that teardown routines execute as long as their corresponding
* setup was called, even if that setup or any calls in between failed.
*
* @param {...Context} contexts - the conjugations to be applied onion-wise
* @returns {(testRoutine: TestRoutine) => TestRoutine}
*/
export const withContext =
(...contexts) =>
testRoutine => {
let composition = testRoutine;
for (const context of contexts.reverse()) {
composition = makeContextWrapper(context)(composition);
}
return composition;
};
93 changes: 91 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,7 @@ __metadata:
eslint-config-prettier: "npm:^9.1.0"
eslint-plugin-eslint-comments: "npm:^3.2.0"
eslint-plugin-import: "npm:^2.29.1"
execa: "npm:^9.3.0"
open: "npm:^9.1.0"
prettier: "npm:^3.2.5"
ses: "npm:^1.7.0"
Expand Down Expand Up @@ -2582,6 +2583,13 @@ __metadata:
languageName: node
linkType: hard

"@sec-ant/readable-stream@npm:^0.4.1":
version: 0.4.1
resolution: "@sec-ant/readable-stream@npm:0.4.1"
checksum: 10c0/64e9e9cf161e848067a5bf60cdc04d18495dc28bb63a8d9f8993e4dd99b91ad34e4b563c85de17d91ffb177ec17a0664991d2e115f6543e73236a906068987af
languageName: node
linkType: hard

"@sinclair/typebox@npm:^0.25.16":
version: 0.25.24
resolution: "@sinclair/typebox@npm:0.25.24"
Expand All @@ -2596,6 +2604,13 @@ __metadata:
languageName: node
linkType: hard

"@sindresorhus/merge-streams@npm:^4.0.0":
version: 4.0.0
resolution: "@sindresorhus/merge-streams@npm:4.0.0"
checksum: 10c0/482ee543629aa1933b332f811a1ae805a213681ecdd98c042b1c1b89387df63e7812248bb4df3910b02b3cc5589d3d73e4393f30e197c9dde18046ccd471fc6b
languageName: node
linkType: hard

"@sinonjs/commons@npm:^2.0.0":
version: 2.0.0
resolution: "@sinonjs/commons@npm:2.0.0"
Expand Down Expand Up @@ -5642,6 +5657,26 @@ __metadata:
languageName: node
linkType: hard

"execa@npm:^9.3.0":
version: 9.3.0
resolution: "execa@npm:9.3.0"
dependencies:
"@sindresorhus/merge-streams": "npm:^4.0.0"
cross-spawn: "npm:^7.0.3"
figures: "npm:^6.1.0"
get-stream: "npm:^9.0.0"
human-signals: "npm:^7.0.0"
is-plain-obj: "npm:^4.1.0"
is-stream: "npm:^4.0.1"
npm-run-path: "npm:^5.2.0"
pretty-ms: "npm:^9.0.0"
signal-exit: "npm:^4.1.0"
strip-final-newline: "npm:^4.0.0"
yoctocolors: "npm:^2.0.0"
checksum: 10c0/99ae08e7fb9172d25c453c2a9c414b54c7689e72f68263f6da7bc94c7011720dc8129cc64c2e3be44fb0c6ae8e37a08d346a61dbcfe9f1e79ad24364da2c48ce
languageName: node
linkType: hard

"execution-time@npm:^1.2.0":
version: 1.4.1
resolution: "execution-time@npm:1.4.1"
Expand Down Expand Up @@ -5814,7 +5849,7 @@ __metadata:
languageName: node
linkType: hard

"figures@npm:^6.0.1":
"figures@npm:^6.0.1, figures@npm:^6.1.0":
version: 6.1.0
resolution: "figures@npm:6.1.0"
dependencies:
Expand Down Expand Up @@ -6204,6 +6239,16 @@ __metadata:
languageName: node
linkType: hard

"get-stream@npm:^9.0.0":
version: 9.0.1
resolution: "get-stream@npm:9.0.1"
dependencies:
"@sec-ant/readable-stream": "npm:^0.4.1"
is-stream: "npm:^4.0.1"
checksum: 10c0/d70e73857f2eea1826ac570c3a912757dcfbe8a718a033fa0c23e12ac8e7d633195b01710e0559af574cbb5af101009b42df7b6f6b29ceec8dbdf7291931b948
languageName: node
linkType: hard

"get-symbol-description@npm:^1.0.0":
version: 1.0.0
resolution: "get-symbol-description@npm:1.0.0"
Expand Down Expand Up @@ -6758,6 +6803,13 @@ __metadata:
languageName: node
linkType: hard

"human-signals@npm:^7.0.0":
version: 7.0.0
resolution: "human-signals@npm:7.0.0"
checksum: 10c0/ce0c6d62d2e9bfe529d48f7c7fdf4b8c70fce950eef7850719b4e3f5bc71795ae7d61a3699ce13262bed7847705822601cc81f1921ea6a2906852e16228a94ab
languageName: node
linkType: hard

"humanize-ms@npm:^1.2.1":
version: 1.2.1
resolution: "humanize-ms@npm:1.2.1"
Expand Down Expand Up @@ -7354,6 +7406,13 @@ __metadata:
languageName: node
linkType: hard

"is-plain-obj@npm:^4.1.0":
version: 4.1.0
resolution: "is-plain-obj@npm:4.1.0"
checksum: 10c0/32130d651d71d9564dc88ba7e6fda0e91a1010a3694648e9f4f47bb6080438140696d3e3e15c741411d712e47ac9edc1a8a9de1fe76f3487b0d90be06ac9975e
languageName: node
linkType: hard

"is-plain-object@npm:^2.0.3, is-plain-object@npm:^2.0.4":
version: 2.0.4
resolution: "is-plain-object@npm:2.0.4"
Expand Down Expand Up @@ -7428,6 +7487,13 @@ __metadata:
languageName: node
linkType: hard

"is-stream@npm:^4.0.1":
version: 4.0.1
resolution: "is-stream@npm:4.0.1"
checksum: 10c0/2706c7f19b851327ba374687bc4a3940805e14ca496dc672b9629e744d143b1ad9c6f1b162dece81c7bfbc0f83b32b61ccc19ad2e05aad2dd7af347408f60c7f
languageName: node
linkType: hard

"is-string@npm:^1.0.5, is-string@npm:^1.0.7":
version: 1.0.7
resolution: "is-string@npm:1.0.7"
Expand Down Expand Up @@ -9077,6 +9143,15 @@ __metadata:
languageName: node
linkType: hard

"npm-run-path@npm:^5.2.0":
version: 5.3.0
resolution: "npm-run-path@npm:5.3.0"
dependencies:
path-key: "npm:^4.0.0"
checksum: 10c0/124df74820c40c2eb9a8612a254ea1d557ddfab1581c3e751f825e3e366d9f00b0d76a3c94ecd8398e7f3eee193018622677e95816e8491f0797b21e30b2deba
languageName: node
linkType: hard

"npmlog@npm:^5.0.1":
version: 5.0.1
resolution: "npmlog@npm:5.0.1"
Expand Down Expand Up @@ -10678,7 +10753,7 @@ __metadata:
languageName: node
linkType: hard

"signal-exit@npm:^4.0.1":
"signal-exit@npm:^4.0.1, signal-exit@npm:^4.1.0":
version: 4.1.0
resolution: "signal-exit@npm:4.1.0"
checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83
Expand Down Expand Up @@ -11223,6 +11298,13 @@ __metadata:
languageName: node
linkType: hard

"strip-final-newline@npm:^4.0.0":
version: 4.0.0
resolution: "strip-final-newline@npm:4.0.0"
checksum: 10c0/b0cf2b62d597a1b0e3ebc42b88767f0a0d45601f89fd379a928a1812c8779440c81abba708082c946445af1d6b62d5f16e2a7cf4f30d9d6587b89425fae801ff
languageName: node
linkType: hard

"strip-indent@npm:^2.0.0":
version: 2.0.0
resolution: "strip-indent@npm:2.0.0"
Expand Down Expand Up @@ -12521,3 +12603,10 @@ __metadata:
checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f
languageName: node
linkType: hard

"yoctocolors@npm:^2.0.0":
version: 2.1.1
resolution: "yoctocolors@npm:2.1.1"
checksum: 10c0/85903f7fa96f1c70badee94789fade709f9d83dab2ec92753d612d84fcea6d34c772337a9f8914c6bed2f5fc03a428ac5d893e76fab636da5f1236ab725486d0
languageName: node
linkType: hard

0 comments on commit 9bcb8c7

Please sign in to comment.