Skip to content

Commit b053a93

Browse files
committed
Add basic tests for functionality using each update mechanism
1 parent 0c902e4 commit b053a93

File tree

1 file changed

+278
-0
lines changed

1 file changed

+278
-0
lines changed

packages/react-reconciler/src/__tests__/ReactSuspenseWithNoopRenderer-test.internal.js

Lines changed: 278 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,4 +1701,282 @@ describe('ReactSuspenseWithNoopRenderer', () => {
17011701

17021702
expect(ReactNoop.getChildren()).toEqual([span('Loading A...')]);
17031703
});
1704+
1705+
describe('delays transitions when there a suspense config is supplied', () => {
1706+
const SUSPENSE_CONFIG = {
1707+
timeoutMs: 2000,
1708+
};
1709+
1710+
it('top level render', async () => {
1711+
function App({page}) {
1712+
return (
1713+
<Suspense fallback={<Text text="Loading..." />}>
1714+
<AsyncText text={page} ms={5000} />
1715+
</Suspense>
1716+
);
1717+
}
1718+
1719+
// Initial render.
1720+
React.unstable_withSuspenseConfig(
1721+
() => ReactNoop.render(<App page="A" />),
1722+
SUSPENSE_CONFIG,
1723+
);
1724+
1725+
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
1726+
// Only a short time is needed to unsuspend the initial loading state.
1727+
Scheduler.advanceTime(400);
1728+
await advanceTimers(400);
1729+
expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1730+
1731+
// Later we load the data.
1732+
Scheduler.advanceTime(5000);
1733+
await advanceTimers(5000);
1734+
expect(Scheduler).toHaveYielded(['Promise resolved [A]']);
1735+
expect(Scheduler).toFlushAndYield(['A']);
1736+
expect(ReactNoop.getChildren()).toEqual([span('A')]);
1737+
1738+
// Start transition.
1739+
React.unstable_withSuspenseConfig(
1740+
() => ReactNoop.render(<App page="B" />),
1741+
SUSPENSE_CONFIG,
1742+
);
1743+
1744+
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
1745+
Scheduler.advanceTime(1000);
1746+
await advanceTimers(1000);
1747+
// Even after a second, we have still not yet flushed the loading state.
1748+
expect(ReactNoop.getChildren()).toEqual([span('A')]);
1749+
Scheduler.advanceTime(1100);
1750+
await advanceTimers(1100);
1751+
// After the timeout, we do show the loading state.
1752+
expect(ReactNoop.getChildren()).toEqual([
1753+
hiddenSpan('A'),
1754+
span('Loading...'),
1755+
]);
1756+
// Later we load the data.
1757+
Scheduler.advanceTime(3000);
1758+
await advanceTimers(3000);
1759+
expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
1760+
expect(Scheduler).toFlushAndYield(['B']);
1761+
expect(ReactNoop.getChildren()).toEqual([span('B')]);
1762+
});
1763+
1764+
it('hooks', async () => {
1765+
let transitionToPage;
1766+
function App() {
1767+
let [page, setPage] = React.useState('none');
1768+
transitionToPage = setPage;
1769+
if (page === 'none') {
1770+
return null;
1771+
}
1772+
return (
1773+
<Suspense fallback={<Text text="Loading..." />}>
1774+
<AsyncText text={page} ms={5000} />
1775+
</Suspense>
1776+
);
1777+
}
1778+
1779+
ReactNoop.render(<App />);
1780+
expect(Scheduler).toFlushAndYield([]);
1781+
1782+
// Initial render.
1783+
await ReactNoop.act(async () => {
1784+
React.unstable_withSuspenseConfig(
1785+
() => transitionToPage('A'),
1786+
SUSPENSE_CONFIG,
1787+
);
1788+
1789+
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
1790+
// Only a short time is needed to unsuspend the initial loading state.
1791+
Scheduler.advanceTime(400);
1792+
await advanceTimers(400);
1793+
expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1794+
});
1795+
1796+
// Later we load the data.
1797+
Scheduler.advanceTime(5000);
1798+
await advanceTimers(5000);
1799+
expect(Scheduler).toHaveYielded(['Promise resolved [A]']);
1800+
expect(Scheduler).toFlushAndYield(['A']);
1801+
expect(ReactNoop.getChildren()).toEqual([span('A')]);
1802+
1803+
// Start transition.
1804+
await ReactNoop.act(async () => {
1805+
React.unstable_withSuspenseConfig(
1806+
() => transitionToPage('B'),
1807+
SUSPENSE_CONFIG,
1808+
);
1809+
1810+
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
1811+
Scheduler.advanceTime(1000);
1812+
await advanceTimers(1000);
1813+
// Even after a second, we have still not yet flushed the loading state.
1814+
expect(ReactNoop.getChildren()).toEqual([span('A')]);
1815+
Scheduler.advanceTime(1100);
1816+
await advanceTimers(1100);
1817+
// After the timeout, we do show the loading state.
1818+
expect(ReactNoop.getChildren()).toEqual([
1819+
hiddenSpan('A'),
1820+
span('Loading...'),
1821+
]);
1822+
});
1823+
// Later we load the data.
1824+
Scheduler.advanceTime(3000);
1825+
await advanceTimers(3000);
1826+
expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
1827+
expect(Scheduler).toFlushAndYield(['B']);
1828+
expect(ReactNoop.getChildren()).toEqual([span('B')]);
1829+
});
1830+
1831+
it('classes', async () => {
1832+
let transitionToPage;
1833+
class App extends React.Component {
1834+
state = {page: 'none'};
1835+
render() {
1836+
transitionToPage = page => this.setState({page});
1837+
let page = this.state.page;
1838+
if (page === 'none') {
1839+
return null;
1840+
}
1841+
return (
1842+
<Suspense fallback={<Text text="Loading..." />}>
1843+
<AsyncText text={page} ms={5000} />
1844+
</Suspense>
1845+
);
1846+
}
1847+
}
1848+
1849+
ReactNoop.render(<App />);
1850+
expect(Scheduler).toFlushAndYield([]);
1851+
1852+
// Initial render.
1853+
await ReactNoop.act(async () => {
1854+
React.unstable_withSuspenseConfig(
1855+
() => transitionToPage('A'),
1856+
SUSPENSE_CONFIG,
1857+
);
1858+
1859+
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
1860+
// Only a short time is needed to unsuspend the initial loading state.
1861+
Scheduler.advanceTime(400);
1862+
await advanceTimers(400);
1863+
expect(ReactNoop.getChildren()).toEqual([span('Loading...')]);
1864+
});
1865+
1866+
// Later we load the data.
1867+
Scheduler.advanceTime(5000);
1868+
await advanceTimers(5000);
1869+
expect(Scheduler).toHaveYielded(['Promise resolved [A]']);
1870+
expect(Scheduler).toFlushAndYield(['A']);
1871+
expect(ReactNoop.getChildren()).toEqual([span('A')]);
1872+
1873+
// Start transition.
1874+
await ReactNoop.act(async () => {
1875+
React.unstable_withSuspenseConfig(
1876+
() => transitionToPage('B'),
1877+
SUSPENSE_CONFIG,
1878+
);
1879+
1880+
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
1881+
Scheduler.advanceTime(1000);
1882+
await advanceTimers(1000);
1883+
// Even after a second, we have still not yet flushed the loading state.
1884+
expect(ReactNoop.getChildren()).toEqual([span('A')]);
1885+
Scheduler.advanceTime(1100);
1886+
await advanceTimers(1100);
1887+
// After the timeout, we do show the loading state.
1888+
expect(ReactNoop.getChildren()).toEqual([
1889+
hiddenSpan('A'),
1890+
span('Loading...'),
1891+
]);
1892+
});
1893+
// Later we load the data.
1894+
Scheduler.advanceTime(3000);
1895+
await advanceTimers(3000);
1896+
expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
1897+
expect(Scheduler).toFlushAndYield(['B']);
1898+
expect(ReactNoop.getChildren()).toEqual([span('B')]);
1899+
});
1900+
});
1901+
1902+
it('disables suspense config when nothing is passed to withSuspenseConfig', async () => {
1903+
function App({page}) {
1904+
return (
1905+
<Fragment>
1906+
<Suspense fallback={<Text text="Loading..." />}>
1907+
<AsyncText text={page} ms={2000} />
1908+
</Suspense>
1909+
</Fragment>
1910+
);
1911+
}
1912+
1913+
// Initial render.
1914+
ReactNoop.render(<App page="A" />);
1915+
expect(Scheduler).toFlushAndYield(['Suspend! [A]', 'Loading...']);
1916+
Scheduler.advanceTime(2000);
1917+
await advanceTimers(2000);
1918+
expect(Scheduler).toHaveYielded(['Promise resolved [A]']);
1919+
expect(Scheduler).toFlushAndYield(['A']);
1920+
expect(ReactNoop.getChildren()).toEqual([span('A')]);
1921+
1922+
// Start transition.
1923+
React.unstable_withSuspenseConfig(
1924+
() => {
1925+
// When we schedule an inner transition without a suspense config
1926+
// so it should only suspend for a short time.
1927+
React.unstable_withSuspenseConfig(() =>
1928+
ReactNoop.render(<App page="B" />),
1929+
);
1930+
},
1931+
{timeoutMs: 2000},
1932+
);
1933+
1934+
expect(Scheduler).toFlushAndYield(['Suspend! [B]', 'Loading...']);
1935+
// Suspended
1936+
expect(ReactNoop.getChildren()).toEqual([span('A')]);
1937+
Scheduler.advanceTime(500);
1938+
await advanceTimers(500);
1939+
// Committed loading state.
1940+
expect(ReactNoop.getChildren()).toEqual([
1941+
hiddenSpan('A'),
1942+
span('Loading...'),
1943+
]);
1944+
1945+
Scheduler.advanceTime(2000);
1946+
await advanceTimers(2000);
1947+
expect(Scheduler).toHaveYielded(['Promise resolved [B]']);
1948+
expect(Scheduler).toFlushAndYield(['B']);
1949+
expect(ReactNoop.getChildren()).toEqual([span('B')]);
1950+
1951+
React.unstable_withSuspenseConfig(
1952+
() => {
1953+
// First we schedule an inner unrelated update.
1954+
React.unstable_withSuspenseConfig(() =>
1955+
ReactNoop.render(<App page="B" unrelated={true} />),
1956+
);
1957+
// Then we schedule another transition to a slow page,
1958+
// but at this scope we should suspend for longer.
1959+
Scheduler.unstable_next(() => ReactNoop.render(<App page="C" />));
1960+
},
1961+
{timeoutMs: 2000},
1962+
);
1963+
expect(Scheduler).toFlushAndYield([
1964+
'Suspend! [C]',
1965+
'Loading...',
1966+
'Suspend! [C]',
1967+
'Loading...',
1968+
]);
1969+
expect(ReactNoop.getChildren()).toEqual([span('B')]);
1970+
Scheduler.advanceTime(1200);
1971+
await advanceTimers(1200);
1972+
// Even after a second, we have still not yet flushed the loading state.
1973+
expect(ReactNoop.getChildren()).toEqual([span('B')]);
1974+
Scheduler.advanceTime(1200);
1975+
await advanceTimers(1200);
1976+
// After the two second timeout we show the loading state.
1977+
expect(ReactNoop.getChildren()).toEqual([
1978+
hiddenSpan('B'),
1979+
span('Loading...'),
1980+
]);
1981+
});
17041982
});

0 commit comments

Comments
 (0)