-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test(cli): Setup demo test framework
- Loading branch information
Showing
7 changed files
with
226 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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`; | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }); | ||
}), | ||
), | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters