@kiwa-test/nextjs 1.0.5 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -3
- package/dist/index.cjs +569 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +252 -1
- package/dist/index.d.ts +252 -1
- package/dist/index.js +541 -0
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
package/dist/index.d.ts
CHANGED
|
@@ -225,4 +225,255 @@ interface InvokeParallelRoutesResult<TSlots extends string, TNode = unknown> {
|
|
|
225
225
|
*/
|
|
226
226
|
declare function invokeParallelRoutes<TSlots extends string, TLayoutProps = Record<string, unknown>, TNode = unknown>(opts: InvokeParallelRoutesOptions<TSlots, TLayoutProps, TNode>): Promise<InvokeParallelRoutesResult<TSlots, TNode>>;
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
declare const RSC_ERROR_BOUNDARY_SYMBOL: unique symbol;
|
|
229
|
+
interface RscErrorBoundarySignal {
|
|
230
|
+
readonly [RSC_ERROR_BOUNDARY_SYMBOL]: true;
|
|
231
|
+
readonly error: unknown;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* An async source the helper consumes chunk-by-chunk. Each yielded value is
|
|
235
|
+
* one streaming frame; the helper appends it to `env.chunks` in arrival order
|
|
236
|
+
* and uses the last chunk as `env.resolved` once the source completes.
|
|
237
|
+
*
|
|
238
|
+
* Use a plain async generator for most cases:
|
|
239
|
+
*
|
|
240
|
+
* async function* source() {
|
|
241
|
+
* yield <Spinner />; // initial chunk
|
|
242
|
+
* yield <Skeleton rows={3} />; // partial data
|
|
243
|
+
* yield <Items list={data} />; // final resolved chunk
|
|
244
|
+
* }
|
|
245
|
+
*/
|
|
246
|
+
type RscStreamSource = AsyncIterable<RscNode>;
|
|
247
|
+
interface SetupNextRscEnvOptions {
|
|
248
|
+
/**
|
|
249
|
+
* The async server component under test. If `dataSource` is omitted, the
|
|
250
|
+
* helper awaits this function once and treats its return value as the only
|
|
251
|
+
* (resolved) chunk — equivalent to a synchronous resolution.
|
|
252
|
+
*
|
|
253
|
+
* The component may throw to trigger the error boundary path. See
|
|
254
|
+
* `injectError` for the test-side variant.
|
|
255
|
+
*/
|
|
256
|
+
readonly component?: (props: Record<string, unknown>) => Promise<RscNode> | RscNode;
|
|
257
|
+
/**
|
|
258
|
+
* Optional props forwarded to `component`. Defaults to `{}`.
|
|
259
|
+
*/
|
|
260
|
+
readonly props?: Record<string, unknown>;
|
|
261
|
+
/**
|
|
262
|
+
* Explicit streaming source. When provided, the helper iterates this and
|
|
263
|
+
* ignores `component`. Useful when the production code already produces a
|
|
264
|
+
* stream and the test wants to feed a deterministic sequence.
|
|
265
|
+
*/
|
|
266
|
+
readonly dataSource?: RscStreamSource;
|
|
267
|
+
/**
|
|
268
|
+
* Markup shown while the (first) chunk is pending. Captured as
|
|
269
|
+
* `env.fallback` so tests can assert that `<Suspense fallback={...}>`
|
|
270
|
+
* surfaces the right loading state before the data arrives.
|
|
271
|
+
*/
|
|
272
|
+
readonly suspenseFallback?: RscNode;
|
|
273
|
+
/**
|
|
274
|
+
* Hard timeout (ms) for the whole stream. If the source has not completed
|
|
275
|
+
* by this deadline, the helper resolves with `env.timedOut = true` and the
|
|
276
|
+
* chunks collected so far. Default 5000ms.
|
|
277
|
+
*/
|
|
278
|
+
readonly streamingTimeout?: number;
|
|
279
|
+
/**
|
|
280
|
+
* Test-side error injection. When set, the helper short-circuits before
|
|
281
|
+
* iterating the source and routes the error into `env.errorBoundary` —
|
|
282
|
+
* the same shape a production `error.tsx` boundary would see.
|
|
283
|
+
*/
|
|
284
|
+
readonly injectError?: unknown;
|
|
285
|
+
}
|
|
286
|
+
interface SetupNextRscEnvResult {
|
|
287
|
+
/**
|
|
288
|
+
* Streaming chunks in arrival order. For a Suspense boundary, the first
|
|
289
|
+
* chunk is typically the fallback markup and the last chunk is the
|
|
290
|
+
* resolved subtree.
|
|
291
|
+
*/
|
|
292
|
+
readonly chunks: RscNode[];
|
|
293
|
+
/**
|
|
294
|
+
* The fallback markup captured before the source produced its first
|
|
295
|
+
* non-fallback chunk. `null` when the test did not pass `suspenseFallback`
|
|
296
|
+
* or when the source resolved synchronously without an explicit fallback.
|
|
297
|
+
*/
|
|
298
|
+
readonly fallback: RscNode | null;
|
|
299
|
+
/**
|
|
300
|
+
* The last chunk yielded by the source — the markup a real Next.js page
|
|
301
|
+
* would settle on after streaming finishes. `null` when the source threw
|
|
302
|
+
* or timed out before producing any chunk.
|
|
303
|
+
*/
|
|
304
|
+
readonly resolved: RscNode | null;
|
|
305
|
+
/**
|
|
306
|
+
* Set when the component or source threw, or when `injectError` was
|
|
307
|
+
* provided. Mirrors the value a production `error.tsx` boundary receives.
|
|
308
|
+
* `null` for happy-path streams.
|
|
309
|
+
*/
|
|
310
|
+
readonly errorBoundary: RscErrorBoundarySignal | null;
|
|
311
|
+
/**
|
|
312
|
+
* `true` when `streamingTimeout` elapsed before the source completed.
|
|
313
|
+
* `chunks` still contains any chunks that arrived before the deadline.
|
|
314
|
+
*/
|
|
315
|
+
readonly timedOut: boolean;
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Drive an async RSC stream through a single Suspense boundary and capture
|
|
319
|
+
* every chunk + the fallback + the resolved subtree + any error-boundary
|
|
320
|
+
* trigger. The helper is deterministic — chunks arrive in the order the
|
|
321
|
+
* source yields them, and the timeout is wall-clock-bounded so tests cannot
|
|
322
|
+
* hang on a stuck stream.
|
|
323
|
+
*
|
|
324
|
+
* Typical usage:
|
|
325
|
+
*
|
|
326
|
+
* const env = await setupNextRscEnv({
|
|
327
|
+
* dataSource: streamItems(),
|
|
328
|
+
* suspenseFallback: <Skeleton />,
|
|
329
|
+
* streamingTimeout: 1000,
|
|
330
|
+
* });
|
|
331
|
+
* expect(env.fallback).toEqual(<Skeleton />);
|
|
332
|
+
* expect(env.chunks).toHaveLength(3);
|
|
333
|
+
* expect(env.resolved).toEqual(<ItemList items={items} />);
|
|
334
|
+
* expect(env.errorBoundary).toBeNull();
|
|
335
|
+
* expect(env.timedOut).toBe(false);
|
|
336
|
+
*/
|
|
337
|
+
declare function setupNextRscEnv(opts?: SetupNextRscEnvOptions): Promise<SetupNextRscEnvResult>;
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Advanced Next.js semantics — target-neutral axis SSOT.
|
|
341
|
+
*
|
|
342
|
+
* The helpers model App Router, Pages Router, and Edge Runtime behavior as
|
|
343
|
+
* pure state machines. Tests can assert the neutral event while still seeing
|
|
344
|
+
* a target-specific dialect through providerEventName.
|
|
345
|
+
*/
|
|
346
|
+
type NextTarget = 'app-router' | 'pages-router' | 'edge-runtime';
|
|
347
|
+
type NextAxis = 'server-action-advanced' | 'partial-prerendering' | 'interception-routes' | 'parallel-routes-advanced';
|
|
348
|
+
type NeutralEventName = 'action.form_submitted' | 'action.revalidate_path' | 'action.revalidate_tag' | 'action.redirected' | 'ppr.static_shell_rendered' | 'ppr.dynamic_hole_opened' | 'ppr.streaming_boundary_flushed' | 'ppr.completed' | 'intercept.current_segment' | 'intercept.parent_segment' | 'intercept.root_catchall' | 'intercept.modal_opened' | 'parallel.default_rendered' | 'parallel.loading_rendered' | 'parallel.error_boundary_captured' | 'parallel.slot_navigated';
|
|
349
|
+
interface AxisStep<TState extends string> {
|
|
350
|
+
neutralEvent: NeutralEventName;
|
|
351
|
+
providerEvent: string;
|
|
352
|
+
state: TState;
|
|
353
|
+
amountCents: number;
|
|
354
|
+
metadata: Record<string, string | number | boolean>;
|
|
355
|
+
}
|
|
356
|
+
declare function providerEventName(target: NextTarget, neutral: NeutralEventName): string;
|
|
357
|
+
|
|
358
|
+
type ServerActionAdvancedState = 'idle' | 'submitted' | 'path-revalidated' | 'tag-revalidated' | 'redirected';
|
|
359
|
+
interface ServerActionAdvancedSession {
|
|
360
|
+
target: NextTarget;
|
|
361
|
+
actionId: string;
|
|
362
|
+
state: ServerActionAdvancedState;
|
|
363
|
+
form: Record<string, string>;
|
|
364
|
+
revalidatedPaths: string[];
|
|
365
|
+
revalidatedTags: string[];
|
|
366
|
+
redirectUrl: string | null;
|
|
367
|
+
history: AxisStep<ServerActionAdvancedState>[];
|
|
368
|
+
}
|
|
369
|
+
declare function startServerActionAdvanced(input: {
|
|
370
|
+
target: NextTarget;
|
|
371
|
+
actionId: string;
|
|
372
|
+
}): ServerActionAdvancedSession;
|
|
373
|
+
declare function submitFormAction(session: ServerActionAdvancedSession, form: Record<string, string>): AxisStep<ServerActionAdvancedState>;
|
|
374
|
+
declare function revalidateActionPath(session: ServerActionAdvancedSession, path: string): AxisStep<ServerActionAdvancedState>;
|
|
375
|
+
declare function revalidateActionTag(session: ServerActionAdvancedSession, tag: string): AxisStep<ServerActionAdvancedState>;
|
|
376
|
+
declare function redirectAction(session: ServerActionAdvancedSession, url: string): AxisStep<ServerActionAdvancedState>;
|
|
377
|
+
|
|
378
|
+
type PartialPrerenderingState = 'idle' | 'static-shell' | 'dynamic-hole' | 'streaming' | 'completed';
|
|
379
|
+
interface PartialPrerenderingSession {
|
|
380
|
+
target: NextTarget;
|
|
381
|
+
routeId: string;
|
|
382
|
+
state: PartialPrerenderingState;
|
|
383
|
+
shellHtml: string | null;
|
|
384
|
+
dynamicHoles: Map<string, string>;
|
|
385
|
+
streamedBoundaries: string[];
|
|
386
|
+
history: AxisStep<PartialPrerenderingState>[];
|
|
387
|
+
}
|
|
388
|
+
declare function startPartialPrerendering(input: {
|
|
389
|
+
target: NextTarget;
|
|
390
|
+
routeId: string;
|
|
391
|
+
}): PartialPrerenderingSession;
|
|
392
|
+
declare function renderStaticShell(session: PartialPrerenderingSession, html: string): AxisStep<PartialPrerenderingState>;
|
|
393
|
+
declare function openDynamicHole(session: PartialPrerenderingSession, input: {
|
|
394
|
+
holeId: string;
|
|
395
|
+
fallback: string;
|
|
396
|
+
}): AxisStep<PartialPrerenderingState>;
|
|
397
|
+
declare function flushStreamingBoundary(session: PartialPrerenderingSession, input: {
|
|
398
|
+
holeId: string;
|
|
399
|
+
html: string;
|
|
400
|
+
}): AxisStep<PartialPrerenderingState>;
|
|
401
|
+
declare function completePartialPrerendering(session: PartialPrerenderingSession): AxisStep<PartialPrerenderingState>;
|
|
402
|
+
|
|
403
|
+
type InterceptionRoutesState = 'idle' | 'current' | 'parent' | 'root' | 'modal-open';
|
|
404
|
+
type InterceptionMatcher = '(.)' | '(..)' | '(...)';
|
|
405
|
+
interface InterceptionRoutesSession {
|
|
406
|
+
target: NextTarget;
|
|
407
|
+
routeId: string;
|
|
408
|
+
state: InterceptionRoutesState;
|
|
409
|
+
matches: Array<{
|
|
410
|
+
matcher: InterceptionMatcher;
|
|
411
|
+
from: string;
|
|
412
|
+
to: string;
|
|
413
|
+
}>;
|
|
414
|
+
modalRoute: string | null;
|
|
415
|
+
history: AxisStep<InterceptionRoutesState>[];
|
|
416
|
+
}
|
|
417
|
+
declare function startInterceptionRoutes(input: {
|
|
418
|
+
target: NextTarget;
|
|
419
|
+
routeId: string;
|
|
420
|
+
}): InterceptionRoutesSession;
|
|
421
|
+
declare function interceptCurrentSegment(session: InterceptionRoutesSession, from: string, to: string): AxisStep<InterceptionRoutesState>;
|
|
422
|
+
declare function interceptParentSegment(session: InterceptionRoutesSession, from: string, to: string): AxisStep<InterceptionRoutesState>;
|
|
423
|
+
declare function interceptRootCatchall(session: InterceptionRoutesSession, from: string, to: string): AxisStep<InterceptionRoutesState>;
|
|
424
|
+
declare function openInterceptedModal(session: InterceptionRoutesSession, modalRoute: string): AxisStep<InterceptionRoutesState>;
|
|
425
|
+
|
|
426
|
+
type ParallelRoutesAdvancedState = 'idle' | 'default-rendered' | 'loading-rendered' | 'error-captured' | 'slot-navigated';
|
|
427
|
+
interface ParallelRoutesAdvancedSession {
|
|
428
|
+
target: NextTarget;
|
|
429
|
+
layoutId: string;
|
|
430
|
+
state: ParallelRoutesAdvancedState;
|
|
431
|
+
slots: Map<string, string>;
|
|
432
|
+
loadingSlots: Set<string>;
|
|
433
|
+
errors: Array<{
|
|
434
|
+
slot: string;
|
|
435
|
+
message: string;
|
|
436
|
+
}>;
|
|
437
|
+
history: AxisStep<ParallelRoutesAdvancedState>[];
|
|
438
|
+
}
|
|
439
|
+
declare function startParallelRoutesAdvanced(input: {
|
|
440
|
+
target: NextTarget;
|
|
441
|
+
layoutId: string;
|
|
442
|
+
}): ParallelRoutesAdvancedSession;
|
|
443
|
+
declare function renderDefaultSlot(session: ParallelRoutesAdvancedSession, slot: string, html: string): AxisStep<ParallelRoutesAdvancedState>;
|
|
444
|
+
declare function renderLoadingState(session: ParallelRoutesAdvancedSession, slot: string): AxisStep<ParallelRoutesAdvancedState>;
|
|
445
|
+
declare function captureParallelError(session: ParallelRoutesAdvancedSession, input: {
|
|
446
|
+
slot: string;
|
|
447
|
+
error: Error | string;
|
|
448
|
+
}): AxisStep<ParallelRoutesAdvancedState>;
|
|
449
|
+
declare function navigateSlot(session: ParallelRoutesAdvancedSession, input: {
|
|
450
|
+
slot: string;
|
|
451
|
+
from: string;
|
|
452
|
+
to: string;
|
|
453
|
+
}): AxisStep<ParallelRoutesAdvancedState>;
|
|
454
|
+
|
|
455
|
+
interface FidelityRow {
|
|
456
|
+
provider: NextTarget;
|
|
457
|
+
axis: NextAxis;
|
|
458
|
+
neutralEvents: NeutralEventName[];
|
|
459
|
+
providerEvents: string[];
|
|
460
|
+
}
|
|
461
|
+
interface FidelityCoverage {
|
|
462
|
+
providers: NextTarget[];
|
|
463
|
+
axes: NextAxis[];
|
|
464
|
+
rows: FidelityRow[];
|
|
465
|
+
}
|
|
466
|
+
declare const NEXT_AXIS_TO_EVENTS: Record<NextAxis, NeutralEventName[]>;
|
|
467
|
+
declare function collectFidelityCoverage(providers?: NextTarget[]): FidelityCoverage;
|
|
468
|
+
|
|
469
|
+
type KiwaTestMode = 'mock' | 'real';
|
|
470
|
+
interface ResolvedMode {
|
|
471
|
+
mode: KiwaTestMode;
|
|
472
|
+
provider: NextTarget;
|
|
473
|
+
reason: 'default-mock' | 'kiwa-mode-real' | 'missing-key' | 'invalid-mode';
|
|
474
|
+
}
|
|
475
|
+
declare function resolveMode(provider: NextTarget, env?: Record<string, string | undefined>): ResolvedMode;
|
|
476
|
+
declare function resolveAllModes(env?: Record<string, string | undefined>): ResolvedMode[];
|
|
477
|
+
declare function assertMode(provider: NextTarget, expected: KiwaTestMode, env?: Record<string, string | undefined>): void;
|
|
478
|
+
|
|
479
|
+
export { type AxisStep, type CookieJar, type DefaultFallbackComponent, FORBIDDEN_SYMBOL, type FidelityCoverage, type FidelityRow, type ForbiddenSignal, type InterceptionMatch, type InterceptionMatcher, type InterceptionRoutesSession, type InterceptionRoutesState, type InvokeMiddlewareOptions, type InvokeMiddlewareResult, type InvokeParallelRoutesOptions, type InvokeParallelRoutesResult, type KiwaTestMode, MIDDLEWARE_ACTION_SYMBOL, type MiddlewareAction, type MiddlewareActionKind, type MiddlewareEnv, type MiddlewareFunction, type MiddlewareRequest, NEXT_AXIS_TO_EVENTS, NOT_FOUND_SYMBOL, type NeutralEventName, type NextAxis, type NextTarget, type NotFoundSignal, PARALLEL_INTERCEPTION_SYMBOL, type ParallelLayoutChildren, type ParallelLayoutFunction, type ParallelRoutesAdvancedSession, type ParallelRoutesAdvancedState, type PartialPrerenderingSession, type PartialPrerenderingState, REDIRECT_SYMBOL, RSC_ERROR_BOUNDARY_SYMBOL, RSC_REDIRECT_SYMBOL, type RedirectSignal, type RenderServerComponentOptions, type RenderServerComponentResult, type ResolvedMode, type RscElement, type RscErrorBoundarySignal, type RscNode, type RscRedirectSignal, type RscSignal, type RscStreamSource, type ServerActionAdvancedSession, type ServerActionAdvancedState, type ServerActionEnv, type ServerActionFunction, type ServerActionInvocation, type ServerActionResult, type SetupNextRscEnvOptions, type SetupNextRscEnvResult, type SlotComponent, type SlotInput, type SlotRenderResult, assertMode, captureParallelError, collectFidelityCoverage, completePartialPrerendering, findAll, flushStreamingBoundary, interceptCurrentSegment, interceptParentSegment, interceptRootCatchall, invokeMiddleware, invokeParallelRoutes, invokeServerAction, middlewareActions, navigateSlot, openDynamicHole, openInterceptedModal, providerEventName, redirectAction, renderDefaultSlot, renderLoadingState, renderServerComponent, renderStaticShell, resolveAllModes, resolveMode, revalidateActionPath, revalidateActionTag, setupNextRscEnv, startInterceptionRoutes, startParallelRoutesAdvanced, startPartialPrerendering, startServerActionAdvanced, submitFormAction, textContent };
|