A Jest preset to painlessly test your Expo / React Native apps.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Jest Expo provides comprehensive testing support for React Server Components, including specialized presets, utilities for rendering RSC to flight data, and custom Jest matchers for validating RSC output.
Specialized Jest preset configurations for testing React Server Components across different platforms.
// In package.json or jest.config.js
{
"jest": {
"preset": "jest-expo/rsc"
}
}Features:
// Available RSC platform presets
"jest-expo/rsc/ios" // RSC iOS testing preset
"jest-expo/rsc/android" // RSC Android testing preset
"jest-expo/rsc/web" // RSC web testing presetRSC iOS Preset:
.rsc.ios.snap files)ios with React Server Component modegetIOSPreset({ isReactServer: true })RSC Android Preset:
.rsc.android.snap files)android with React Server Component modegetAndroidPreset({ isReactServer: true })RSC Web Preset:
.rsc.web.snap files)web with React Server Component modegetWebPreset({ isReactServer: true })Common RSC Features:
__rsc_tests__ directoriesCore utilities for rendering React Server Components to flight data format for testing.
/**
* Convert ReadableStream to string for testing
* @param stream - ReadableStream to convert
* @returns Promise that resolves to string content
*/
function streamToString(stream: ReadableStream): Promise<string>;Usage Example:
import { streamToString } from "jest-expo/src/rsc-utils";
test("stream conversion", async () => {
const stream = new ReadableStream({
start(controller) {
controller.enqueue("Hello");
controller.enqueue(" World");
controller.close();
}
});
const result = await streamToString(stream);
expect(result).toBe("Hello World");
});/**
* Render JSX to RSC payload string
* @param jsx - React element to render
* @param throwOnError - Whether to throw errors during rendering
* @returns Promise that resolves to RSC flight string
*/
function renderJsxToFlightStringAsync(
jsx: React.ReactNode,
throwOnError?: boolean
): Promise<string>;Usage Example:
import { renderJsxToFlightStringAsync } from "jest-expo/src/rsc-utils";
test("RSC rendering", async () => {
const element = <div>Hello RSC</div>;
const flightString = await renderJsxToFlightStringAsync(element);
expect(flightString).toContain('"type":"div"');
expect(flightString).toContain('Hello RSC');
});/**
* Render JSX to RSC ReadableStream
* @param jsx - React element to render
* @param options - Rendering options
* @returns Object containing ReadableStream and client boundaries
*/
function renderJsxToReadableStream(
jsx: React.ReactNode,
options?: RSCRenderOptions
): RSCRenderResult;
interface RSCRenderOptions {
onError?: (error: Error) => void;
}
interface RSCRenderResult {
stream: ReadableStream;
clientBoundaries: string[];
}Usage Example:
import { renderJsxToReadableStream, streamToString } from "jest-expo/src/rsc-utils";
test("RSC stream rendering", async () => {
const element = <div>Streaming RSC</div>;
const { stream, clientBoundaries } = renderJsxToReadableStream(element);
const result = await streamToString(stream);
expect(result).toContain("Streaming RSC");
expect(clientBoundaries).toEqual([]);
});Custom Jest matchers specifically designed for testing React Server Components flight data.
/**
* Compare RSC flight data to expected string
* @param expectedFlight - Expected RSC flight string
*/
expect(data: React.ReactNode | ReadableStream).toMatchFlight(expectedFlight: string);Features:
Usage Example:
describe("RSC flight matching", () => {
test("JSX to flight comparison", () => {
const element = <div className="test">Content</div>;
const expectedFlight = '{"type":"div","props":{"className":"test"},"children":["Content"]}';
expect(element).toMatchFlight(expectedFlight);
});
test("stream to flight comparison", async () => {
const element = <span>Stream test</span>;
const stream = renderJsxToReadableStream(element);
const expectedFlight = '{"type":"span","props":{},"children":["Stream test"]}';
expect(stream).toMatchFlight(expectedFlight);
});
});/**
* Compare RSC flight data to snapshot file
*/
expect(data: React.ReactNode | ReadableStream).toMatchFlightSnapshot();Features:
--updateSnapshot.rsc.ios.snap, etc.)Usage Example:
describe("RSC snapshot testing", () => {
test("component flight snapshot", () => {
const element = (
<div>
<h1>Title</h1>
<p>Description</p>
</div>
);
expect(element).toMatchFlightSnapshot();
});
test("async component snapshot", async () => {
const AsyncComponent = async () => {
const data = await fetchData();
return <div>{data.title}</div>;
};
const element = <AsyncComponent />;
expect(element).toMatchFlightSnapshot();
});
});RSC presets automatically configure the test environment with necessary globals:
// Automatically configured in RSC presets
global.__DEV__ = true;
global.Blob = Blob;
global.File = File;
global.ReadableStream = ReadableStream;
global.WritableStream = WritableStream;
global.TransformStream = TransformStream;
global.TextDecoder = TextDecoder;
global.TextEncoder = TextEncoder;// Custom RSC test setup
import { expect } from "@jest/globals";
// Extend Jest matchers with RSC matchers
declare global {
namespace jest {
interface Matchers<R> {
toMatchFlight(expectedFlight: string): R;
toMatchFlightSnapshot(): R;
}
}
}describe("Server Components", () => {
test("async server component", async () => {
const ServerComponent = async ({ userId }) => {
const user = await fetchUser(userId);
return <div>Hello {user.name}</div>;
};
const element = <ServerComponent userId="123" />;
expect(element).toMatchFlightSnapshot();
});
});describe("Client/Server boundaries", () => {
test("server component with client children", () => {
const ServerWrapper = ({ children }) => (
<div className="server-wrapper">{children}</div>
);
const ClientComponent = () => <button>Click me</button>;
const element = (
<ServerWrapper>
<ClientComponent />
</ServerWrapper>
);
expect(element).toMatchFlightSnapshot();
});
});// RSC utility types
interface RSCRenderOptions {
onError?: (error: Error) => void;
identifierPrefix?: string;
}
// Jest matcher interfaces
interface JestExpoMatchers<R> extends Record<string, any> {
/**
* Compare RSC flight data to expected string
*/
toMatchFlight(expectedFlight: string): R;
/**
* Compare RSC flight data to snapshot file
*/
toMatchFlightSnapshot(): R;
}
// RSC utility function types
declare function streamToString(stream: ReadableStream): Promise<string>;
declare function renderJsxToFlightStringAsync(
jsx: React.ReactNode,
throwOnError?: boolean
): Promise<string>;
declare function renderJsxToReadableStream(
jsx: React.ReactNode,
options?: RSCRenderOptions
): RSCRenderResult;
// RSC utility interfaces
interface RSCRenderOptions {
onError?: (error: Error) => void;
}
interface RSCRenderResult {
stream: ReadableStream;
clientBoundaries: string[];
}
// Global extensions for RSC testing
declare global {
namespace jest {
interface Matchers<R> extends JestExpoMatchers<R> {}
interface Expect extends JestExpoMatchers<any> {}
interface InverseAsymmetricMatchers extends Expect {}
}
}
// React Server Component types
type ServerComponent<P = {}> = (props: P) => Promise<React.ReactElement>;
type ClientComponent<P = {}> = React.ComponentType<P>;
interface RSCFlightData {
type: string;
props: Record<string, any>;
children?: React.ReactNode[];
}