@yankeeinlondon/typed-tester@0.13.2Built and signed on GitHub ActionsBuilt and signed on GitHub Actions
A Typescript testing package for your "type tests"
typed-tester
a CLI and Vite plugin which runs "type tests" for your Typescript projects
Install
You can install locally in a project or globally via npm:
# install globally npm install -g typed-tester
or
# install locally to a project pnpm install -D typed-tester
Usage
This library's primary utility is to provide a CLI to inspect your repo's type usage and most of all to provide an opinionated way of running type tests.
In addition to the CLI, it also exposes some type utilities as an ES Module export which are intended to be used to build your type tests.
CLI
The CLI has the following commands:
-
Type Testing:
[npx] typed test [filter]This is the primary utility for this repo will identify test files in your repo and run "type tests" on them. Runtime results are not a concern of this command, however, you can optionally "opt in" to get additional type errors or warnings outside of your core tests (although this is reserved to just the "test files").
By default this library will cache your file dependencies (aka, what symbols does your test file use, and then what is the graph of types which make up those symbols) with the goal of being able to watch and report on changes as you code. To run your testing in "watch mode" add the
--watchflag to the end of the command.The dependency caching will also cache an AST representation of all source files which were needed to complete your test coverage. This has valuable performance improvements but if you're ever feeling uncertain whether the cache is actually interfering with accurate results you can clear it at startup by adding the
--clearflag. Refer to the Cache Details section below for more details. -
Source Diagnostics:
[npx] typed diagnostics [filter] [--show-warnings] [--only-top=10]Evaluates all of the sources files and provides the following metrics by file:
- errors: type errors not reduced to warnings
- warnings: type errors specified to show as warnings (such as type defined but not used)
- performance metrics:
- AST build time: how long did it take to create an AST out of the file)
- Perf per Lines of Code: build time by lines of code
- Perf per Symbol: build time by symbols in file
-
Source Dependency Graph:
[npx] typed graph [filter]Produces a list of source type symbols and their dependencies on other symbols.
-
Type Coverage:
[npx] typed coverageProvides the following metrics:
- total number of symbols found in source code
- symbols tested directly (and coverage %)
- symbols tested directly or indirectly (and coverage %)
Note:
- These metrics aren't perfect but they're better than the absence of metrics
- The coverage relating to "directly or indirectly" means ...
- Where the symbol was directly tested (aka, there was a direct test that symbol result in an expected type) or (less ideally) that some other symbol used the type to calculate a tested outcome.
Type Utilities
The following type utilities can be imported from this library:
Expect/Test/IT- all tests will be wrapped with the
Expect/Test/ITutility; this utility will wrap one of the assertions listed below
Expect,Test, andITare identical but allow the developer to choose the nomenclature they prefer; in this repo we will useTest- all tests will be wrapped with the
AssertTrue<T>- tests whether the tested type
Tis the typetrue
- tests whether the tested type
AssertFalse<T>- tests whether the tested type
Tis the typefalse
- tests whether the tested type
AssertEqual<T,E>- tests that the tested type
Tequals the expected typeE
- tests that the tested type
AssertExtends<T,E>- tests that the tested type
Textends the expected typeE
- tests that the tested type
AssertSameValues<T,E>- tests that the tested type
Tis an array type and every element ofEandTare the same but the order in which they arrive does not matter
- tests that the tested type
AssertContains<T,E>- when the tested type
Tis astring:- this utility will pass when
Eis also astringand represents a sub-string of the sting literalT
- this utility will pass when
- when the tested type
Tis an array then:- this utility
- when the tested type
How to Write Type Tests
Type tests should be created for a repos "type utilities" as well as for most if not all of it's exported runtime functions.
-
obviously for a "type utility" the test file will only have type tests, but
-
anytime you're adding type tests for runtime symbols in your repo it is a good practice to intermingle runtime tests and type tests.
-
below is an example of intermingling type and runtime tests:
import { describe, it, expect } from "vitest"; import { Test, AssertTrue, AssertFalse } from "typed-tester"; import { isValidSyntax } from "src"; describe("isValidSyntax(val) -> boolean", () => { it("positive testing", () => { const t1 = isValidTest }) })Note: the example uses
vitestas test runner for runtime tests but you can use any test runner. Plus most test runners have some conception or corollary ofdescribeanditthat provide the structure to tests.
Details
Configuring "test files"
We attempt to find these for you by doing the following two things automatically:
- If you have a
vite.config.tsorvitest.config.tsfile we will use the glob patterns defined there to identify candidate files. - If neither of the two above files are present then we will use the default include pattern of:
{test,tests,src}/**/*.{test,spec}.ts
Finally, if the ENV variable TEST_FILES or VITE_TEST_FILES is set then we will use this value over everything else.
Cache Details
The cached data is stored in the root of the repo which you are testing and will appear in the following files:
-
.dependencies.json- is a JSON dictionary whose keys are the test files which were tested, they map to hash values which represent the symbol at a point in time. If that point in time is no longer valid (aka, the cache is stale) then this hash key will no longer be available in the.symbols.jsonlookup -
.symbols.jsonis a JSON dictionary whose keys are types in your source code and whose value corresponds to something similar to the following (refer toSymbolDefnin the source for an always current definition):export type SymbolDefn = { /** symbol name */ symbol: string; /** source file */ filepath: string; /** uses xxhash hasher to determine whether hosting file's contents have changed */ file_hash: string; /** uses xxhash hasher to determine whether the specific text representing the symbol has changed */ symbol_hash: string; /** serialized AST representation */ ast: string; }
To Git or Not to Git the Cache
Rather than state an opinion outright, I will say that users of this library should consider whether the "cache files" should go into the git repo or be included as part of the .gitignore. It really comes down to:
- your confidence that the caching algorithm's ability to detect stale data and re-cache when necessary
- the benefits of the increased performance of running off a cache (this will be larger and larger as your types increase in quantity and complexity)
Contributing and Reporting Issues
If you do find any issues with this library please consider making a pull request with a tested fix. If you're only going to enter an issue please be 100% sure that you've provided enough information to easily reproduce your issue (or just don't submit it and live with manual cache breaking).
License
MIT License
Copyright (c) 2024-PRESENT Ken Snyderhttps://github.com/yankeeinlondon
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Add Package
deno add jsr:@yankeeinlondon/typed-tester
Import symbol
import * as typed_tester from "@yankeeinlondon/typed-tester";
Import directly with a jsr specifier
import * as typed_tester from "jsr:@yankeeinlondon/typed-tester";
Add Package
pnpm i jsr:@yankeeinlondon/typed-tester
pnpm dlx jsr add @yankeeinlondon/typed-tester
Import symbol
import * as typed_tester from "@yankeeinlondon/typed-tester";
Add Package
yarn add jsr:@yankeeinlondon/typed-tester
yarn dlx jsr add @yankeeinlondon/typed-tester
Import symbol
import * as typed_tester from "@yankeeinlondon/typed-tester";
Add Package
vlt install jsr:@yankeeinlondon/typed-tester
Import symbol
import * as typed_tester from "@yankeeinlondon/typed-tester";
Add Package
npx jsr add @yankeeinlondon/typed-tester
Import symbol
import * as typed_tester from "@yankeeinlondon/typed-tester";
Add Package
bunx jsr add @yankeeinlondon/typed-tester
Import symbol
import * as typed_tester from "@yankeeinlondon/typed-tester";