3562
);
3563
});
3564
3565
+ it('reports abort errors for every suspended task when aborting fatals the shell', async () => {
3566
+ const promise = new Promise(() => {});
3567
+ const rendered = [];
3568
+ function Suspend({label}) {
3569
+ rendered.push(label);
3570
+ use(promise);
3571
+ return null;
3572
+ }
3573
+
3574
+ function App() {
3575
+ return (
3576
+ <>
3577
+ <Suspense fallback="Loading...">
3578
+ <Suspend label="boundary" />
3579
+ </Suspense>
3580
+ <Suspend label="root one" />
3581
+ <Suspend label="root two" />
3582
+ </>
3583
+ );
3584
+ }
3585
+
3586
+ const errors = [];
3587
+ let abort;
3588
+ await act(() => {
3589
+ abort = renderToPipeableStream(<App />, {
3590
+ onError(error) {
3591
+ errors.push(error.message);
3592
+ },
3593
+ onShellError() {},
3594
+ }).abort;
3595
+ });
3596
+
3597
+ expect(rendered).toEqual(['boundary', 'root one', 'root two']);
3598
+
3599
+ await act(() => {
3600
+ abort(new Error('abort reason'));
3601
+ });
3602
+
3603
+ expect(errors).toEqual(['abort reason', 'abort reason', 'abort reason']);
3604
+ });
3605
+
3606
it('warns in dev if you access digest from errorInfo in onRecoverableError', async () => {
3607
await act(() => {
3608
const {pipe} = renderToPipeableStream(
3844
3845
expect(headers).toEqual({
3846
Link: `
3806
-<non-responsive-preload>; rel=preload; as="image"; fetchpriority="high",
3807
-<non-responsive-img>; rel=preload; as="image"; fetchpriority="high"
3847
+<non-responsive-preload>; rel=preload; as="image"; fetchpriority="high",
3848
+ <non-responsive-img>; rel=preload; as="image"; fetchpriority="high"
3849
`
3850
.replaceAll('\n', '')
3851
.trim(),
7063
);
7064
});
7065
7066
+ it('currently does not report an in-flight root task after another root task fatals while aborting', async () => {
7067
+ const promise = new Promise(() => {});
7068
+ function SuspendedRoot() {
7069
+ use(promise);
7070
+ return null;
7071
+ }
7072
+
7073
+ function Child() {
7074
+ return 'child';
7075
+ }
7076
+
7077
+ const abortRef = {current: null};
7078
+ function ComponentThatAborts() {
7079
+ abortRef.current(new Error('abort reason'));
7080
+ return <Child />;
7081
+ }
7082
+
7083
+ const errors = [];
7084
+ await act(() => {
7085
+ const {abort} = renderToPipeableStream(
7086
+ <>
7087
+ <SuspendedRoot />
7088
+ <ComponentThatAborts />
7089
+ </>,
7090
+ {
7091
+ onError(error) {
7092
+ errors.push(error.message);
7093
+ },
7094
+ onShellError() {},
7095
+ },
7096
+ );
7097
+ abortRef.current = abort;
7098
+ });
7099
+
7100
+ expect(errors).toEqual(['abort reason']);
7101
+ });
7102
+
7103
+ it('currently does not report a root task that suspends after aborting during render', async () => {
7104
+ const promise = new Promise(() => {});
7105
+ function SuspendedRoot() {
7106
+ use(promise);
7107
+ return null;
7108
+ }
7109
+
7110
+ function Child() {
7111
+ use(promise);
7112
+ return null;
7113
+ }
7114
+
7115
+ const abortRef = {current: null};
7116
+ function ComponentThatAborts() {
7117
+ abortRef.current(new Error('abort reason'));
7118
+ return <Child />;
7119
+ }
7120
+
7121
+ const errors = [];
7122
+ await act(() => {
7123
+ const {abort} = renderToPipeableStream(
7124
+ <>
7125
+ <SuspendedRoot />
7126
+ <ComponentThatAborts />
7127
+ </>,
7128
+ {
7129
+ onError(error) {
7130
+ errors.push(error.message);
7131
+ },
7132
+ onShellError() {},
7133
+ },
7134
+ );
7135
+ abortRef.current = abort;
7136
+ });
7137
+
7138
+ // TODO: Once abort completion is async, this still-suspended task should
7139
+ // observe ABORTING and report the abort reason as well.
7140
+ expect(errors).toEqual(['abort reason']);
7141
+ });
7142
+
7143
it('can abort during render in a lazy initializer for a component', async () => {
7144
function Sibling() {
7145
return <p>sibling</p>;