@typesugar/testing
v0.1.0
Published
π§ Compile-time testing macros β power assertions, property-based testing, type assertions
Maintainers
Readme
@typesugar/testing
Compile-time testing macros β power assertions, property-based testing, and more.
Overview
@typesugar/testing provides compile-time testing superpowers: power assertions with sub-expression capture, compile-time assertions that fail the build, parameterized tests, snapshot testing with source capture, and property-based testing.
Installation
npm install @typesugar/testing
# or
pnpm add @typesugar/testingQuick Start
import {
assert, // Power assertions with sub-expression capture
staticAssert, // Compile-time assertions (fail BUILD, not test)
typeAssert, // Type-level assertions
type Equal, // Type equality check
type Extends, // Type extends check
} from "@typesugar/testing";
// Runtime assertion β captures sub-expression values on failure
assert(users.length === expected.length);
// Compile-time assertion β fails the BUILD if false
staticAssert(CONFIG.TIMEOUT > 0, "timeout must be positive");
// Type assertion β verifies types at compile time
typeAssert<Equal<ReturnType<typeof parse>, AST>>();Usage
assert() β Power Assertions
On failure, shows the value of every sub-expression.
import { assert } from "@typesugar/testing";
assert(users.length === filtered.length);
// On failure:
// Power Assert Failed
//
// users.length === filtered.length
//
// Sub-expressions:
// users.length === filtered.length β false
// users.length β 3
// users β [{...}, {...}, {...}]
// filtered.length β 2
// filtered β [{...}, {...}]staticAssert() β Compile-Time Assertions
Fail the BUILD, not the test. Zero runtime cost.
import { staticAssert } from "@typesugar/testing";
staticAssert(3 + 4 === 7, "basic math must work");
staticAssert(SUPPORTED_LOCALES.length > 0, "must have locales");
// If false: BUILD FAILS with the message
// If true: No runtime cost (expands to void 0)typeAssert() β Type-Level Assertions
Verify type relationships at compile time.
import { typeAssert, Equal, Extends } from "@typesugar/testing";
typeAssert<Equal<1 + 1, 2>>();
typeAssert<Extends<"hello", string>>();
typeAssert<Equal<ReturnType<typeof parse>, AST>>();
// Build fails if type doesn't match@testCases() β Parameterized Tests
Expand one test function into multiple cases.
import { testCases } from "@typesugar/testing";
@testCases([
{ input: "", expected: true },
{ input: "hello", expected: false },
{ input: " ", expected: true },
])
function testIsBlank(input: string, expected: boolean) {
expect(isBlank(input)).toBe(expected);
}
// Expands to:
// it('testIsBlank (case #1: input="", expected=true)', ...)
// it('testIsBlank (case #2: input="hello", expected=false)', ...)
// it('testIsBlank (case #3: input=" ", expected=true)', ...)assertSnapshot() β Source-Capturing Snapshots
Snapshot testing with compile-time source capture.
import { assertSnapshot } from "@typesugar/testing";
assertSnapshot(formatUser(testUser));
// Label: "file.ts:42 β formatUser(testUser)"
assertSnapshot(renderComponent(props), "dark mode");
// Label: "file.ts:45 β renderComponent(props) [dark mode]"forAll() β Property-Based Testing
Test properties with auto-generated values.
import { forAll } from "@typesugar/testing";
import { derive } from "@typesugar/derive";
@derive(Arbitrary)
interface User {
name: string;
age: number;
active: boolean;
}
// Test that serialization round-trips
forAll(arbitraryUser, (user) => {
expect(deserialize(serialize(user))).toEqual(user);
});
// With custom iteration count
forAll(arbitraryUser, 500, (user) => {
expect(user.age).toBeGreaterThanOrEqual(0);
});
// On failure:
// Property failed after 42 tests.
// Failing input: {"name":"...","age":-1,...}
// Error: Expected age >= 0Type Utilities
import {
Equal, // Type equality
Extends, // Subtype check
Not, // Negation
And, // Conjunction
Or, // Disjunction
IsNever, // Check for never
IsAny, // Check for any
IsUnknown, // Check for unknown
} from "@typesugar/testing";
// Use with typeAssert
typeAssert<Equal<A, B>>();
typeAssert<Extends<Child, Parent>>();
typeAssert<Not<IsAny<T>>>();
typeAssert<And<Extends<A, B>, Extends<B, C>>>();Examples
See the examples/ directory for real-world patterns:
- basic.ts β Core testing patterns from dogfooding typesugar's own test suite
API Reference
Assertions
assert(condition, message?)β Assert with sub-expression capturestaticAssert(condition, message?)β Compile-time assertiontypeAssert<T extends true>()β Type-level assertionassertSnapshot(value, name?)β Snapshot with source capture
Deprecated Aliases
powerAssertβ Useassert()insteadcomptimeAssertβ UsestaticAssert()instead
Parameterized Testing
@testCases(cases)β Expand to multiple test cases
Property-Based Testing
forAll(generator, property)β Test property with 100 iterationsforAll(generator, count, property)β Test property with custom count
Type Utilities
Equal<A, B>β True if A and B are the same typeExtends<A, B>β True if A extends BNot<T>β Negate a boolean typeAnd<A, B>β Conjunction of boolean typesOr<A, B>β Disjunction of boolean typesIsNever<T>β True if T is neverIsAny<T>β True if T is anyIsUnknown<T>β True if T is unknown
Vitest Integration
To use @typesugar/testing macros in your vitest tests, add the typesugar transformer plugin to your vitest.config.ts:
import { defineConfig } from "vitest/config";
import typemacro from "unplugin-typesugar/vite";
export default defineConfig({
plugins: [typemacro()],
test: {
// your test config
},
});Then import and use the macros in your test files:
import { describe, it } from "vitest";
import { assert, typeAssert, type Equal } from "@typesugar/testing";
describe("my tests", () => {
it("uses power assertions", () => {
assert(result.status === "success");
});
it("verifies types", () => {
typeAssert<Equal<typeof result, MyType>>();
});
});License
MIT
