### Install Jest and Jest Globals Source: https://github.com/littensy/rbxts-jest/blob/main/README.md Install Jest and Jest Globals using npm, yarn, or pnpm. For pnpm users, ensure a `.npmrc` file with `node-linker=hoisted` is present. ```sh npm install @rbxts/jest @rbxts/jest-globals ``` ```sh yarn add @rbxts/jest @rbxts/jest-globals ``` ```sh pnpm add @rbxts/jest @rbxts/jest-globals # 🛑 See below ``` -------------------------------- ### Install Core Packages Source: https://context7.com/littensy/rbxts-jest/llms.txt Install the necessary @rbxts/jest packages using npm, yarn, or pnpm. For pnpm, ensure a hoisted node-linker is configured. ```sh # npm / yarn npm install @rbxts/jest @rbxts/jest-globals # pnpm (requires hoisted linker) echo "node-linker=hoisted" > .npmrc pnpm add @rbxts/jest @rbxts/jest-globals ``` -------------------------------- ### Basic Benchmark Usage Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates the fundamental usage of the `benchmark` function for profiling FPS and section execution time. It includes starting and stopping profiler sections and finishing the benchmark. ```APIDOC ## benchmark() ### Description Adds a `benchmark` function analogous to `test` that automatically profiles FPS and section execution time. ### Usage ```typescript import { benchmark } from "@rbxts/jest-benchmark"; benchmark("Render large scene", (profiler) => { profiler.start("initial render"); // ... expensive Roblox rendering operation ... profiler.stop(); profiler.start("update pass"); // ... update pass ... profiler.stop(); profiler.finish(); // Outputs: fps/initial render: 60, sectionTime/initial render: 0.016, ... }); ``` ``` -------------------------------- ### Custom Reporter Example Source: https://context7.com/littensy/rbxts-jest/llms.txt Illustrates how to create and use a custom reporter to track specific metrics, such as the average of reported values over a section. ```APIDOC ## Custom Reporter ### Description Allows defining custom reporters to capture arbitrary metrics beyond default FPS and time profiling. ### Usage ```typescript import { Reporter } from "@rbxts/jest-benchmark"; const avgReporter = Reporter.initializeReporter("avgValue", (values) => { return values.reduce((a, b) => a + b, 0) / values.size(); }); avgReporter.start("section1"); avgReporter.report(10); avgReporter.report(20); avgReporter.stop(); avgReporter.start("section2"); avgReporter.report(30); // report using a function receiving (previousValue, timeDelta) avgReporter.report((prev, dt) => (prev ?? 0) + (dt ?? 0) * 1000); avgReporter.stop(); const [names, values] = avgReporter.finish(); // names: ["section1", "section2"] // values: [15, ...] ``` ``` -------------------------------- ### Basic Formatting with @rbxts/pretty-format Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates the default serialization of numbers, strings, arrays, and objects. No special setup is required for basic usage. ```typescript import format, { plugins, DEFAULT_OPTIONS } from "@rbxts/pretty-format"; import type { NewPlugin, PrettyFormatOptions, Config, Refs, Printer } from "@rbxts/pretty-format"; // ── Basic formatting ───────────────────────────────────────────────────────── print(format(42)); // 42 print(format("hello")); // "hello" print(format([1, 2, 3])); // Array [1, 2, 3] print(format({ a: 1, b: { c: true } })); // Object { // "a": 1, // "b": Object { // "c": true, // }, // } ``` -------------------------------- ### Custom Matchers with expect.extend Source: https://context7.com/littensy/rbxts-jest/llms.txt Extend the `expect` API with custom matchers using `expect.extend`. This example adds a `.toBeEvenNumber` matcher. ```typescript // ── Custom matchers via expect.extend ─────────────────────────────────────── expect.extend({ toBeEvenNumber(received: number) { const pass = received % 2 === 0; return { pass, message: () => `expected ${received} ${pass ? "not " : ""}to be even`, }; }, }); // (After augmenting the jest namespace you can use the custom matcher) expect(4).toEqual(expect.objectContaining({ valueOf: expect.callable() })); ``` -------------------------------- ### Schedule and Run Tests with createTestScheduler Source: https://context7.com/littensy/rbxts-jest/llms.txt Use `createTestScheduler` to manage test execution. It allows for custom reporters to observe test lifecycle events like run start, case results, and run completion. ```typescript const globalConfig: GlobalConfig = { bail: 0, expand: false, json: false, listTests: false, maxConcurrency: 5, maxWorkers: 1, noStackTrace: false, stackDepth: 5, nonFlagArgs: [], passWithNoTests: false, projects: [], reporters: [], runTestsByPath: false, rootDir: game, skipFilter: false, snapshotFormat: {}, testFailureExitCode: 1, testPathPattern: "", updateSnapshot: "none", }; const scheduler = await createTestScheduler(globalConfig, { firstRun: true, previousSuccess: true, }); // Register a custom reporter scheduler.addReporter({ onRunStart(results, { estimatedTime }) { print(`Starting run, estimated ${estimatedTime}s`); }, onTestCaseResult(test, { title, status, failureMessages }) { const icon = status === "passed" ? "✓" : "✗"; print(` ${icon} ${title}`); if (status === "failed") failureMessages.forEach((m) => warn(m)); }, onRunComplete(_, results) { print(`Done: ${results.numPassedTests} passed, ${results.numFailedTests} failed`); }, getLastError() { return undefined; }, }); // Placeholder for tests variable, assuming it's populated elsewhere const tests: Array = []; // Replace with actual test discovery logic // ── TestWatcher — interrupt a running test suite ──────────────────────────── const watcher = new TestWatcher({ isWatchMode: false }); // Interrupt from another coroutine if needed task.delay(30, async () => { if (!watcher.isInterrupted()) { await watcher.setState({ interrupted: true }); } }); const aggregated = await scheduler.scheduleTests(tests, watcher); print(`Success: ${aggregated.success}`); ``` -------------------------------- ### Using Built-in Plugins Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates how to use pre-built plugins like `AsymmetricMatcher` for specific serialization needs. Ensure the plugin is included in the `plugins` array of the options. ```typescript // plugins.AsymmetricMatcher, ConvertAnsi, DOMCollection, DOMElement, // Immutable, ReactElement, ReactTestComponent are available as pre-built plugins. print(format(expect.any("number"), { plugins: [plugins.AsymmetricMatcher] })); // Any ``` -------------------------------- ### Customizing Output with PrettyFormatOptions Source: https://context7.com/littensy/rbxts-jest/llms.txt Shows how to customize the output format using `PrettyFormatOptions`. Options include indentation, maximum depth, compact output, and key sorting. ```typescript const opts: PrettyFormatOptions = { indent: 4, maxDepth: 3, min: false, // compact single-line output when true highlight: true, escapeString: false, printFunctionName: true, printBasicPrototype: false, compareKeys: (a, b) => a.localeCompare(b), // sort keys alphabetically }; print(format({ z: 1, a: 2, m: 3 }, opts)); // Object { // "a": 2, // "m": 3, // "z": 1, // } // Minimal (single-line) output print(format({ x: 1, y: 2 }, { min: true })); // {"x": 1, "y": 2} ``` -------------------------------- ### Benchmark Control: only and skip Source: https://context7.com/littensy/rbxts-jest/llms.txt Shows how to use `benchmark.only` to run a specific benchmark and `benchmark.skip` to exclude a benchmark from execution. ```APIDOC ## benchmark.only / benchmark.skip ### Description Provides methods to selectively run or skip benchmarks. ### Usage ```typescript benchmark.only("Critical path only", (profiler) => { profiler.start("critical"); profiler.stop(); profiler.finish(); }); benchmark.skip("Skip this one for now", (profiler) => { profiler.finish(); }); ``` ``` -------------------------------- ### Adding Global Custom Reporters Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates how to globally add custom reporters that will be available to all benchmarks, alongside the default reporters. ```APIDOC ## CustomReporters ### Description Enables the global addition of custom reporters that are automatically included in all benchmarks. ### Usage ```typescript import { Reporter, CustomReporters, benchmark } from "@rbxts/jest-benchmark"; const countReporter = Reporter.initializeReporter("callCount", (v) => v.size()); CustomReporters.useCustomReporters({ callCount: countReporter }); benchmark("With extra reporter", (profiler, reporters) => { profiler.start("work"); reporters["callCount"].report(1); reporters["callCount"].report(1); profiler.stop(); profiler.finish(); // Outputs callCount/work: 2 as well as default fps/sectionTime }); CustomReporters.useDefaultReporters(); ``` ``` -------------------------------- ### Basic Benchmark with FPS and Time Profiling Source: https://context7.com/littensy/rbxts-jest/llms.txt Use `benchmark` to profile FPS and section execution time. The profiler automatically tracks metrics like 'fps' and 'sectionTime'. ```typescript import { benchmark, Reporter, Profiler, HeartbeatReporter, MetricLogger, CustomReporters, } from "@rbxts/jest-benchmark"; // ── benchmark() — basic FPS + time profiling ──────────────────────────────── benchmark("Render large scene", (profiler) => { profiler.start("initial render"); // ... expensive Roblox rendering operation ... profiler.stop(); profiler.start("update pass"); // ... update pass ... profiler.stop(); profiler.finish(); // Outputs: fps/initial render: 60, sectionTime/initial render: 0.016, ... }); ``` -------------------------------- ### Custom Metric Logger Source: https://context7.com/littensy/rbxts-jest/llms.txt Explains how to replace the default benchmark output sink with a custom `MetricLogger` to redirect metrics to different destinations. ```APIDOC ## MetricLogger ### Description Allows redirection of benchmark output to custom logging functions, enabling integration with various output sinks. ### Usage ```typescript import { MetricLogger, benchmark } from "@rbxts/jest-benchmark"; MetricLogger.useCustomMetricLogger((metricName, value) => { // write to a RemoteEvent, file, etc. print(`CUSTOM LOG >> ${metricName} = ${tostring(value)}`); }); benchmark("With custom logger", (profiler) => { profiler.start("test"); profiler.stop(); profiler.finish(); // calls our custom logger }); MetricLogger.useDefaultMetricLogger(); // restore default ``` ``` -------------------------------- ### Rojo Project Configuration Source: https://github.com/littensy/rbxts-jest/blob/main/README.md Add this configuration to your Rojo project file under the `node_modules` folder to ensure correct module linking. ```json "node_modules": { "$className": "Folder", "@rbxts": { "$path": "node_modules/@rbxts" }, "@rbxts-js": { "$path": "node_modules/@rbxts-js" } } ``` -------------------------------- ### Configure Rojo Project Source: https://context7.com/littensy/rbxts-jest/llms.txt Configure your default.project.json to expose both the @rbxts and @rbxts-js module namespaces to the DataModel. ```json // default.project.json — expose both namespaces to the DataModel { "name": "my-roblox-project", "tree": { "$className": "DataModel", "node_modules": { "$className": "Folder", "@rbxts": { "$path": "node_modules/@rbxts" }, "@rbxts-js": { "$path": "node_modules/@rbxts-js" } } } } ``` -------------------------------- ### Custom Matchers via expect.extend Source: https://context7.com/littensy/rbxts-jest/llms.txt Explains how to define and use custom assertion matchers. ```APIDOC ## Custom Matchers via expect.extend ### Description Allows extending the `expect` API with custom assertion logic. ### Code Example ```typescript expect.extend({ toBeEvenNumber(received: number) { const pass = received % 2 === 0; return { pass, message: () => `expected ${received} ${pass ? "not " : ""}to be even`, }; }, }); // (After augmenting the jest namespace you can use the custom matcher) expect(4).toEqual(expect.objectContaining({ valueOf: expect.callable() })); ``` ``` -------------------------------- ### pnpm .npmrc Configuration Source: https://github.com/littensy/rbxts-jest/blob/main/README.md If using pnpm, create a `.npmrc` file in your project root with this content to enable hoisted node linking. ```ini node-linker=hoisted ``` -------------------------------- ### Roblox Instance Matcher Source: https://context7.com/littensy/rbxts-jest/llms.txt Shows how to use `.toMatchInstance` to assert properties of Roblox Instances. ```APIDOC ## Roblox Instance Matcher ### Description Asserts properties of Roblox Instances, including nested children. ### Code Example ```typescript const part = new Instance("Part"); part.Name = "Floor"; part.Size = new Vector3(10, 1, 10); expect(part).toMatchInstance({ Name: "Floor", ClassName: "Part", Size: new Vector3(10, 1, 10), }); ``` ``` -------------------------------- ### Primitive Matchers Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates the usage of basic assertion matchers for primitive types and nil checks. ```APIDOC ## Primitive Matchers ### Description Basic assertion matchers for primitive values and nil checks. ### Code Example ```typescript expect(42).toBe(42); expect("hello").not.toBe("world"); expect(undefined).toBeNil(); // preferred over toBeUndefined in Luau expect(0).toBeFalsy(); expect(1).toBeTruthy(); expect(math.huge).toBeGreaterThan(999); expect(0.1 + 0.2).toBeCloseTo(0.3, 5); ``` ``` -------------------------------- ### Basic Test Structure with @rbxts/jest-globals Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates the basic structure of tests using describe, it/test, expect, and lifecycle hooks. Imports are required from @rbxts/jest-globals. ```typescript import { describe, it, test, expect, beforeAll, beforeEach, afterEach, afterAll, jest, } from "@rbxts/jest-globals"; // ── Basic test structure ──────────────────────────────────────────────────── describe("PlayerService", () => { let playerCount: number; beforeAll(() => { playerCount = 0; }); afterEach(() => { jest.clearAllMocks(); }); // test / it are interchangeable it("starts with zero players", () => { expect(playerCount).toBe(0); }); test("increments correctly", () => { playerCount += 1; expect(playerCount).toBeGreaterThan(0); expect(playerCount).toBeLessThanOrEqual(100); }); }); ``` -------------------------------- ### Custom Reporter for Average Metric Source: https://context7.com/littensy/rbxts-jest/llms.txt Create a custom reporter using `Reporter.initializeReporter` to calculate and track specific metrics, like the average of reported values. ```typescript const avgReporter = Reporter.initializeReporter("avgValue", (values) => { return values.reduce((a, b) => a + b, 0) / values.size(); }); avgReporter.start("section1"); avgReporter.report(10); avgReporter.report(20); avgReporter.stop(); avgReporter.start("section2"); avgReporter.report(30); // report using a function receiving (previousValue, timeDelta) avgReporter.report((prev, dt) => (prev ?? 0) + (dt ?? 0) * 1000); avgReporter.stop(); const [names, values] = avgReporter.finish(); // names: ["section1", "section2"] // values: [15, ...] ``` -------------------------------- ### Profiler with Multiple Reporters Source: https://context7.com/littensy/rbxts-jest/llms.txt Shows how to combine multiple reporters (e.g., custom and heartbeat) into a single `Profiler` instance and apply a prefix to all metric names. ```APIDOC ## Profiler ### Description Combines multiple reporters and allows for a global prefix for all reported metrics. ### Usage ```typescript import { Profiler, Reporter, HeartbeatReporter } from "@rbxts/jest-benchmark"; // Assuming avgReporter and heartbeat are already initialized const avgReporter = Reporter.initializeReporter("avgValue", (values) => values.reduce((a, b) => a + b, 0) / values.size()); const heartbeat = HeartbeatReporter.initializeHeartbeatReporter("heartbeatAvg", (deltas) => deltas.reduce((a, b) => a + b, 0) / deltas.size()); const profiler = Profiler.initializeProfiler( [avgReporter, heartbeat], (metricName, value) => print(`[METRIC] ${metricName}: ${value}`), "MyBenchmark/" // prefix all section names ); profiler.start("combined section"); // ... work ... profiler.stop(); profiler.finish(); // Outputs: [METRIC] MyBenchmark/avgValue/combined section: // [METRIC] MyBenchmark/heartbeatAvg/combined section: ``` ``` -------------------------------- ### Matcher Output Helpers (@rbxts/jest-matcher-utils) Source: https://context7.com/littensy/rbxts-jest/llms.txt Provides formatting primitives for custom Jest matchers, including functions for building hints, printing received/expected values, stringifying, and type checking. ```APIDOC ## matcherHint() ### Description Generates a formatted hint string for custom Jest matchers, including the matcher name and options like `isNot`. ### Parameters - `name` (string): The name of the matcher. - `received` (string | undefined): The received value (optional). - `expected` (string | undefined): The expected value (optional). - `options` (object, optional): Options for the hint, such as `isNot`. ### Request Example ```typescript const hint = matcherHint(".toBeWithinRange", undefined, undefined, { isNot: this.isNot, }); ``` ``` ```APIDOC ## printReceived() / printExpected() ### Description These functions format the received and expected values for display in matcher failure messages, applying appropriate colors. ### Parameters - `value` (any): The value to format. ### Request Example ```typescript printReceived(5); printExpected("hello"); ``` ``` ```APIDOC ## stringify() ### Description Serializes any JavaScript value into a human-readable string representation, suitable for outputting in test results. ### Parameters - `value` (any): The value to serialize. ### Request Example ```typescript print(stringify({ a: 1, b: [2, 3] })); // "{"a": 1, "b": [2, 3]}" print(stringify("hello")); // '"hello"' print(stringify(undefined)); // "undefined" ``` ``` ```APIDOC ## getLabelPrinter() ### Description Creates a function that aligns multiple labels, ensuring consistent spacing for output like "Expected", "Received", etc. ### Parameters - `...labels` (string): The labels to align. ### Returns - `function`: A function that takes a label and returns it with appropriate padding. ### Request Example ```typescript const printLabel = getLabelPrinter("Expected", "Received", "Difference"); print(printLabel("Expected")); // "Expected " ``` ``` ```APIDOC ## pluralize() ### Description Returns a string with the correct pluralization for a given count and word. ### Parameters - `word` (string): The singular form of the word. - `count` (number): The number to determine pluralization. ### Returns - `string`: The word, pluralized if necessary, with the count prepended. ### Request Example ```typescript print(pluralize("assertion", 1)); // "1 assertion" print(pluralize("assertion", 3)); // "3 assertions" ``` ``` ```APIDOC ## diff() ### Description Generates an inline diff between two strings, useful for displaying differences directly within matcher messages. ### Parameters - `received` (string): The received string. - `expected` (string): The expected string. - `options` (object, optional): Options for diffing, including annotations. ### Request Example ```typescript const diffOutput = diff("received string", "expected string", { aAnnotation: "Expected", bAnnotation: "Received", }); if (diffOutput !== undefined) print(diffOutput); ``` ``` ```APIDOC ## matcherErrorMessage() ### Description Constructs a standardized error message for matcher parameter type errors. ### Parameters - `hint` (string): The matcher hint string. - `message` (string): The core error message. - `formattedValue` (string): The formatted representation of the incorrect value. ### Request Example ```typescript const errMsg = matcherErrorMessage( matcherHint(".toBeWithinRange"), "The floor argument must be a number", printWithType("Floor", "not-a-number", printReceived), ); print(errMsg); ``` ``` ```APIDOC ## Type Guard Helpers (ensureNumbers, ensureNoExpected) ### Description These functions help validate input types within custom matchers, ensuring that arguments are of the expected type (e.g., numbers) or that certain arguments are not provided. ### Functions - `ensureNumbers(...values: unknown[]): void` - `ensureNoExpected(value: unknown, matcherName: string): void` ### Request Example ```typescript // Inside a custom matcher: ensureNumbers(received, floor, ".toBeWithinRange"); ``` ``` -------------------------------- ### Typed Event Emitter with @rbxts/emittery Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates defining typed events, creating an emitter instance, and setting up various types of listeners. Use this for managing application-wide event buses with compile-time safety. ```typescript import Emittery from "@rbxts/emittery"; // ── Typed event map ────────────────────────────────────────────────────────── type GameEvents = { playerJoined: { name: string; userId: number }; playerLeft: { name: string; reason: string }; roundStarted: undefined; // dataless event }; const bus = new Emittery({ debug: { name: "GameBus", enabled: false }, }); // ── on() — persistent listener ─────────────────────────────────────────────── const offJoined = bus.on("playerJoined", ({ name, userId }) => { print(`${name} (${userId}) joined`); }); // Listen to multiple events with one handler bus.on(["playerJoined", "playerLeft"], (data) => { print("Roster changed:", data); }); // ── once() — single-fire listener ──────────────────────────────────────────── bus.once("roundStarted").then(() => { print("Round has started!"); }); // ── emit() — concurrent listeners ──────────────────────────────────────────── await bus.emit("playerJoined", { name: "Alice", userId: 123 }); await bus.emit("roundStarted"); // no payload required // ── emitSerial() — sequential listeners ────────────────────────────────────── await bus.emitSerial("playerLeft", { name: "Alice", reason: "disconnect" }); // ── onAny() — catch-all listener ───────────────────────────────────────────── const offAny = bus.onAny((eventName, data) => { print(`Event fired: ${tostring(eventName)}`); }); // ── events() — async iterator ──────────────────────────────────────────────── const iterator = bus.events("playerJoined"); await bus.emit("playerJoined", { name: "Bob", userId: 456 }); const { value } = await iterator.next(); print(value.name); // "Bob" await iterator.return!(); // unsubscribe // ── off() / offAny() / clearListeners() ────────────────────────────────────── offJoined(); // remove specific listener offAny(); // remove onAny listener bus.clearListeners("playerLeft"); // clear all listeners for one event bus.clearListeners(); // clear everything print(`Listener count: ${bus.listenerCount()}`); // 0 // ── Emittery.listenerAdded / listenerRemoved meta-events ───────────────────── bus.on(Emittery.listenerAdded, ({ listener, eventName }) => { print(`New listener added for: ${tostring(eventName)}`); }); bus.on("roundStarted", () => {}); // triggers listenerAdded ``` -------------------------------- ### Custom Serialization with NewPlugin Source: https://context7.com/littensy/rbxts-jest/llms.txt Illustrates creating a custom plugin to serialize application-specific types like `Vector2Like`. The `test` function identifies the type, and `serialize` defines its string representation. ```typescript interface Vector2Like { X: number; Y: number } const Vector2Plugin: NewPlugin = { test(val: unknown): boolean { return ( typeof val === "table" && typeof (val as any).X === "number" && typeof (val as any).Y === "number" ); }, serialize( val: Vector2Like, _config: Config, _indentation: string, _depth: number, _refs: Refs, _printer: Printer ): string { return `Vector2(${val.X}, ${val.Y})`; }, }; const vec: Vector2Like = { X: 3, Y: 4 }; print(format(vec, { plugins: [Vector2Plugin] })); // Vector2(3, 4) print(format({ position: vec }, { plugins: [Vector2Plugin] })); // Object { // "position": Vector2(3, 4), // } ``` -------------------------------- ### Snapshot Matchers Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates snapshot testing for objects and inline values. ```APIDOC ## Snapshot Matchers ### Description Utilizes snapshot testing to capture and compare object structures or inline values. ### Code Example ```typescript expect({ player: "Alice", score: 100 }).toMatchSnapshot(); expect("inline value").toMatchInlineSnapshot(`"inline value"`); ``` ``` -------------------------------- ### Adding Global Custom Reporters Source: https://context7.com/littensy/rbxts-jest/llms.txt Globally add custom reporters to all benchmarks using `CustomReporters.useCustomReporters`. These reporters will run alongside the default ones. ```typescript const countReporter = Reporter.initializeReporter("callCount", (v) => v.size()); CustomReporters.useCustomReporters({ callCount: countReporter }); benchmark("With extra reporter", (profiler, reporters) => { profiler.start("work"); reporters["callCount"].report(1); reporters["callCount"].report(1); profiler.stop(); profiler.finish(); // Outputs callCount/work: 2 as well as default fps/sectionTime }); CustomReporters.useDefaultReporters(); ``` -------------------------------- ### Asymmetric Matchers Source: https://context7.com/littensy/rbxts-jest/llms.txt Covers the use of asymmetric matchers like `expect.any` and `expect.anything`. ```APIDOC ## Asymmetric Matchers ### Description Uses asymmetric matchers for flexible assertions, such as checking types or any value. ### Code Example ```typescript const [mockFn, fn] = jest.fn((x: number) => ({ id: x, timestamp: os.time() })); fn(1); expect(mockFn).toHaveBeenCalledWith(expect.any("number")); expect(mockFn.mock.results[0].value).toMatchObject({ id: 1, timestamp: expect.anything(), }); ``` ``` -------------------------------- ### runCLI - Programmatic Test Runner Source: https://context7.com/littensy/rbxts-jest/llms.txt The `runCLI` function allows for programmatic execution of the Jest test runner. It accepts a root instance (acting as the current working directory), command-line arguments (as an object conforming to `InitialOptions`), and an array of projects to run tests against. It returns the test results and global configuration. ```APIDOC ## runCLI ### Description Launches a full test run programmatically. ### Parameters - **rootInstance** (Instance) - The Roblox Instance acting as the working directory. - **argv** (Partial) - An object containing configuration options for the test run, such as `verbose`, `testMatch`, `bail`, and `testTimeout`. - **projects** (Array) - An array of Roblox Instances representing the projects to run tests against. ### Returns - **results** (object) - An object containing the test results, including `numPassedTests`, `numTotalTests`, `numFailedTestSuites`, `success`, and `testResults`. - **globalConfig** (object) - The global configuration object for the test run. ``` -------------------------------- ### SearchSource - Discover Test Files Source: https://context7.com/littensy/rbxts-jest/llms.txt The `SearchSource` class is used to discover test files within a project. It can determine if a given file path matches the test file pattern and can find all tests matching a specific pattern. ```APIDOC ## SearchSource ### Description Discovers test files from a project configuration. ### Methods - **isTestFilePath(filePath: string): boolean** Checks if the given file path is a test file. - **findMatchingTests(pattern: string): { tests: Array, total: number | undefined }** Finds all test files matching the provided pattern. ### Usage Example ```typescript const context = { config: { /* ProjectConfig */ } as any }; const source = new SearchSource(context); const isTest = source.isTestFilePath("src/__tests__/inventory.spec.ts"); // true const { tests, total } = source.findMatchingTests("inventory"); print(`Found ${total ?? tests.size()} matching tests`); ``` ``` -------------------------------- ### Programmatic Test Runner with runCLI Source: https://context7.com/littensy/rbxts-jest/llms.txt Use `runCLI` to execute tests programmatically. Configure test matching, bail on failure, and timeouts. It returns test results and global configuration. ```typescript import { runCLI, SearchSource, createTestScheduler, TestWatcher } from "@rbxts/jest"; import type { GlobalConfig, InitialOptions } from "@rbxts/jest/src/config"; // ── runCLI — programmatic test runner ─────────────────────────────────────── async function runTests(rootInstance: Instance) { const argv: Partial = { verbose: true, testMatch: ["**/__tests__/**/*.spec.ts"], bail: 1, // stop after first failure testTimeout: 5000, }; const { results, globalConfig } = await runCLI( rootInstance, // cwdInstance: the Roblox Instance acting as the working dir argv, [rootInstance] // projects array ); print(`Passed: ${results.numPassedTests} / ${results.numTotalTests}`); print(`Suites failed: ${results.numFailedTestSuites}`); if (!results.success) { results.testResults.forEach((tr) => { if (tr.status === "done") { tr.errors.forEach((e) => warn(e)); } }); } return results.success; } ``` -------------------------------- ### Mock Assertion Helpers Source: https://context7.com/littensy/rbxts-jest/llms.txt Details helpers for asserting the behavior of mock functions. ```APIDOC ## Mock Assertion Helpers ### Description Provides helpers to assert call counts, arguments, and return values of mock functions. ### Code Example ```typescript const [counter, counterFn] = jest.fn(); counterFn(); counterFn(); counterFn(); expect(counter).toHaveBeenCalledTimes(3); expect(counter).toHaveBeenLastCalledWith(); // called with no args expect(counter).toHaveReturnedTimes(3); ``` ``` -------------------------------- ### Benchmark Control: Only and Skip Source: https://context7.com/littensy/rbxts-jest/llms.txt Use `benchmark.only` to run a specific benchmark or `benchmark.skip` to exclude one from execution. ```typescript benchmark.only("Critical path only", (profiler) => { profiler.start("critical"); profiler.stop(); profiler.finish(); }); benchmark.skip("Skip this one for now", (profiler) => { profiler.finish(); }); ``` -------------------------------- ### Mocking Functions with jest.fn() Source: https://context7.com/littensy/rbxts-jest/llms.txt Creates a mock function using jest.fn(). It returns a callable table and a forwarding function. Use expect to assert calls and return values. ```typescript // ── jest.fn() — creates a mock function ──────────────────────────────────── // Returns a LuaTuple: [mock (callable table), mockFn (forwarding function)] const [addMock, addFn] = jest.fn((a: number, b: number) => a + b); addFn(2, 3); expect(addMock).toHaveBeenCalledWith(2, 3); expect(addMock).toHaveReturnedWith(5); expect(addMock.mock.calls).toHaveLength(1); ``` -------------------------------- ### Profiler Combining Multiple Reporters Source: https://context7.com/littensy/rbxts-jest/llms.txt Use `Profiler.initializeProfiler` to combine multiple reporters and define a global metric logging function. All section names will be prefixed. ```typescript const profiler = Profiler.initializeProfiler( [avgReporter, heartbeat], (metricName, value) => print(`[METRIC] ${metricName}: ${value}`), "MyBenchmark/" // prefix all section names ); profiler.start("combined section"); // ... work ... profiler.stop(); profiler.finish(); // Outputs: [METRIC] MyBenchmark/avgValue/combined section: // [METRIC] MyBenchmark/heartbeatAvg/combined section: ``` -------------------------------- ### Custom Metric Logger Source: https://context7.com/littensy/rbxts-jest/llms.txt Replace the default output sink with a custom logger using `MetricLogger.useCustomMetricLogger`. This allows redirecting benchmark output to various destinations. ```typescript MetricLogger.useCustomMetricLogger((metricName, value) => { // write to a RemoteEvent, file, etc. print(`CUSTOM LOG >> ${metricName} = ${tostring(value)}`); }); benchmark("With custom logger", (profiler) => { profiler.start("test"); profiler.stop(); profiler.finish(); // calls our custom logger }); MetricLogger.useDefaultMetricLogger(); // restore default ``` -------------------------------- ### Discover Test Files with SearchSource Source: https://context7.com/littensy/rbxts-jest/llms.txt Utilize `SearchSource` to find test files based on a project configuration. It can check if a file is a test and find matching tests by a given string. ```typescript const context = { config: { /* ProjectConfig */ } } as any; const source = new SearchSource(context); const isTest = source.isTestFilePath("src/__tests__/inventory.spec.ts"); // true const { tests, total } = source.findMatchingTests("inventory"); print(`Found ${total ?? tests.size()} matching tests`); ``` -------------------------------- ### Error Matchers Source: https://context7.com/littensy/rbxts-jest/llms.txt Explains how to test for thrown errors and their messages. ```APIDOC ## Error Matchers ### Description Tests if a function throws an error and optionally checks the error message. ### Code Example ```typescript expect(() => { error("something went wrong"); }).toThrow("something went wrong"); ``` ``` -------------------------------- ### Heartbeat Reporter Source: https://context7.com/littensy/rbxts-jest/llms.txt Demonstrates the use of `HeartbeatReporter` to capture and process deltas from `RunService.Heartbeat`. ```APIDOC ## HeartbeatReporter ### Description Initializes a reporter specifically for capturing and processing deltas from `RunService.Heartbeat`. ### Usage ```typescript import { HeartbeatReporter } from "@rbxts/jest-benchmark"; const heartbeat = HeartbeatReporter.initializeHeartbeatReporter( "heartbeatAvg", (deltas) => deltas.reduce((a, b) => a + b, 0) / deltas.size() ); ``` ``` -------------------------------- ### Deep Equality Matchers Source: https://context7.com/littensy/rbxts-jest/llms.txt Illustrates matchers used for comparing objects, arrays, and their contents deeply. ```APIDOC ## Deep Equality Matchers ### Description Matchers for performing deep comparisons of objects and arrays. ### Code Example ```typescript expect({ a: 1, b: { c: 2 } }).toEqual({ a: 1, b: { c: 2 } }); expect([1, 2, 3]).toContain(2); expect([{ id: 1 }, { id: 2 }]).toContainEqual({ id: 2 }); expect({ name: "Roblox", version: 3 }).toMatchObject({ name: "Roblox" }); ``` ``` -------------------------------- ### Build Custom Matcher with Jest Utilities Source: https://context7.com/littensy/rbxts-jest/llms.txt Extend Jest with custom matchers using utilities like `matcherHint`, `printReceived`, and `printExpected` for consistent failure messages. Ensure inputs are numbers with `ensureNumbers`. ```typescript import { matcherHint, printReceived, printExpected, printWithType, stringify, EXPECTED_COLOR, RECEIVED_COLOR, BOLD_WEIGHT, ensureNumbers, ensureNoExpected, matcherErrorMessage, getLabelPrinter, pluralize, diff, } from "@rbxts/jest-matcher-utils"; import { expect, jest } from "@rbxts/jest-globals"; // ── Building a custom matcher ──────────────────────────────────────────────── expect.extend({ toBeWithinRange(received: number, floor: number, ceiling: number) { ensureNumbers(received, floor, ".toBeWithinRange"); const pass = received >= floor && received <= ceiling; const hint = matcherHint(".toBeWithinRange", undefined, undefined, { isNot: this.isNot, }); const message = pass ? () => `${hint}\n\n` + `Expected: not within range ${printExpected(floor)} – ${printExpected(ceiling)}\n` + `Received: ${printReceived(received)}` : () => `${hint}\n\n` + `Expected: within range ${printExpected(floor)} – ${printExpected(ceiling)}\n` + `Received: ${printReceived(received)}`; return { message, pass }; }, }); // Usage: expect(50).toBeWithinRange(0, 100); ``` -------------------------------- ### Heartbeat Reporter for RunService Deltas Source: https://context7.com/littensy/rbxts-jest/llms.txt Initialize a `HeartbeatReporter` to capture and process deltas from `RunService.Heartbeat`. ```typescript const heartbeat = HeartbeatReporter.initializeHeartbeatReporter( "heartbeatAvg", (deltas) => deltas.reduce((a, b) => a + b, 0) / deltas.size() ); ``` -------------------------------- ### createTestScheduler - Schedule and Run Tests Source: https://context7.com/littensy/rbxts-jest/llms.txt The `createTestScheduler` function creates a test scheduler that can be used to schedule and run discovered tests with reporters. It requires global configuration and options for the initial run. ```APIDOC ## createTestScheduler ### Description Schedules discovered tests with reporters. ### Parameters - **globalConfig** (GlobalConfig) - Configuration object for the test run, including options like `bail`, `maxWorkers`, `rootDir`, etc. - **options** (object) - Options for the scheduler, such as `firstRun` and `previousSuccess`. - **firstRun** (boolean) - Indicates if this is the first test run. - **previousSuccess** (boolean) - Indicates if the previous test run was successful. ### Methods on Scheduler - **addReporter(reporter: object): void** Registers a custom reporter with event handlers like `onRunStart`, `onTestCaseResult`, and `onRunComplete`. - **scheduleTests(tests: Array, watcher: TestWatcher): Promise** Schedules and runs the provided tests using the given watcher. Returns an aggregated result object. ### Usage Example ```typescript const globalConfig: GlobalConfig = { /* ... */ }; const scheduler = await createTestScheduler(globalConfig, { firstRun: true, previousSuccess: true }); scheduler.addReporter({ onRunStart(results, { estimatedTime }) { print(`Starting run, estimated ${estimatedTime}s`); }, // ... other reporter methods }); const aggregated = await scheduler.scheduleTests(tests, watcher); print(`Success: ${aggregated.success}`); ``` ``` -------------------------------- ### Compare Any Two Values with diff() Source: https://context7.com/littensy/rbxts-jest/llms.txt Use `diff()` to compare arbitrary values and generate a human-readable diff. Configure annotations, expansion, and context lines. ```typescript import { diff, diffLinesRaw, diffLinesUnified, diffStringsRaw, diffStringsUnified, DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL, } from "@rbxts/jest-diff"; // ── diff() — compare any two values ───────────────────────────────────────── const received = { name: "Alice", score: 95, level: 3 }; const expected = { name: "Alice", score: 100, level: 3 }; const output = diff(received, expected, { aAnnotation: "Expected", bAnnotation: "Received", expand: false, contextLines: 2, }); // - Expected // + Received // { name: "Alice", // - score: 100, // + score: 95, // level: 3 } ``` -------------------------------- ### Mocking Global Lua APIs with jest.globalEnv Source: https://context7.com/littensy/rbxts-jest/llms.txt Mock global Luau APIs like 'print' using jest.globalEnv. Remember to restore the original global environment after the test. ```typescript // ── jest.globalEnv — mock global Lua APIs ────────────────────────────────── test("mocks print", () => { const spy = jest.spyOn(jest.globalEnv, "print"); print("hello"); expect(spy).toHaveBeenCalledWith("hello"); spy.mockRestore(); }); ``` -------------------------------- ### Roblox Instance Matcher Source: https://context7.com/littensy/rbxts-jest/llms.txt Use `.toMatchInstance()` to assert properties of Roblox Instances, including nested children. ```typescript // ── Roblox Instance matcher ───────────────────────────────────────────────── // Checks a part's properties, including nested children const part = new Instance("Part"); part.Name = "Floor"; part.Size = new Vector3(10, 1, 10); expect(part).toMatchInstance({ Name: "Floor", ClassName: "Part", Size: new Vector3(10, 1, 10), }); ``` -------------------------------- ### Mocking ModuleScripts with jest.mock() Source: https://context7.com/littensy/rbxts-jest/llms.txt Mock a ModuleScript using jest.mock() and reset modules with jest.resetModules() in beforeEach. Use jest.requireActual to access the original module if needed. ```typescript // ── jest.mock() / jest.requireActual() ──────────────────────────────────── // Mocking a ModuleScript (the Roblox-ts equivalent of a Node module) beforeEach(() => { jest.resetModules(); jest.mock(script.Parent!.FindFirstChild("myModule") as ModuleScript, () => ({ default: jest.fn(() => 42), })); }); ``` -------------------------------- ### Data-Driven Tests with test.each() Source: https://context7.com/littensy/rbxts-jest/llms.txt Implement data-driven tests using test.each() to run the same test logic with different sets of inputs and expected outputs. ```typescript // ── test.each — data-driven tests ────────────────────────────────────────── test.each([ [1, 1, 2], [2, 3, 5], [10, -5, 5], ])("adds %i + %i = %i", (a, b, expected) => { expect(a + b).toBe(expected); }); ``` -------------------------------- ### Align Labels with getLabelPrinter() Source: https://context7.com/littensy/rbxts-jest/llms.txt Create a label printing function using `getLabelPrinter` to ensure consistent alignment of multiple labels in output messages. ```typescript // ── getLabelPrinter — align multiple labels ────────────────────────────────── const printLabel = getLabelPrinter("Expected", "Received", "Difference"); print(printLabel("Expected")); // "Expected " (padded to align) ```