The separate create-testers
package includes testing utilities that run Producers in fully virtualized environments.
This is intended for use in unit tests that should mock out all System Context .
testBase
For Bases , a testBase
function is exported that is analogous to produceBase
.
It takes in similar arguments:
base
(required) : a Base
settings
(optional) : production settings including simulated user-provided Options
For example, this test asserts that a Base defaults its value
option to "default"
when not provided:
import { testBase } from " create-testers " ;
import { describe, expect, it } from " vitest " ;
import { base } from " ./base " ;
describe ( " value " , () => {
it ( " defaults to 'default' when not provided " , async () => {
const actual = await testBase (base);
As with produceBase
, testBase
returns a Promise for the Base’s Options.
settings
and all its properties are optional.
However, some properties will cause testBase
to throw an error if they’re not provided and the Base attempts to use them:
options
: each property throws an error if accessed at all
take
: by default, throws an error if called as a function
options
Simulated user-provided Base Options may be provided under options
.
For example, this test asserts that a Base uses a value
if provided:
import { testBase } from " create-testers " ;
import { describe, expect, it } from " vitest " ;
import { base } from " ./base " ;
describe ( " value " , () => {
it ( " uses a provided value when it exists " , async () => {
const value = " override " ;
const actual = await testBase (base , {
expect (actual) . toEqual ({ value });
take
The Context take
function may be provided under take
.
This is how to simulate the results of Inputs .
For example, this test asserts that a Base defaults its name
to the property in package.json
:
import { testBase } from " create-testers " ;
import { describe, expect, it, vi } from " vitest " ;
import { base } from " ./base " ;
import { inputJsonFile } from " ./inputJsonFile " ;
it ( " uses the package.json name if it exists " , async () => {
const name = " create-create-app " ;
const take = vi . fn () . mockResolvedValue ( { name } );
const actual = await testBase (base , { take } );
expect (actual) . toEqual ({ name });
expect (take) . toHaveBeenCalledWith (inputJsonFile, " package.json " );
testBlock
For Blocks , a testBlocks
function is exported that is analogous to produceBlock
.
It takes in similar arguments:
block
(required) : a Block
settings
(optional) : production settings including the Block’s Options and any Args
For example, this test asserts that an nvmrc Block creates an ".nvmrc"
file with content "20.12.2"
:
import { testBlock } from " create-testers " ;
import { describe, expect, it } from " vitest " ;
import { blockNvmrc } from " ./blockNvmrc " ;
describe ( " blockNvmrc " , () => {
it ( " returns an .nvmrc " , async () => {
const actual = await testBlock (blockNvmrc);
files: { " .nvmrc " : " 20.12.2 " },
As with produceBlock
, testBlock
returns a Promise for the Block’s Creation .
Both Direct Creations and Indirect Creations will be present.
settings
and all its properties are optional.
However, some properties will cause testBlock
to throw an error if they’re not provided and the Block attempts to use them:
addons
: throws an error if accessed at all
options
: each property throws an error if accessed at all
addons
Block Addons may be provided under addons
.
For example, this test asserts that a Prettier block adds a useTabs
arg to its output ".prettierrc.json"
:
import { testBlock } from " create-testers " ;
import { describe, expect, expect, it } from " vitest " ;
import { base } from " ./base " ;
const blockPrettier = base . createBlock ( {
" .prettierrc.json " : JSON . stringify ( {
$schema: " http://json.schemastore.org/prettierrc " ,
describe ( " blockPrettier " , () => {
it ( " creates a .prettierrc.json when provided options " , async () => {
const actual = await testBlock (blockPrettier , {
" .prettierrc.json " : JSON . stringify ({
$schema: " http://json.schemastore.org/prettierrc " ,
options
Base Options may be provided under options
.
For example, this test asserts that a README.md uses the title
defined under options
:
import { testBlock } from " create-testers " ;
import { describe, expect, it } from " vitest " ;
import { base } from " ./base " ;
const blockReadme = base . createBlock ( {
" README.md " : ` # ${ options . title } ` ,
describe ( " blockDocs " , () => {
it ( " uses options.name for the README.md title " , async () => {
const actual = await testBlock (blockReadme , {
" README.md " : ` # My Project ` ,
For Inputs , a testInput
function is exported that is analogous to produceInput
.
It takes in similar arguments:
input
(required) : an Input
settings
(optional) : production settings including the Input’s Options and any Args
For example, this test asserts that an inputNow
returns a numeric timestamp:
import { testInput } from " create-testers " ;
import { describe, expect, it } from " vitest " ;
import { inputNow } from " ./inputNow " ;
describe ( " inputNow " , () => {
it ( " returns a numeric timestamp " , async () => {
const actual = await testInput (inputNow);
expect (actual) . toBeTypeOf ( " number " );
As with produceInput
, testInput
returns the data from the Input.
settings
and all its properties are optional.
However, some properties will cause testInput
to throw an error if they’re not provided and the Input attempts to use them:
args
: throws an error if accessed at all
fetcher
: by default, throws an error if called as a function
fs
: by default, each method throws an error if called as a function
runner
: by default, throws an error if called as a function
Input Args may be provided under args
.
import { testInput } from " create-testers " ;
import { describe, expect, it } from " vitest " ;
import { inputFromFile } from " ./inputFromFile.js " ;
describe ( " inputFromFile " , () => {
it ( " returns the file's text when it exists " , async () => {
const actual = await testInput (inputFromFile , {
readFile : () => Promise . resolve (text) ,
expect (actual) . toBe (text);
A mock function to act as the global fetch
.
For example, this test asserts that an inputCatFact
Input returns the fact
property of a response:
import { testInput } from " create-testers " ;
import { describe, expect, it, vi } from " vitest " ;
import { inputCatFact } from " ./inputCatFact " ;
describe ( " inputCatFact " , () => {
it ( " returns the cat fact from the API " , async () => {
" Owning a cat is actually proven to be beneficial for your health. " ;
const fetcher = vi . fn () . mockResolvedValueOnce ( {
json : () => Promise . resolve ( { fact } ) ,
const actual = await testInput (inputCatFact , { fetcher } );
expect (actual) . toEqual (fact);
expect (fetcher) . toHaveBeenCalledWith ( " https://catfact.ninja/fact " );
An object containing mocks to act as a file system.
For example, this test asserts that an inputFromFile
input returns the text of a file from disk:
import { testInput } from " create-testers " ;
import { describe, expect, it, vi } from " vitest " ;
import { inputFromFile } from " ./inputCatFact " ;
describe ( " inputFromFile " , () => {
it ( " returns the contents of a file " , async () => {
const contents = " abc123 " ;
const readFile = vi . fn () . mockResolvedValue (contents);
const actual = await testInput (inputFromFile , {
args: { fileName: " text.txt " },
expect (actual) . toEqual (contents);
expect (readFile) . toHaveBeenCalledWith ( " text.txt " );
A mock function to act as execa
.
For example, this test asserts that an inputGitUserEmail
Input returns the text from running git config user.email
:
import { testInput } from " create-testers " ;
import { describe, expect, it, vi } from " vitest " ;
import { inputGitUserEmail } from " ./inputGitUserEmail " ;
describe ( " inputGitUserEmail " , () => {
it ( " returns text from git config user.email " , async () => {
const email = " rick.astley@example.com " ;
const runner = vi . fn () . mockResolvedValueOnce ( {
const actual = await testInput (inputGitUserEmail , { runner } );
expect (actual) . toEqual (email);
expect (runner) . toHaveBeenCalledWith ( " git config user.email " );
The Context take
function may be provided under take
.
This is how to simulate the results of calling to other Inputs .
For example, this test asserts that an inputNpmUsername
Input uses the result of an inputNpmWhoami
Input:
import { testInput } from " create-testers " ;
import { describe, expect, it, vi } from " vitest " ;
import { inputNpmUsername } from " ./inputNpmUsername " ;
import { inputNpmWhoami } from " ./inputNpmWhoami " ;
describe ( " inputNpmUsername " , () => {
it ( " uses the result of npm whoami when available " , async () => {
const username = " joshuakgoldberg " ;
const take = vi . fn () . mockResolvedValue ( {
const actual = await testInput (inputNpmUsername);
expect (actual) . toBe (username);
expect (take) . toHaveBeenCalledWith (inputNpmWhoami);
testPreset
For Presets , a testPreset
function is exported that is analogous to producePreset
.
It takes in similar arguments:
preset
(required) : a Preset
settings
(optional) : production settings including the Preset’s Options and any Args
For example, this test asserts that a Preset using an nvmrc Block creates an ".nvmrc"
file with content equal to its version
option:
import { testPreset } from " create-testers " ;
import { describe, expect, it } from " vitest " ;
import { presetWithNvmrc } from " ./presetWithNvmrc " ;
describe ( " presetWithNvmrc " , () => {
it ( " returns an .nvmrc " , async () => {
const actual = await testPreset (presetWithNvmrc , {
files: { " .nvmrc " : " 20.12.2 " },
As with producePreset
, testPreset
returns a Promise for the Preset’s Creation .
Both Direct Creations and Indirect Creations will be present.
settings
and all its properties are optional.
However, some properties will cause testPreset
to throw an error if they’re not provided and the Block attempts to use them:
fetcher
: by default, throws an error if called as a function
fs
: by default, each method throws an error if called as a function
runner
: by default, throws an error if called as a function
fetcher
A mock function to act as the global fetch
.
For example, this test asserts that a Preset internally fetches cat facts from an API and stores them in a file:
import { testPreset } from " create-testers " ;
import { describe, expect, it, vi } from " vitest " ;
import { presetWithCatFact } from " ./presetWithCatFact " ;
describe ( " presetWithCatFact " , () => {
it ( " prints the cat fact from the API in fact.txt file " , async () => {
" Owning a cat is actually proven to be beneficial for your health. " ;
const fetcher = vi . fn () . mockResolvedValueOnce ( {
json : () => Promise . resolve ( { fact } ) ,
const actual = await testPreset (presetWithCatFact , { fetcher } );
expect (fetcher) . toHaveBeenCalledWith ( " https://catfact.ninja/fact " );
fs
An object containing mocks to act as a file system.
For example, this test asserts that a Preset internally copies a backup.txt
file to an current.txt
file:
import { testPreset } from " create-testers " ;
import { describe, expect, it, vi } from " vitest " ;
import { presetWithBackup } from " ./inputCatFact " ;
describe ( " presetWithBackup " , () => {
it ( " copies backup.txt to current.txt when backup.txt exists " , async () => {
const contents = " abc123 " ;
const readFile = vi . fn () . mockResolvedValue (contents);
const actual = await testPreset (presetWithBackup , {
expect (readFile) . toHaveBeenCalledWith ( " backup.txt " );
runner
A mock function to act as execa
.
For example, this test asserts that Preset includes the running user’s email in an AUTHORS.md
file:
import { testPreset } from " create-testers " ;
import { describe, expect, it, vi } from " vitest " ;
import { presetAuthorship } from " ./presetAuthorship " ;
describe ( " presetAuthorship " , () => {
it ( " puts the running user's git config user.email in AUTHORS.md " , async () => {
const email = " rick.astley@example.com " ;
const runner = vi . fn () . mockResolvedValueOnce ( {
const actual = await testPreset (presetAuthorship , { runner } );
expect (runner) . toHaveBeenCalledWith ( " git config user.email " );