@@ -107,6 +107,38 @@ describe('ReactFlightDOM', () => {
107
107
return maybePromise ;
108
108
}
109
109
110
+ async function readInto (
111
+ container : Document | HTMLElement ,
112
+ stream : ReadableStream ,
113
+ ) {
114
+ const reader = stream . getReader ( ) ;
115
+ const decoder = new TextDecoder ( ) ;
116
+ let content = '' ;
117
+ while ( true ) {
118
+ const { done, value} = await reader . read ( ) ;
119
+ if ( done ) {
120
+ content += decoder . decode ( ) ;
121
+ break ;
122
+ }
123
+ content += decoder . decode ( value , { stream : true } ) ;
124
+ }
125
+ if ( container . nodeType === 9 /* DOCUMENT */ ) {
126
+ const doc = new JSDOM ( content ) . window . document ;
127
+ container . documentElement . innerHTML = doc . documentElement . innerHTML ;
128
+ while ( container . documentElement . attributes . length > 0 ) {
129
+ container . documentElement . removeAttribute (
130
+ container . documentElement . attributes [ 0 ] . name ,
131
+ ) ;
132
+ }
133
+ const attrs = doc . documentElement . attributes ;
134
+ for ( let i = 0 ; i < attrs . length ; i ++ ) {
135
+ container . documentElement . setAttribute ( attrs [ i ] . name , attrs [ i ] . value ) ;
136
+ }
137
+ } else {
138
+ container . innerHTML = content ;
139
+ }
140
+ }
141
+
110
142
function getTestStream ( ) {
111
143
const writable = new Stream . PassThrough ( ) ;
112
144
const readable = new ReadableStream ( {
@@ -1633,20 +1665,8 @@ describe('ReactFlightDOM', () => {
1633
1665
ReactDOMFizzServer . renderToPipeableStream ( < App /> ) . pipe ( fizzWritable ) ;
1634
1666
} ) ;
1635
1667
1636
- const decoder = new TextDecoder ( ) ;
1637
- const reader = fizzReadable . getReader ( ) ;
1638
- let content = '' ;
1639
- while ( true ) {
1640
- const { done, value} = await reader . read ( ) ;
1641
- if ( done ) {
1642
- content += decoder . decode ( ) ;
1643
- break ;
1644
- }
1645
- content += decoder . decode ( value , { stream : true } ) ;
1646
- }
1647
-
1648
- const doc = new JSDOM ( content ) . window . document ;
1649
- expect ( getMeaningfulChildren ( doc ) ) . toEqual (
1668
+ await readInto ( document , fizzReadable ) ;
1669
+ expect ( getMeaningfulChildren ( document ) ) . toEqual (
1650
1670
< html >
1651
1671
< head >
1652
1672
< link rel = "dns-prefetch" href = "d before" />
@@ -1912,4 +1932,279 @@ describe('ReactFlightDOM', () => {
1912
1932
} ) ;
1913
1933
expect ( container . innerHTML ) . toBe ( 'Hello World' ) ;
1914
1934
} ) ;
1935
+
1936
+ it ( 'can abort synchronously during render' , async ( ) => {
1937
+ let siblingDidRender = false ;
1938
+ function Sibling ( ) {
1939
+ siblingDidRender = true ;
1940
+ return < p > sibling</ p > ;
1941
+ }
1942
+
1943
+ function App ( ) {
1944
+ return (
1945
+ < Suspense fallback = { < p > loading...</ p > } >
1946
+ < ComponentThatAborts />
1947
+ < Sibling />
1948
+ < div >
1949
+ < Sibling />
1950
+ </ div >
1951
+ </ Suspense >
1952
+ ) ;
1953
+ }
1954
+
1955
+ const abortRef = { current : null } ;
1956
+ function ComponentThatAborts ( ) {
1957
+ abortRef . current ( ) ;
1958
+ return < p > hello world</ p > ;
1959
+ }
1960
+
1961
+ const { writable : flightWritable , readable : flightReadable } =
1962
+ getTestStream ( ) ;
1963
+
1964
+ await serverAct ( ( ) => {
1965
+ const { pipe, abort} = ReactServerDOMServer . renderToPipeableStream (
1966
+ < App /> ,
1967
+ webpackMap ,
1968
+ ) ;
1969
+ abortRef . current = abort ;
1970
+ pipe ( flightWritable ) ;
1971
+ } ) ;
1972
+
1973
+ expect ( siblingDidRender ) . toBe ( false ) ;
1974
+
1975
+ const response =
1976
+ ReactServerDOMClient . createFromReadableStream ( flightReadable ) ;
1977
+
1978
+ const { writable : fizzWritable , readable : fizzReadable } = getTestStream ( ) ;
1979
+
1980
+ function ClientApp ( ) {
1981
+ return use ( response ) ;
1982
+ }
1983
+
1984
+ const shellErrors = [ ] ;
1985
+ await serverAct ( async ( ) => {
1986
+ ReactDOMFizzServer . renderToPipeableStream (
1987
+ React . createElement ( ClientApp ) ,
1988
+ {
1989
+ onShellError ( error ) {
1990
+ shellErrors . push ( error . message ) ;
1991
+ } ,
1992
+ } ,
1993
+ ) . pipe ( fizzWritable ) ;
1994
+ } ) ;
1995
+
1996
+ expect ( shellErrors ) . toEqual ( [ ] ) ;
1997
+
1998
+ const container = document . createElement ( 'div' ) ;
1999
+ await readInto ( container , fizzReadable ) ;
2000
+ expect ( getMeaningfulChildren ( container ) ) . toEqual ( < p > loading...</ p > ) ;
2001
+ } ) ;
2002
+
2003
+ it ( 'can abort during render in an async tick' , async ( ) => {
2004
+ let siblingDidRender = false ;
2005
+ function DidRender ( { children} ) {
2006
+ siblingDidRender = true ;
2007
+ }
2008
+
2009
+ async function Sibling ( ) {
2010
+ return (
2011
+ < DidRender >
2012
+ < p > sibling</ p >
2013
+ </ DidRender >
2014
+ ) ;
2015
+ }
2016
+
2017
+ function App ( ) {
2018
+ return (
2019
+ < Suspense fallback = { < p > loading...</ p > } >
2020
+ < ComponentThatAborts />
2021
+ < Sibling />
2022
+ </ Suspense >
2023
+ ) ;
2024
+ }
2025
+
2026
+ const abortRef = { current : null } ;
2027
+ async function ComponentThatAborts ( ) {
2028
+ await 1 ;
2029
+ abortRef . current ( ) ;
2030
+ return < p > hello world</ p > ;
2031
+ }
2032
+
2033
+ const { writable : flightWritable , readable : flightReadable } =
2034
+ getTestStream ( ) ;
2035
+
2036
+ await serverAct ( ( ) => {
2037
+ const { pipe, abort} = ReactServerDOMServer . renderToPipeableStream (
2038
+ < App /> ,
2039
+ webpackMap ,
2040
+ ) ;
2041
+ abortRef . current = abort ;
2042
+ pipe ( flightWritable ) ;
2043
+ } ) ;
2044
+
2045
+ expect ( siblingDidRender ) . toBe ( false ) ;
2046
+
2047
+ const response =
2048
+ ReactServerDOMClient . createFromReadableStream ( flightReadable ) ;
2049
+
2050
+ const { writable : fizzWritable , readable : fizzReadable } = getTestStream ( ) ;
2051
+
2052
+ function ClientApp ( ) {
2053
+ return use ( response ) ;
2054
+ }
2055
+
2056
+ const shellErrors = [ ] ;
2057
+ await serverAct ( async ( ) => {
2058
+ ReactDOMFizzServer . renderToPipeableStream (
2059
+ React . createElement ( ClientApp ) ,
2060
+ {
2061
+ onShellError ( error ) {
2062
+ shellErrors . push ( error . message ) ;
2063
+ } ,
2064
+ } ,
2065
+ ) . pipe ( fizzWritable ) ;
2066
+ } ) ;
2067
+
2068
+ expect ( shellErrors ) . toEqual ( [ ] ) ;
2069
+
2070
+ const container = document . createElement ( 'div' ) ;
2071
+ await readInto ( container , fizzReadable ) ;
2072
+ expect ( getMeaningfulChildren ( container ) ) . toEqual ( < p > loading...</ p > ) ;
2073
+ } ) ;
2074
+
2075
+ it ( 'can abort during render in a lazy initializer for a component' , async ( ) => {
2076
+ let siblingDidRender = false ;
2077
+
2078
+ function Sibling ( ) {
2079
+ siblingDidRender = true ;
2080
+ return < p > sibling</ p > ;
2081
+ }
2082
+
2083
+ function App ( ) {
2084
+ return (
2085
+ < Suspense fallback = { < p > loading...</ p > } >
2086
+ < LazyAbort />
2087
+ < Sibling />
2088
+ </ Suspense >
2089
+ ) ;
2090
+ }
2091
+
2092
+ const abortRef = { current : null } ;
2093
+ const LazyAbort = React . lazy ( ( ) => {
2094
+ abortRef . current ( ) ;
2095
+ return Promise . resolve ( {
2096
+ default : function LazyComponent ( ) {
2097
+ return < p > hello world</ p > ;
2098
+ } ,
2099
+ } ) ;
2100
+ } ) ;
2101
+
2102
+ const { writable : flightWritable , readable : flightReadable } =
2103
+ getTestStream ( ) ;
2104
+
2105
+ await serverAct ( ( ) => {
2106
+ const { pipe, abort} = ReactServerDOMServer . renderToPipeableStream (
2107
+ < App /> ,
2108
+ webpackMap ,
2109
+ ) ;
2110
+ abortRef . current = abort ;
2111
+ pipe ( flightWritable ) ;
2112
+ } ) ;
2113
+
2114
+ expect ( siblingDidRender ) . toBe ( false ) ;
2115
+
2116
+ const response =
2117
+ ReactServerDOMClient . createFromReadableStream ( flightReadable ) ;
2118
+
2119
+ const { writable : fizzWritable , readable : fizzReadable } = getTestStream ( ) ;
2120
+
2121
+ function ClientApp ( ) {
2122
+ return use ( response ) ;
2123
+ }
2124
+
2125
+ const shellErrors = [ ] ;
2126
+ await serverAct ( async ( ) => {
2127
+ ReactDOMFizzServer . renderToPipeableStream (
2128
+ React . createElement ( ClientApp ) ,
2129
+ {
2130
+ onShellError ( error ) {
2131
+ shellErrors . push ( error . message ) ;
2132
+ } ,
2133
+ } ,
2134
+ ) . pipe ( fizzWritable ) ;
2135
+ } ) ;
2136
+
2137
+ expect ( shellErrors ) . toEqual ( [ ] ) ;
2138
+
2139
+ const container = document . createElement ( 'div' ) ;
2140
+ await readInto ( container , fizzReadable ) ;
2141
+ expect ( getMeaningfulChildren ( container ) ) . toEqual ( < p > loading...</ p > ) ;
2142
+ } ) ;
2143
+
2144
+ it ( 'can abort during render in a lazy initializer for an element' , async ( ) => {
2145
+ let siblingDidRender = false ;
2146
+
2147
+ function Sibling ( ) {
2148
+ siblingDidRender = true ;
2149
+ return < p > sibling</ p > ;
2150
+ }
2151
+
2152
+ function App ( ) {
2153
+ return (
2154
+ < Suspense fallback = { < p > loading...</ p > } >
2155
+ { lazyAbort }
2156
+ < Sibling />
2157
+ </ Suspense >
2158
+ ) ;
2159
+ }
2160
+
2161
+ const abortRef = { current : null } ;
2162
+ const lazyAbort = React . lazy ( ( ) => {
2163
+ abortRef . current ( ) ;
2164
+ return Promise . resolve ( {
2165
+ default : < p > hello world</ p > ,
2166
+ } ) ;
2167
+ } ) ;
2168
+
2169
+ const { writable : flightWritable , readable : flightReadable } =
2170
+ getTestStream ( ) ;
2171
+
2172
+ await serverAct ( ( ) => {
2173
+ const { pipe, abort} = ReactServerDOMServer . renderToPipeableStream (
2174
+ < App /> ,
2175
+ webpackMap ,
2176
+ ) ;
2177
+ abortRef . current = abort ;
2178
+ pipe ( flightWritable ) ;
2179
+ } ) ;
2180
+
2181
+ expect ( siblingDidRender ) . toBe ( false ) ;
2182
+
2183
+ const response =
2184
+ ReactServerDOMClient . createFromReadableStream ( flightReadable ) ;
2185
+
2186
+ const { writable : fizzWritable , readable : fizzReadable } = getTestStream ( ) ;
2187
+
2188
+ function ClientApp ( ) {
2189
+ return use ( response ) ;
2190
+ }
2191
+
2192
+ const shellErrors = [ ] ;
2193
+ await serverAct ( async ( ) => {
2194
+ ReactDOMFizzServer . renderToPipeableStream (
2195
+ React . createElement ( ClientApp ) ,
2196
+ {
2197
+ onShellError ( error ) {
2198
+ shellErrors . push ( error . message ) ;
2199
+ } ,
2200
+ } ,
2201
+ ) . pipe ( fizzWritable ) ;
2202
+ } ) ;
2203
+
2204
+ expect ( shellErrors ) . toEqual ( [ ] ) ;
2205
+
2206
+ const container = document . createElement ( 'div' ) ;
2207
+ await readInto ( container , fizzReadable ) ;
2208
+ expect ( getMeaningfulChildren ( container ) ) . toEqual ( < p > loading...</ p > ) ;
2209
+ } ) ;
1915
2210
} ) ;
0 commit comments