Docs

Run

Configuration reference

The fields of testkit.config.ts in Testkit 0.4.1. The file lives at the repo root and holds the repo's test policy.

View as Markdown

On this page
  1. The file
  2. Top-level fields
  3. execution
  4. services
  5. profiles.http
  6. ui
  7. lint
  8. areas
  9. bugs
  10. artifacts
  11. fingerprints
  12. toolchains
  13. Services
  14. app.node(...)
  15. app.next(...)
  16. Service fields
  17. env
  18. envFile and envFiles
  19. database
  20. databaseFrom
  21. dependsOn
  22. runtime
  23. skip
  24. requirements
  25. discovery
  26. Template tokens
  27. Environment limits

The file

testkit.config.ts
import { app, defineConfig } from "@gfdelarue/testkit/config";

export default defineConfig({
	services: {
		api: app.node({
			build: null,
			start: "npm start",
			port: 4392,
		}),
	},
});

Import the builders from @gfdelarue/testkit/config.

ExportReturns
defineConfig(config)The config, typed
app.node(options)A Node service
app.next(options)A Next.js service
database.postgres(options)A Postgres database for a service
database.fixture(options)A service that is only a database
toolchain.node(options)A rule for the Node and npm versions

Testkit 0.4.1 ignores a top-level key that it does not know, and prints no warning. A skip block placed beside services does nothing. To confirm that a rule took effect, run testkit discover and read the skipped count.

Top-level fields

execution

FieldDefaultMeaning
taskConcurrency1The most test tasks that run at once. --task-concurrency overrides it.
fileTimeoutSeconds60The wall-clock limit for each file. waitFor() spends from the same limit.
execution: {
	taskConcurrency: 4,
	fileTimeoutSeconds: 120,
},

services

A map from a service name to a service. The name appears in run output, and --service takes it. Build each service with app.node(...), app.next(...), or database.fixture(...).

profiles.http

Named k6 options. An HTTP test selects one with defineHttpSuite({ profile: "raw" }, ...).

profiles: {
	http: {
		raw: {
			options: { vus: 1, iterations: 1 },
		},
	},
},

When a run selects a k6 file, every profile must be plain JSON. A profile that holds a function fails config validation. Put auth and header functions in the test file or in a helper beside it.

ui

Targets for browser tests.

FieldDefaultMeaning
frontendBaseUrlThe run's base URLThe browser target
backendBaseUrlNoneThe API target for createAuthedApiClient
allowRemotefalsePermit a target that is not on loopback

Options that a test passes to createUiSandbox win over this block.

lint

Settings for testkit lint. Omit the block to run the default rules.

RuleDefault
engineImportsOn
configImportsOn
dataSuiteContractOn
dalParallelSafetyOn
uiSpecShapeOn
unitTestBoundariesOff
testkitBoundaryOff

Set a rule to true, false, or an options object. The options are enabled, include, and exclude. testkitBoundary also takes allowed and trackedOnly.

lint.ui sets the limits for browser test files.

FieldDefaultMeaning
ui.maxLines220The most lines in one ui file
ui.maxTests8The most tests in one ui file
lint: {
	rules: {
		unitTestBoundaries: true,
	},
	ui: { maxLines: 300 },
},

A finding from an enabled rule fails the command. There are no severity levels.

areas

A map from an area id to a named part of the product. testkit bug file uses it to name the area that holds a fault, and puts an area:<id> label on the issue.

FieldMeaning
The keyThe area id. It uses lowercase letters, digits, and single hyphens, and has at most 40 characters.
nameOne line of 1 to 60 characters. It becomes the label's description.
pathsAt least one glob, relative to the repo root and written with /. Testkit rejects a leading !, a leading /, and a .. segment.
areas: {
	checkout: { name: "Checkout", paths: ["src/checkout/**"] },
	billing: { name: "Billing", paths: ["src/billing/**", "src/invoices/**"] },
},

Each source file belongs to one area at most. testkit campaign start lists the source files that no area claims and the files that two areas claim. testkit bug file stops when no area claims the fault's file, or when two areas do, and names the file. Without areas, Testkit files bugs with no area.

An area's share is its part of the repo's source bytes at the commit that holds the bug. Tests, generated files, vendored code, and migrations do not count as source.

bugs

Settings for bug filing, and for runs that meet a filed bug.

FieldDefaultMeaning
knownFailures"fail""fail" or "allow". With "fail", every failing test fails the run. With "allow", a run passes when an open issue with the testkit label covers each failure.
repositoryThe origin remoteThe repo that holds the bug issues. Write owner/name for a repo on the same host as the origin remote, or a URL such as https://host/owner/name.
bugs: {
	knownFailures: "allow",
	repository: "acme/shop-bugs",
},

Known failures gives the rule for knownFailures. With "allow", the run reads the open issues, so the testkit run step in CI needs a token.

testkit bug file files issues in repository, and testkit run and testkit ci report read the open issues from it. Commit statuses and pull request comments always go to the origin repo. If a Turnstone project reads this repo's bugs, enter the same repo in the project.

artifacts

FieldMeaning
retention.maxRunsThe most runs to keep
retention.maxAgeDaysThe oldest run to keep, in days
retention.preserveSuitesSuites whose runs retention does not remove, such as ui:performance
publication.directoryA directory that Testkit copies each finished run into
publication.requiredWhen true, a failed copy fails the command. The local run stays in place.

With no retention block, Testkit keeps every run. It records retention decisions under .testkit/results/retention and each publication under .testkit/results/publications.

fingerprints

Testkit hashes the repo's files to decide when a cache is stale. exclude removes generated files from the hash, and include puts a path back below an excluded directory.

fingerprints: {
	exclude: ["next-env.d.ts"],
},

Patterns use / as the only separator. Testkit always excludes .testkit/ and .next-testkit/.

toolchains

Named rules for the Node and npm versions that run a service's commands. A service selects one with runtime.toolchain.

toolchains: {
	frontendNode: toolchain.node({
		cwd: "frontend",
		detect: "auto",
		install: "download",
	}),
},
OptionValuesMeaning
cwdA path, relative to the repo rootThe directory that holds the version files
detect"auto", "off""auto" reads the versions from the repo
install"require-host", "download""download" fetches Node into .testkit/_toolchains/node and checks it against a trusted release manifest. It supports glibc hosts only.
node, npmA version or a rangeThe version to use

With detect: "auto", Testkit reads the Node version from package.json#volta.node, .nvmrc, .node-version, .tool-versions, and package.json#engines.node. It reads the npm version from package.json#volta.npm, package.json#packageManager, and package.json#engines.npm.

Services

app.node(...)

OptionMeaning
portRequired. The port the service listens on. Testkit passes it to the process as PORT.
startThe command that starts the service
entryThe service's entry file, such as src/index.ts
buildHow Testkit builds the service. Set null when the service needs no build.
cwdThe service's directory, relative to the repo root
env{ values, databases }. See env.
toolchainA name from toolchains, or an inline toolchain.node(...)
shutdownTimeoutMsHow long the service gets to exit before Testkit kills it. The default is 1000 ms.

app.node also accepts baseUrl, buildInputs, outDir, readyPath, readyTimeoutMs, readyUrl, runtime, and tsconfig, and every field under Service fields. The types in @gfdelarue/testkit/config define each one.

app.next(...)

Takes the same options as app.node, except entry, outDir, readyPath, and tsconfig. It adds mode, which is "dev" or "start", and api.

frontend: app.next({
	cwd: "frontend",
	mode: "start",
	port: 3000,
	dependsOn: ["api"],
	env: {
		values: { NEXT_PUBLIC_API_URL: "{baseUrl:api}" },
	},
}),

Service fields

env

Environment values for the service process and for every test that targets the service. app.node and app.next take { values, databases }.

Values can use template tokens.

databases expands one service's database into prefixed entries.

env: {
	databases: { api: { prefix: "API", service: "api" } },
},

That block sets API_DATABASE_HOST, API_DATABASE_PORT, API_DATABASE_NAME, API_DATABASE_USER, API_DATABASE_PASSWORD, and API_DATABASE_SSL. An entry in values with the same name wins.

A test reads the values from its engine's context.

EngineWhere the values are
k6__ENV
node-vitestcontext.env, read-only
playwrightreadPlaywrightContext()?.env

envFile and envFiles

Env files to load into the same environment as env: envFile takes one path, and envFiles takes a list.

database

database.postgres(...) gives the service a local Postgres in Docker. The service process receives it as DATABASE_URL. Test the database has a working example.

OptionMeaning
template.inputsFiles that rebuild the template when they change
template.migrateSteps that build the schema
template.seedSteps that add data
template.verifySteps that must pass before Testkit caches the template
binding"shared" reuses one database. "per-runtime" clones the template for each runtime instance.
runtimeConnectionsThe connection budget of one runtime instance
maxConnections, reservedConnectionsThe capacity of the local Postgres, which the scheduler uses to admit tasks
rolesRoles to create, each as { name, login: false }
sourceSchemaA source database to replay the schema from, such as database.schema.fromEnv("PRODUCTION_DATABASE_URL")
imageThe Postgres image

A template stage is an ordered list of steps.

StepExample
A command{ kind: "command", run: "npm run db:seed" }
An SQL file{ kind: "sql-file", path: "db/schema.sql" }
A module export{ kind: "module", target: "src/testkit/verify-seed.ts#verifySeed" }

Docker also runs psql and pg_dump. To use another client image, set TESTKIT_POSTGRES_CLIENT_IMAGE.

databaseFrom

The name of another service. This service receives that service's DATABASE_URL.

dependsOn

The services that must start first.

runtime

FieldMeaning
instancesHow many copies of the service Testkit keeps running
maxConcurrentTasksThe most test files that one instance serves at once. Omit it to leave taskConcurrency as the only limit.
toolchainA name from toolchains, or an inline toolchain.node(...)
prepare.stepsSteps that run once per instance before the service starts. Each step takes cwd and inputs.
prepare.inputsFiles that rerun the steps when they change
runtime: {
	instances: 1,
	maxConcurrentTasks: 4,
	prepare: {
		inputs: ["frontend/src", "frontend/package.json"],
		steps: [{ kind: "command", run: "npm run build", cwd: "frontend" }],
	},
},

skip

Skip rules for the service. Every rule needs a reason.

services: {
	api: app.node({
		port: 4392,
		skip: {
			files: [
				{
					path: "src/checkout/__testkit__/payout.contract.testkit.ts",
					reason: "The payout sandbox is down until Friday",
				},
			],
			suites: [
				{ selector: "ui:billing", reason: "Billing UI is a placeholder" },
			],
		},
	}),
},
FieldMeaning
files[].pathAn exact path, relative to the repo root
suites[].selectorA suite selector, such as ui:billing
reasonRequired. The run prints it and the run record keeps it.

To skip one file, prefer defineFile({ skip }) in the file itself. testkit run --ignore-skip-rules runs skipped files anyway.

requirements

Lock rules for the service. Files that need the same lock do not run at the same time.

requirements: {
	suites: [{ selector: "ui:billing", locks: ["billing-ledger"] }],
	files: [
		{
			path: "src/jobs/__testkit__/worker.int.testkit.ts",
			locks: ["global-worker-loop"],
		},
	],
},

To lock one file, prefer defineFile({ locks }).

discovery

discovery.roots assigns directories to the service. Testkit normally gives a test file to the service with the deepest matching cwd, so set roots only when two services share a directory.

Template tokens

Strings in a service can hold these tokens.

TokenValue
{service}The service's name
{runtime}The runtime instance
{port}, {port:name}The port of this service, or of the service name
{baseUrl}, {baseUrl:name}The base URL
{readyUrl}, {readyUrl:name}The readiness URL
{stateDir}, {prepareDir}, {lease}, {leaseDir}Testkit's directories and lease for the instance

These tokens work only in env values.

TokenValue
{dbUrl}The connection URL
{dbHost}, {dbPort}The host and the host port
{dbName}, {dbUser}, {dbPassword}The database name, user, and password
{dbSsl}1 when the connection uses SSL, and 0 when it does not

A bare database token reads the service's own database. {dbUrl:api} reads the database of the service api, which must be in dependsOn.

Testkit rejects a database token in start, cwd, runtime.prepare, and database.template strings. In those places, read DATABASE_URL when the command runs.

Environment limits

Each engine reserves some names and limits how much environment a test can receive.

EngineReserved prefixesReserved namesLimit
k6K6_, TESTKIT_BASE_URL, GODEBUG128 entries and 16,384 environment-block units
node-vitestTESTKIT_, VITEST_BASE_URL, FORCE_COLOR, NODE_OPTIONS, NO_COLOR, UPDATE_SNAPSHOT512 entries and 4 MiB
playwrightTESTKIT_, PLAYWRIGHT_, PWTEST_, PW_TEST_BASE_URL, FORCE_COLOR, NODE_OPTIONS, NO_COLOR512 entries and 2 MiB

Every engine also reserves APPDATA, ComSpec, HOME, LANG, LC_ALL, LOCALAPPDATA, PATH, PATHEXT, SystemRoot, TEMP, TMP, TMPDIR, USERPROFILE, XDG_CACHE_HOME, XDG_CONFIG_HOME, and XDG_DATA_HOME.

Testkit compares names without regard to case. It rejects an empty name, a name that contains =, and two names that differ only in case.

  1. OverviewStartWhat Testkit is, and what you need to run it.
  2. Get startedStartFive steps from install to a merge gate.
  3. Test filesWrite testsWhere a test lives and what its name means.
  4. Contract testsWrite testsCall your running service from Node.
  5. HTTP testsWrite testsDrive routes with k6, signed in as named actors.
  6. Browser testsWrite testsAssert on a real page in Chromium.
  7. Database testsWrite testsRun SQL against a Postgres that Testkit builds.
  8. CIRunOne command decides whether a pull request merges.
  9. ResultsRunWhere a run's evidence lands and how to read it.
  10. CLIRunEvery command and flag.
  11. ConfigurationRunEvery field of testkit.config.ts.
  12. CampaignsHunt bugsGive your agent a target and collect proven bugs.
  13. Bug reportsHunt bugsThe five things every issue states.
  14. TurnstoneTurnstoneWhat the dashboard keeps, and what it never stores.
  15. Connect GitHubTurnstoneConnect a repository, and see the access Turnstone asks for.
  16. Connect GiteaTurnstoneAdd your Gitea host, then connect a repository.
  17. SyncTurnstoneWhen Turnstone reads your issues, and what it reads.
  18. FactsTurnstoneEach fact about a bug, and where Turnstone reads it.