import { Subscription, Observable } from 'rxjs'; /** * The dispose key this package defines on, and answers, on every double. * * Read it instead of `Symbol.dispose` in library code: the constant is what forces this module — and * with it the install above — to load before the first double is built. * * Typed as the well-known symbol rather than as a plain `symbol` so it can key a class member — * TypeScript accepts a computed member name only from a literal or `unique symbol` type, and * {@link ObserverSpy} declares its `[DISPOSE]()` that way. */ declare const DISPOSE: typeof Symbol.dispose; /** * `subscribeSpyTo` — the `@hirez_io/observer-spy` surface, for a suite that arrives carrying it. * * A `jasmine-auto-spies` suite almost always has this package beside it: the two are by the same * author, and observer-spy is the larger of the two by downloads. It was last published in 2022 and * its oldest open issue is from 2023, so a suite moving to Vitest has to bring it along or rewrite * every stream assertion at the same time as everything else. This is the smaller of those. * * **It is a bridge, and the destination is different in kind.** observer-spy is *synchronous * inspection*: subscribe, let things happen, then read the spy. The failure mode that design has is * silence — a stream that never emits produces a spy with no values, and a spec that reads * `getValues()` gets `[]` and asserts something about it, so the test passes having observed * nothing. `expectEmission` and friends invert that: the assertion *is* the await, and silence is a * failure with a watchdog rather than an empty array. Land the suite green on this, then move the * assertions over. * * Three deliberate departures from upstream, each closing a defect rather than adding a feature: * * - `getValues()` returns a **copy**. Upstream hands back its live internal array, so a spec that * sorts or splices what it read corrupts the spy it is still reading. * - `getValues()` is typed `T[]`. Upstream types it `any[]` (its own issue #69), which silently * turns every downstream inference in the assertion into `any`. * - `getFirstValue()` / `getValueAt(i)` **throw** when there is nothing there. Upstream types them * `T` and returns `undefined`, which is the same lie this library refuses everywhere else — but * the signature is kept, so a migrated spec still compiles. * - An **unexpected error** is thrown by the value readers rather than out of the subscription. See * {@link ObserverSpy.error}: upstream's rethrow stopped reaching the subscriber in rxjs 7. */ /** Upstream's one configuration flag. */ interface ObserverSpyConfig { /** * Treat an error as an expected outcome, so reading the spy's values stays legal. * * Without it, an unexpected error is still recorded — but every *value* reader throws it, naming * it, at the line that asked. See the note on {@link ObserverSpy.error}. */ expectErrors: boolean; } /** * The recording observer — upstream's `ObserverSpy`, usable as an `Observer` anywhere rxjs * takes one. */ declare class ObserverSpy { #private; constructor(config?: ObserverSpyConfig); next(value: T): void; /** * Record the error — and, when nothing said to expect one, arm every value reader to throw it. * * Upstream rethrows from here, and under rxjs 6 that surfaced at the subscription. It cannot any * more: rxjs 7 routes anything thrown out of an observer callback through `reportUnhandledError`, * which reports it *asynchronously*, so the throw no longer reaches the line that subscribed — * `expect(() => subscribeSpyTo(failing$)).toThrow()` does not see it, and Vitest reports an * unhandled error attributed to the file rather than to the assertion. Loud, and unattributable. * * Deferring it to the readers keeps the loudness and puts it back where it can be read: a spec * that forgot `{ expectErrors: true }` fails at `getValues()` with the original error, and one * that meant to assert on the failure reads `getError()` as before. */ error(errorValue: unknown): void; complete(): void; /** Record errors rather than rethrowing them, after construction. Chainable, as upstream's is. */ expectErrors(): this; /** * Resolves (or invokes `callback`) when the stream completes — immediately if it already has. * * The promise form **rejects** on a stream that errored instead: the completion it is waiting for * is not coming, and a promise that can only hang reports the file rather than the stream. */ onComplete(): Promise; onComplete(callback: () => void): void; /** Resolves when the stream errors — immediately if it already has; rejects once it completes instead. */ onError(): Promise; onError(callback: () => void): void; getValuesLength(): number; /** Every value emitted so far — a copy, so sorting or splicing it cannot corrupt the spy. */ getValues(): T[]; /** The value at `index`. Throws rather than returning `undefined` past the end. */ getValueAt(index: number): T; /** The first value. Throws when there is none — see the note at the top of this module. */ getFirstValue(): T; /** The last value, or `undefined` — the one reader upstream already types honestly. */ getLastValue(): T | undefined; getError(): unknown; receivedNext(): boolean; receivedError(): boolean; receivedComplete(): boolean; } /** An {@link ObserverSpy} that owns its subscription — what {@link subscribeSpyTo} hands back. */ declare class SubscriberSpy extends ObserverSpy { readonly subscription: Subscription; constructor(observableUnderTest: Observable, config?: ObserverSpyConfig); unsubscribe(): void; /** * Unsubscribe at the end of a `using` block. * * This is what `autoUnsubscribe()` exists for upstream — a global `afterEach` that tears down * every spy the file created. `using` scopes it to the block that made it instead, so there is no * setup file to remember and no registry that has to be right. * * Keyed by {@link DISPOSE} rather than by `Symbol.dispose` directly, like every other double here: * reading the constant is what loads the shim on a realm that has no `Symbol.dispose` — Node 22 * under `jsdom` — before `using` asks for it. */ [DISPOSE](): void; } /** * Subscribe to `observableUnderTest` and record everything it does. * * ```ts * const spy = subscribeSpyTo(service.load()); * * expect(spy.getValues()).toEqual(['a', 'b']); * expect(spy.receivedComplete()).toBe(true); * ``` * * The subscription is live until `unsubscribe()`, or until the end of a `using` block. Prefer * `expectEmission(source$)` / `expectEmissions(source$, n)` in new specs: they fail on silence, * which this cannot. */ declare function subscribeSpyTo(observableUnderTest: Observable, config?: ObserverSpyConfig): SubscriberSpy; export { ObserverSpy, type ObserverSpyConfig, SubscriberSpy, subscribeSpyTo };