Skip to content

Commit 00c2769

Browse files
committed
fix tests
1 parent c6e46a1 commit 00c2769

File tree

2 files changed

+116
-114
lines changed

2 files changed

+116
-114
lines changed

src/__tests__/suspense-fake-timers.test.tsx

Lines changed: 67 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ jest.useFakeTimers();
77

88
const testGateReact19 = React.version.startsWith('19.') ? test : test.skip;
99

10-
function Suspending({ promise }: { promise: Promise<unknown> }) {
10+
function Suspending({ promise, testID }: { promise: Promise<unknown>; testID: string }) {
1111
React.use(promise);
12-
return <View testID="content" />;
12+
return <View testID={testID} />;
1313
}
1414

1515
testGateReact19('resolves manually-controlled promise', async () => {
@@ -21,7 +21,7 @@ testGateReact19('resolves manually-controlled promise', async () => {
2121
await renderAsync(
2222
<View>
2323
<React.Suspense fallback={<Text>Loading...</Text>}>
24-
<Suspending promise={promise} />
24+
<Suspending promise={promise} testID="content" />
2525
<View testID="sibling" />
2626
</React.Suspense>
2727
</View>,
@@ -45,7 +45,7 @@ testGateReact19('resolves timer-controlled promise', async () => {
4545
await renderAsync(
4646
<View>
4747
<React.Suspense fallback={<Text>Loading...</Text>}>
48-
<Suspending promise={promise} />
48+
<Suspending promise={promise} testID="content" />
4949
<View testID="sibling" />
5050
</React.Suspense>
5151
</View>,
@@ -60,48 +60,11 @@ testGateReact19('resolves timer-controlled promise', async () => {
6060
expect(screen.queryByText('Loading...')).not.toBeOnTheScreen();
6161
});
6262

63-
function DelayedSuspending({ delay, id }: { delay: number; id: string }) {
64-
let resolvePromise: (value: string) => void;
65-
const promise = React.useMemo(() =>
66-
new Promise<string>((resolve) => {
67-
resolvePromise = resolve;
68-
setTimeout(() => resolve(`data-${id}`), delay);
69-
}), [delay, id]
70-
);
71-
72-
const data = React.use(promise);
73-
return <View testID={`delayed-content-${data}`} />;
74-
}
75-
76-
testGateReact19('handles timer-based promises with fake timers', async () => {
77-
let resolveManual: (value: unknown) => void;
78-
const manualPromise = new Promise((resolve) => {
79-
resolveManual = resolve;
80-
});
81-
82-
await renderAsync(
83-
<View>
84-
<React.Suspense fallback={<Text>Manual Loading...</Text>}>
85-
<Suspending promise={manualPromise} />
86-
</React.Suspense>
87-
<View testID="outside-suspense" />
88-
</View>,
89-
);
90-
91-
expect(screen.getByText('Manual Loading...')).toBeOnTheScreen();
92-
expect(screen.getByTestId('outside-suspense')).toBeOnTheScreen();
93-
94-
// eslint-disable-next-line require-await
95-
await act(async () => resolveManual(null));
96-
expect(screen.getByTestId('content')).toBeOnTheScreen();
97-
expect(screen.queryByText('Manual Loading...')).not.toBeOnTheScreen();
98-
});
99-
10063
class ErrorBoundary extends React.Component<
101-
{ children: React.ReactNode; fallback?: React.ReactNode },
64+
{ children: React.ReactNode; fallback: React.ReactNode },
10265
{ hasError: boolean }
10366
> {
104-
constructor(props: { children: React.ReactNode; fallback?: React.ReactNode }) {
67+
constructor(props: { children: React.ReactNode; fallback: React.ReactNode }) {
10568
super(props);
10669
this.state = { hasError: false };
10770
}
@@ -111,67 +74,98 @@ class ErrorBoundary extends React.Component<
11174
}
11275

11376
render() {
114-
if (this.state.hasError) {
115-
return this.props.fallback || <Text>Something went wrong.</Text>;
116-
}
117-
118-
return this.props.children;
77+
return this.state.hasError ? this.props.fallback : this.props.children;
11978
}
12079
}
12180

122-
testGateReact19('handles suspense with error boundary in fake timers', async () => {
81+
testGateReact19('handles promise rejection with error boundary', async () => {
12382
let rejectPromise: (error: Error) => void;
124-
const promise = new Promise<unknown>((_, reject) => {
125-
rejectPromise = reject;
126-
});
83+
const promise = new Promise<unknown>((_, reject) => { rejectPromise = reject; });
12784

12885
await renderAsync(
12986
<ErrorBoundary fallback={<Text>Error occurred</Text>}>
13087
<React.Suspense fallback={<Text>Loading...</Text>}>
131-
<Suspending promise={promise} />
88+
<Suspending promise={promise} testID="content" />
13289
</React.Suspense>
13390
</ErrorBoundary>,
13491
);
13592

13693
expect(screen.getByText('Loading...')).toBeOnTheScreen();
94+
expect(screen.queryByTestId('content')).not.toBeOnTheScreen();
13795

13896
// eslint-disable-next-line require-await
13997
await act(async () => rejectPromise(new Error('Test error')));
14098

14199
expect(screen.getByText('Error occurred')).toBeOnTheScreen();
142100
expect(screen.queryByText('Loading...')).not.toBeOnTheScreen();
101+
expect(screen.queryByTestId('error-content')).not.toBeOnTheScreen();
143102
});
144103

145-
function MultiComponentSuspense() {
146-
let resolveFirst: (value: unknown) => void;
147-
let resolveSecond: (value: unknown) => void;
104+
testGateReact19('handles multiple suspending components', async () => {
105+
let resolvePromise1: (value: unknown) => void;
106+
let resolvePromise2: (value: unknown) => void;
148107

149-
const firstPromise = new Promise((resolve) => {
150-
resolveFirst = resolve;
151-
});
152-
const secondPromise = new Promise((resolve) => {
153-
resolveSecond = resolve;
154-
});
108+
const promise1 = new Promise((resolve) => { resolvePromise1 = resolve; });
109+
const promise2 = new Promise((resolve) => { resolvePromise2 = resolve; });
110+
111+
await renderAsync(
112+
<View>
113+
<React.Suspense fallback={<Text>Loading...</Text>}>
114+
<Suspending promise={promise1} testID="content-1" />
115+
<Suspending promise={promise2} testID="content-2" />
116+
</React.Suspense>
117+
</View>
118+
);
119+
120+
expect(screen.getByText('Loading...')).toBeOnTheScreen();
121+
expect(screen.queryByTestId('content-1')).not.toBeOnTheScreen();
122+
expect(screen.queryByTestId('content-2')).not.toBeOnTheScreen();
123+
124+
// eslint-disable-next-line require-await
125+
await act(async () => resolvePromise1(null));
126+
expect(screen.getByText('Loading...')).toBeOnTheScreen();
127+
expect(screen.queryByTestId('content-1')).not.toBeOnTheScreen();
128+
expect(screen.queryByTestId('content-2')).not.toBeOnTheScreen();
129+
130+
// eslint-disable-next-line require-await
131+
await act(async () => resolvePromise2(null));
132+
expect(screen.getByTestId('content-1')).toBeOnTheScreen();
133+
expect(screen.getByTestId('content-2')).toBeOnTheScreen();
134+
expect(screen.queryByText('Loading...')).not.toBeOnTheScreen();
135+
});
136+
137+
138+
testGateReact19('handles multiple suspense boundaries independently', async () => {
139+
let resolvePromise1: (value: unknown) => void;
140+
let resolvePromise2: (value: unknown) => void;
155141

156-
return (
142+
const promise1 = new Promise((resolve) => { resolvePromise1 = resolve; });
143+
const promise2 = new Promise((resolve) => { resolvePromise2 = resolve; });
144+
145+
await renderAsync(
157146
<View>
158147
<React.Suspense fallback={<Text>First Loading...</Text>}>
159-
<Suspending promise={firstPromise} />
148+
<Suspending promise={promise1} testID="content-1" />
160149
</React.Suspense>
161150
<React.Suspense fallback={<Text>Second Loading...</Text>}>
162-
<View testID="second-boundary">
163-
<Suspending promise={secondPromise} />
164-
</View>
151+
<Suspending promise={promise2} testID="content-2" />
165152
</React.Suspense>
166153
</View>
167154
);
168-
}
169155

170-
testGateReact19('handles multiple independent suspense boundaries', async () => {
171-
await renderAsync(<MultiComponentSuspense />);
172-
173156
expect(screen.getByText('First Loading...')).toBeOnTheScreen();
174157
expect(screen.getByText('Second Loading...')).toBeOnTheScreen();
175-
expect(screen.queryByTestId('content')).not.toBeOnTheScreen();
176-
expect(screen.queryByTestId('second-boundary')).not.toBeOnTheScreen();
158+
expect(screen.queryByTestId('content-1')).not.toBeOnTheScreen();
159+
expect(screen.queryByTestId('content-2')).not.toBeOnTheScreen();
160+
161+
// eslint-disable-next-line require-await
162+
await act(async () => resolvePromise1(null));
163+
expect(screen.getByTestId('content-1')).toBeOnTheScreen();
164+
expect(screen.queryByText('First Loading...')).not.toBeOnTheScreen();
165+
expect(screen.getByText('Second Loading...')).toBeOnTheScreen();
166+
167+
// eslint-disable-next-line require-await
168+
await act(async () => resolvePromise2(null));
169+
expect(screen.getByTestId('content-2')).toBeOnTheScreen();
170+
expect(screen.queryByText('Second Loading...')).not.toBeOnTheScreen();
177171
});

src/__tests__/suspense.test.tsx

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { act, renderAsync, screen } from '..';
55

66
const testGateReact19 = React.version.startsWith('19.') ? test : test.skip;
77

8-
function Suspending({ promise }: { promise: Promise<unknown> }) {
8+
function Suspending({ promise, testID }: { promise: Promise<unknown>; testID: string }) {
99
React.use(promise);
10-
return <View testID="content" />;
10+
return <View testID={testID} />;
1111
}
1212

1313
testGateReact19('resolves manually-controlled promise', async () => {
@@ -19,7 +19,7 @@ testGateReact19('resolves manually-controlled promise', async () => {
1919
await renderAsync(
2020
<View>
2121
<React.Suspense fallback={<Text>Loading...</Text>}>
22-
<Suspending promise={promise} />
22+
<Suspending promise={promise} testID="content" />
2323
<View testID="sibling" />
2424
</React.Suspense>
2525
</View>,
@@ -43,7 +43,7 @@ testGateReact19('resolves timer-controlled promise', async () => {
4343
await renderAsync(
4444
<View>
4545
<React.Suspense fallback={<Text>Loading...</Text>}>
46-
<Suspending promise={promise} />
46+
<Suspending promise={promise} testID="content" />
4747
<View testID="sibling" />
4848
</React.Suspense>
4949
</View>,
@@ -57,16 +57,11 @@ testGateReact19('resolves timer-controlled promise', async () => {
5757
expect(screen.queryByText('Loading...')).not.toBeOnTheScreen();
5858
});
5959

60-
function SuspendingWithError({ promise }: { promise: Promise<unknown> }) {
61-
React.use(promise);
62-
return <View testID="error-content" />;
63-
}
64-
6560
class ErrorBoundary extends React.Component<
66-
{ children: React.ReactNode; fallback?: React.ReactNode },
61+
{ children: React.ReactNode; fallback: React.ReactNode },
6762
{ hasError: boolean }
6863
> {
69-
constructor(props: { children: React.ReactNode; fallback?: React.ReactNode }) {
64+
constructor(props: { children: React.ReactNode; fallback: React.ReactNode }) {
7065
super(props);
7166
this.state = { hasError: false };
7267
}
@@ -76,11 +71,7 @@ class ErrorBoundary extends React.Component<
7671
}
7772

7873
render() {
79-
if (this.state.hasError) {
80-
return this.props.fallback || <Text>Something went wrong.</Text>;
81-
}
82-
83-
return this.props.children;
74+
return this.state.hasError ? this.props.fallback : this.props.children;
8475
}
8576
}
8677

@@ -93,13 +84,13 @@ testGateReact19('handles promise rejection with error boundary', async () => {
9384
await renderAsync(
9485
<ErrorBoundary fallback={<Text>Error occurred</Text>}>
9586
<React.Suspense fallback={<Text>Loading...</Text>}>
96-
<SuspendingWithError promise={promise} />
87+
<Suspending promise={promise} testID="content" />
9788
</React.Suspense>
9889
</ErrorBoundary>,
9990
);
10091

10192
expect(screen.getByText('Loading...')).toBeOnTheScreen();
102-
expect(screen.queryByTestId('error-content')).not.toBeOnTheScreen();
93+
expect(screen.queryByTestId('content')).not.toBeOnTheScreen();
10394

10495
// eslint-disable-next-line require-await
10596
await act(async () => rejectPromise(new Error('Test error')));
@@ -109,54 +100,71 @@ testGateReact19('handles promise rejection with error boundary', async () => {
109100
expect(screen.queryByTestId('error-content')).not.toBeOnTheScreen();
110101
});
111102

112-
function NestedSuspending({ promise }: { promise: Promise<unknown> }) {
113-
React.use(promise);
114-
return (
115-
<React.Suspense fallback={<Text>Inner Loading...</Text>}>
116-
<View testID="inner-resolved" />
117-
</React.Suspense>
103+
testGateReact19('handles multiple suspending components', async () => {
104+
let resolvePromise1: (value: unknown) => void;
105+
let resolvePromise2: (value: unknown) => void;
106+
107+
const promise1 = new Promise((resolve) => { resolvePromise1 = resolve; });
108+
const promise2 = new Promise((resolve) => { resolvePromise2 = resolve; });
109+
110+
await renderAsync(
111+
<View>
112+
<React.Suspense fallback={<Text>Loading...</Text>}>
113+
<Suspending promise={promise1} testID="content-1" />
114+
<Suspending promise={promise2} testID="content-2" />
115+
</React.Suspense>
116+
</View>
118117
);
119-
}
118+
119+
expect(screen.getByText('Loading...')).toBeOnTheScreen();
120+
expect(screen.queryByTestId('content-1')).not.toBeOnTheScreen();
121+
expect(screen.queryByTestId('content-2')).not.toBeOnTheScreen();
122+
123+
// eslint-disable-next-line require-await
124+
await act(async () => resolvePromise1(null));
125+
expect(screen.getByText('Loading...')).toBeOnTheScreen();
126+
expect(screen.queryByTestId('content-1')).not.toBeOnTheScreen();
127+
expect(screen.queryByTestId('content-2')).not.toBeOnTheScreen();
128+
129+
// eslint-disable-next-line require-await
130+
await act(async () => resolvePromise2(null));
131+
expect(screen.getByTestId('content-1')).toBeOnTheScreen();
132+
expect(screen.getByTestId('content-2')).toBeOnTheScreen();
133+
expect(screen.queryByText('Loading...')).not.toBeOnTheScreen();
134+
});
135+
120136

121137
testGateReact19('handles multiple suspense boundaries independently', async () => {
122138
let resolvePromise1: (value: unknown) => void;
123139
let resolvePromise2: (value: unknown) => void;
124140

125-
const promise1 = new Promise((resolve) => {
126-
resolvePromise1 = resolve;
127-
});
128-
const promise2 = new Promise((resolve) => {
129-
resolvePromise2 = resolve;
130-
});
141+
const promise1 = new Promise((resolve) => { resolvePromise1 = resolve; });
142+
const promise2 = new Promise((resolve) => { resolvePromise2 = resolve; });
131143

132144
await renderAsync(
133145
<View>
134146
<React.Suspense fallback={<Text>First Loading...</Text>}>
135-
<Suspending promise={promise1} />
147+
<Suspending promise={promise1} testID="content-1" />
136148
</React.Suspense>
137149
<React.Suspense fallback={<Text>Second Loading...</Text>}>
138-
<View testID="second-boundary">
139-
<Suspending promise={promise2} />
140-
</View>
150+
<Suspending promise={promise2} testID="content-2" />
141151
</React.Suspense>
142152
</View>
143153
);
144154

145155
expect(screen.getByText('First Loading...')).toBeOnTheScreen();
146156
expect(screen.getByText('Second Loading...')).toBeOnTheScreen();
147-
expect(screen.queryByTestId('content')).not.toBeOnTheScreen();
148-
expect(screen.queryByTestId('second-boundary')).not.toBeOnTheScreen();
157+
expect(screen.queryByTestId('content-1')).not.toBeOnTheScreen();
158+
expect(screen.queryByTestId('content-2')).not.toBeOnTheScreen();
149159

150-
// Resolve first promise
151160
// eslint-disable-next-line require-await
152161
await act(async () => resolvePromise1(null));
153-
expect(screen.getByTestId('content')).toBeOnTheScreen();
162+
expect(screen.getByTestId('content-1')).toBeOnTheScreen();
154163
expect(screen.queryByText('First Loading...')).not.toBeOnTheScreen();
155164
expect(screen.getByText('Second Loading...')).toBeOnTheScreen();
156165

157-
// Resolve second promise
158166
// eslint-disable-next-line require-await
159167
await act(async () => resolvePromise2(null));
160-
expect(screen.getByTestId('second-boundary')).toBeOnTheScreen();
168+
expect(screen.getByTestId('content-2')).toBeOnTheScreen();
161169
expect(screen.queryByText('Second Loading...')).not.toBeOnTheScreen();
162170
});

0 commit comments

Comments
 (0)