From c2d581877d85bcdad6063425ef251e62b6d26665 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 17 Jan 2022 21:24:41 +0100 Subject: [PATCH 1/7] Adjust tests for React 18 --- package.json | 2 +- test/CSSTransition-test.js | 49 +++++++++++++++++++++-------------- test/SwitchTransition-test.js | 6 +++++ test/Transition-test.js | 30 +++++++++++++-------- test/TransitionGroup-test.js | 4 +-- yarn.lock | 18 ++++++------- 6 files changed, 67 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index b17cae7f..2afe2dd2 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "@semantic-release/npm": "^7.0.5", "@storybook/addon-actions": "^6.3.4", "@storybook/react": "^6.3.4", - "@testing-library/react": "^12.1.2", + "@testing-library/react": "alpha", "@typescript-eslint/eslint-plugin": "^4.26.1", "astroturf": "^0.10.4", "babel-eslint": "^10.1.0", diff --git a/test/CSSTransition-test.js b/test/CSSTransition-test.js index 0efaaad2..f55d7c5f 100644 --- a/test/CSSTransition-test.js +++ b/test/CSSTransition-test.js @@ -146,8 +146,16 @@ describe('CSSTransition', () => { ); }); - it('should lose the "*-appear-done" class after leaving and entering again', (done) => { + it('should lose the "*-appear-done" class after leaving and entering again', async () => { const nodeRef = React.createRef(); + let handleEntered; + let onEntered = new Promise((resolve) => { + handleEntered = resolve; + }); + let handleExited; + const onExited = new Promise((resolve) => { + handleExited = resolve; + }); const { setProps } = render( { classNames="appear-test" in={true} appear={true} - onEntered={() => { - setProps({ - in: false, - onEntered: () => {}, - onExited: () => { - expect(nodeRef.current.className).toBe('appear-test-exit-done'); - setProps({ - in: true, - onEntered: () => { - expect(nodeRef.current.className).toBe( - 'appear-test-enter-done' - ); - done(); - }, - }); - }, - }); - }} + onEntered={handleEntered} >
); + + await onEntered; + setProps({ + in: false, + onEntered: () => {}, + onExited: handleExited, + }); + + await onExited; + expect(nodeRef.current.className).toBe('appear-test-exit-done'); + onEntered = new Promise((resolve) => { + handleEntered = resolve; + }); + setProps({ + in: true, + onEntered: handleEntered, + }); + + await onEntered; + expect(nodeRef.current.className).toBe('appear-test-enter-done'); }); it('should not add undefined when appearDone is not defined', (done) => { diff --git a/test/SwitchTransition-test.js b/test/SwitchTransition-test.js index 6691d9d5..9250a586 100644 --- a/test/SwitchTransition-test.js +++ b/test/SwitchTransition-test.js @@ -106,6 +106,9 @@ describe('SwitchTransition', () => { act(() => { jest.runAllTimers(); }); + act(() => { + jest.runAllTimers(); + }); expect(log).toEqual([ 'exit', 'exiting', @@ -137,6 +140,9 @@ describe('SwitchTransition', () => { act(() => { jest.runAllTimers(); }); + act(() => { + jest.runAllTimers(); + }); expect(log).toEqual([ 'enter', 'entering', diff --git a/test/Transition-test.js b/test/Transition-test.js index 76b9905c..e7382e8b 100644 --- a/test/Transition-test.js +++ b/test/Transition-test.js @@ -449,25 +449,33 @@ describe('Transition', () => { setProps({ in: true }); }); - it('should stay mounted after exiting', (done) => { + it('should stay mounted after exiting', async () => { + let handleEntered; + const onEntered = new Promise((resolve) => { + handleEntered = resolve; + }); + let handleExited; + const onExited = new Promise((resolve) => { + handleExited = resolve; + }); const { container, setProps } = render( { - expect(container.textContent).toEqual(`status: ${ENTERED}`); - - setProps({ in: false }); - }} - onExited={() => { - expect(container.textContent).toEqual(`status: ${EXITED}`); - - done(); - }} + onEntered={handleEntered} + onExited={handleExited} /> ); expect(container.textContent).toEqual(''); setProps({ in: true }); + + await onEntered; + expect(container.textContent).toEqual(`status: ${ENTERED}`); + + setProps({ in: false }); + + await onExited; + expect(container.textContent).toEqual(`status: ${EXITED}`); }); }); diff --git a/test/TransitionGroup-test.js b/test/TransitionGroup-test.js index 6379dfd9..3fa82438 100644 --- a/test/TransitionGroup-test.js +++ b/test/TransitionGroup-test.js @@ -92,14 +92,14 @@ describe('TransitionGroup', () => { act(() => { jest.runAllTimers(); }); - expect(log).toEqual(['appear', 'appearing', 'appeared']); + expect(log).toEqual(['appear', 'appear', 'appearing', 'appeared']); log = []; renderStrict(, container); act(() => { jest.runAllTimers(); }); - expect(log).toEqual(['enter', 'entering', 'entered']); + expect(log).toEqual(['enter', 'enter', 'entering', 'entered']); log = []; renderStrict(, container); diff --git a/yarn.lock b/yarn.lock index c860a8c8..5a9db17a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3503,10 +3503,10 @@ resolve-from "^5.0.0" store2 "^2.12.0" -"@testing-library/dom@^8.0.0": - version "8.11.1" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.11.1.tgz#03fa2684aa09ade589b460db46b4c7be9fc69753" - integrity sha512-3KQDyx9r0RKYailW2MiYrSSKEfH0GTkI51UGEvJenvcoDoeRYs0PZpi2SXqtnMClQvCqdtTTpOfFETDTVADpAg== +"@testing-library/dom@^8.5.0": + version "8.11.2" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.11.2.tgz#fc110c665a066c2287be765e4a35ba8dad737015" + integrity sha512-idsS/cqbYudXcVWngc1PuWNmXs416oBy2g/7Q8QAUREt5Z3MUkAL2XJD7xazLJ6esDfqRDi/ZBxk+OPPXitHRw== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" @@ -3517,13 +3517,13 @@ lz-string "^1.4.4" pretty-format "^27.0.2" -"@testing-library/react@^12.1.2": - version "12.1.2" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.2.tgz#f1bc9a45943461fa2a598bb4597df1ae044cfc76" - integrity sha512-ihQiEOklNyHIpo2Y8FREkyD1QAea054U0MVbwH1m8N9TxeFz+KoJ9LkqoKqJlzx2JDm56DVwaJ1r36JYxZM05g== +"@testing-library/react@alpha": + version "13.0.0-alpha.5" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.0.0-alpha.5.tgz#29bfc36b550e2a1025cbebf7254d5a0a46cb58c5" + integrity sha512-QrxKC/7pTE0ze3wLZNaenGJqsLcbAJL71XqU/ryJTL2pqZBjiJHuxZavl2ZQAxnBQkDQF9oh9my3bKPstWfnhA== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.0.0" + "@testing-library/dom" "^8.5.0" "@tootallnate/once@1": version "1.0.0" From 56d5eef96b90658e835e978694429ad05f96a0bc Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 13 Dec 2021 11:08:03 +0100 Subject: [PATCH 2/7] Wrap all updates in act() --- test/CSSTransition-test.js | 137 ++++++++++++++++-------------- test/CSSTransitionGroup-test.js | 4 +- test/Transition-test.js | 143 +++++++++++++++++++++----------- 3 files changed, 175 insertions(+), 109 deletions(-) diff --git a/test/CSSTransition-test.js b/test/CSSTransition-test.js index f55d7c5f..9ec4672a 100644 --- a/test/CSSTransition-test.js +++ b/test/CSSTransition-test.js @@ -1,5 +1,5 @@ import React from 'react'; -import { render } from './utils'; +import { render, waitFor } from './utils'; import CSSTransition from '../src/CSSTransition'; import TransitionGroup from '../src/TransitionGroup'; @@ -36,8 +36,9 @@ describe('CSSTransition', () => { }); describe('entering', () => { - it('should apply classes at each transition state', (done) => { + it('should apply classes at each transition state', async () => { let count = 0; + let done = false; const nodeRef = React.createRef(); const { setProps } = render( @@ -63,12 +64,16 @@ describe('CSSTransition', () => { onEntered() { expect(nodeRef.current.className).toEqual('test-enter-done'); expect(count).toEqual(2); - done(); + done = true; }, }); + + await waitFor(() => { + expect(done).toBe(true); + }); }); - it('should apply custom classNames names', (done) => { + it('should apply custom classNames names', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render( @@ -102,15 +107,17 @@ describe('CSSTransition', () => { onEntered() { expect(nodeRef.current.className).toEqual('custom-super-done'); - expect(count).toEqual(2); - done(); }, }); + + await waitFor(() => { + expect(count).toEqual(2); + }); }); }); describe('appearing', () => { - it('should apply appear classes at each transition state', (done) => { + it('should apply appear classes at each transition state', async () => { let count = 0; const nodeRef = React.createRef(); render( @@ -137,25 +144,21 @@ describe('CSSTransition', () => { expect(nodeRef.current.className).toEqual( 'appear-test-appear-done appear-test-enter-done' ); - expect(count).toEqual(2); - done(); }} >
); + + await waitFor(() => { + expect(count).toEqual(2); + }); }); it('should lose the "*-appear-done" class after leaving and entering again', async () => { const nodeRef = React.createRef(); - let handleEntered; - let onEntered = new Promise((resolve) => { - handleEntered = resolve; - }); - let handleExited; - const onExited = new Promise((resolve) => { - handleExited = resolve; - }); + let entered = false; + let exited = false; const { setProps } = render( { classNames="appear-test" in={true} appear={true} - onEntered={handleEntered} + onEntered={() => { + entered = true; + }} >
); - await onEntered; + await waitFor(() => { + expect(entered).toEqual(true); + }); setProps({ in: false, onEntered: () => {}, - onExited: handleExited, + onExited: () => { + exited = true; + }, }); - await onExited; - expect(nodeRef.current.className).toBe('appear-test-exit-done'); - onEntered = new Promise((resolve) => { - handleEntered = resolve; + await waitFor(() => { + expect(exited).toEqual(true); }); + expect(nodeRef.current.className).toBe('appear-test-exit-done'); + entered = false; setProps({ in: true, - onEntered: handleEntered, + onEntered: () => { + entered = true; + }, }); - await onEntered; + await waitFor(() => { + expect(entered).toEqual(true); + }); expect(nodeRef.current.className).toBe('appear-test-enter-done'); }); - it('should not add undefined when appearDone is not defined', (done) => { + it('should not add undefined when appearDone is not defined', async () => { const nodeRef = React.createRef(); + let done = false; render( { onEntered={(isAppearing) => { expect(isAppearing).toEqual(true); expect(nodeRef.current.className).toEqual(''); - done(); + done = true; }} >
); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); - it('should not be appearing in normal enter mode', (done) => { + it('should not be appearing in normal enter mode', async () => { let count = 0; const nodeRef = React.createRef(); render( @@ -248,10 +266,12 @@ describe('CSSTransition', () => { expect(nodeRef.current.className).toEqual( 'not-appear-test-enter-done' ); - expect(count).toEqual(2); - done(); }, }); + + await waitFor(() => { + expect(count).toEqual(2); + }); }); it('should not enter the transition states when appear=false', () => { @@ -280,7 +300,7 @@ describe('CSSTransition', () => { }); describe('exiting', () => { - it('should apply classes at each transition state', (done) => { + it('should apply classes at each transition state', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render( @@ -306,13 +326,15 @@ describe('CSSTransition', () => { onExited() { expect(nodeRef.current.className).toEqual('test-exit-done'); - expect(count).toEqual(2); - done(); }, }); + + await waitFor(() => { + expect(count).toEqual(2); + }); }); - it('should apply custom classNames names', (done) => { + it('should apply custom classNames names', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render( @@ -347,13 +369,15 @@ describe('CSSTransition', () => { onExited() { expect(nodeRef.current.className).toEqual('custom-super-done'); - expect(count).toEqual(2); - done(); }, }); + + await waitFor(() => { + expect(count).toEqual(2); + }); }); - it('should support empty prefix', (done) => { + it('should support empty prefix', async () => { let count = 0; const nodeRef = React.createRef(); @@ -378,10 +402,12 @@ describe('CSSTransition', () => { onExited() { expect(nodeRef.current.className).toEqual('exit-done'); - expect(count).toEqual(2); - done(); }, }); + + await waitFor(() => { + expect(count).toEqual(2); + }); }); }); @@ -423,20 +449,7 @@ describe('CSSTransition', () => { ); - const rerender = (getProps) => - new Promise((resolve) => - setProps({ - onEnter: undefined, - onEntering: undefined, - onEntered: undefined, - onExit: undefined, - onExiting: undefined, - onExited: undefined, - ...getProps(resolve), - }) - ); - - await rerender((resolve) => ({ + setProps({ direction: 'up', text: 'bar', nodeRef: nodeRef.bar, @@ -450,11 +463,14 @@ describe('CSSTransition', () => { expect(nodeRef.bar.current.className).toEqual( 'up-enter up-enter-active' ); - resolve(); }, - })); + }); - await rerender((resolve) => ({ + await waitFor(() => { + expect(count).toEqual(2); + }); + + setProps({ direction: 'down', text: 'foo', nodeRef: nodeRef.foo, @@ -468,11 +484,12 @@ describe('CSSTransition', () => { onEntered() { count++; expect(nodeRef.foo.current.className).toEqual('down-enter-done'); - resolve(); }, - })); + }); - expect(count).toEqual(4); + await waitFor(() => { + expect(count).toEqual(4); + }); }); }); }); diff --git a/test/CSSTransitionGroup-test.js b/test/CSSTransitionGroup-test.js index 4ccd48f2..3f37106c 100644 --- a/test/CSSTransitionGroup-test.js +++ b/test/CSSTransitionGroup-test.js @@ -218,7 +218,9 @@ describe('CSSTransitionGroup', () => { ReactDOM.unmountComponentAtNode(container); // Testing that no exception is thrown here, as the timeout has been cleared. - jest.runAllTimers(); + act(() => { + jest.runAllTimers(); + }); }); it('should handle unmounted elements properly', () => { diff --git a/test/Transition-test.js b/test/Transition-test.js index e7382e8b..e9124e14 100644 --- a/test/Transition-test.js +++ b/test/Transition-test.js @@ -1,7 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom'; -import { render } from './utils'; +import { render, waitFor } from './utils'; import Transition, { UNMOUNTED, @@ -27,14 +27,6 @@ expect.extend({ }); describe('Transition', () => { - // beforeEach(() => { - // jest.useFakeTimers(); - // }); - - // afterEach(() => { - // jest.useRealTimers(); - // }); - it('should not transition on mount', () => { const nodeRef = React.createRef(); render( @@ -119,8 +111,9 @@ describe('Transition', () => { expect(nodeRef.current.textContent).toBe('foo: foo, bar: bar'); }); - it('should allow addEndListener instead of timeouts', (done) => { + it('should allow addEndListener instead of timeouts', async () => { let listener = jest.fn((end) => setTimeout(end, 0)); + let done = false; const nodeRef = React.createRef(); const { setProps } = render( @@ -129,7 +122,7 @@ describe('Transition', () => { addEndListener={listener} onEntered={() => { expect(listener).toHaveBeenCalledTimes(1); - done(); + done = true; }} >
@@ -137,10 +130,15 @@ describe('Transition', () => { ); setProps({ in: true }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); - it('should fallback to timeouts with addEndListener', (done) => { + it('should fallback to timeouts with addEndListener', async () => { let calledEnd = false; + let done = false; let listener = (end) => setTimeout(() => { calledEnd = true; @@ -155,7 +153,7 @@ describe('Transition', () => { addEndListener={listener} onEntered={() => { expect(calledEnd).toEqual(false); - done(); + done = true; }} >
@@ -163,10 +161,15 @@ describe('Transition', () => { ); setProps({ in: true }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); - it('should mount/unmount immediately if not have enter/exit timeout', (done) => { + it('should mount/unmount immediately if not have enter/exit timeout', async () => { const nodeRef = React.createRef(); + let done = false; const { setProps } = render( {(status) =>
status: {status}
} @@ -182,12 +185,16 @@ describe('Transition', () => { in: false, onExited() { expect(nodeRef.current.textContent).toEqual(`status: ${EXITED}`); - if (!calledAfterTimeout) { - return done(); + if (calledAfterTimeout) { + throw new Error('wrong timeout'); } - throw new Error('wrong timeout'); + done = true; }, }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); it('should use `React.findDOMNode` when `nodeRef` is not provided', () => { @@ -220,8 +227,9 @@ describe('Transition', () => { }); describe('appearing timeout', () => { - it('should use enter timeout if appear not set', (done) => { + it('should use enter timeout if appear not set', async () => { let calledBeforeEntered = false; + let done = false; setTimeout(() => { calledBeforeEntered = true; }, 10); @@ -240,15 +248,20 @@ describe('Transition', () => { setProps({ onEntered() { if (calledBeforeEntered) { - done(); + done = true; } else { throw new Error('wrong timeout'); } }, }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); - it('should use appear timeout if appear is set', (done) => { + it('should use appear timeout if appear is set', async () => { + let done = false; const nodeRef = React.createRef(); const { setProps } = render( { if (isCausedLate) { throw new Error('wrong timeout'); } else { - done(); + done = true; } }, }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); }); describe('entering', () => { - it('should fire callbacks', (done) => { + it('should fire callbacks', async () => { let callOrder = []; + let done = false; let onEnter = jest.fn(() => callOrder.push('onEnter')); let onEntering = jest.fn(() => callOrder.push('onEntering')); const nodeRef = React.createRef(); @@ -303,12 +321,16 @@ describe('Transition', () => { expect(onEnter).toHaveBeenCalledTimes(1); expect(onEntering).toHaveBeenCalledTimes(1); expect(callOrder).toEqual(['onEnter', 'onEntering']); - done(); + done = true; }, }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); - it('should move to each transition state', (done) => { + it('should move to each transition state', async () => { let count = 0; const nodeRef = React.createRef(); const { setProps } = render( @@ -334,16 +356,19 @@ describe('Transition', () => { onEntered() { expect(nodeRef.current.textContent).toEqual(`status: ${ENTERED}`); - expect(count).toEqual(2); - done(); }, }); + + await waitFor(() => { + expect(count).toEqual(2); + }); }); }); describe('exiting', () => { - it('should fire callbacks', (done) => { + it('should fire callbacks', async () => { let callOrder = []; + let done = false; let onExit = jest.fn(() => callOrder.push('onExit')); let onExiting = jest.fn(() => callOrder.push('onExiting')); const nodeRef = React.createRef(); @@ -366,13 +391,18 @@ describe('Transition', () => { expect(onExit).toHaveBeenCalledTimes(1); expect(onExiting).toHaveBeenCalledTimes(1); expect(callOrder).toEqual(['onExit', 'onExiting']); - done(); + done = true; }, }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); - it('should move to each transition state', (done) => { + it('should move to each transition state', async () => { let count = 0; + let done = false; const nodeRef = React.createRef(); const { setProps } = render( @@ -398,9 +428,13 @@ describe('Transition', () => { onExited() { expect(nodeRef.current.textContent).toEqual(`status: ${EXITED}`); expect(count).toEqual(2); - done(); + done = true; }, }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); }); @@ -450,31 +484,33 @@ describe('Transition', () => { }); it('should stay mounted after exiting', async () => { - let handleEntered; - const onEntered = new Promise((resolve) => { - handleEntered = resolve; - }); - let handleExited; - const onExited = new Promise((resolve) => { - handleExited = resolve; - }); + let entered = false; + let exited = false; const { container, setProps } = render( { + entered = true; + }} + onExited={() => { + exited = true; + }} /> ); expect(container.textContent).toEqual(''); setProps({ in: true }); - await onEntered; + await waitFor(() => { + expect(entered).toEqual(true); + }); expect(container.textContent).toEqual(`status: ${ENTERED}`); setProps({ in: false }); - await onExited; + await waitFor(() => { + expect(exited).toEqual(true); + }); expect(container.textContent).toEqual(`status: ${EXITED}`); }); }); @@ -508,7 +544,8 @@ describe('Transition', () => { }; } - it('should mount when entering', (done) => { + it('should mount when entering', async () => { + let done = false; const instanceRef = React.createRef(); const { setProps } = render( { expect(instanceRef.current.getStatus()).toEqual(EXITED); expect(instanceRef.current.nodeRef.current).toExist(); - done(); + done = true; }} /> ); @@ -527,9 +564,14 @@ describe('Transition', () => { expect(instanceRef.current.nodeRef.current).toBeNull(); setProps({ in: true }); + + await waitFor(() => { + expect(done).toEqual(true); + }); }); - it('should unmount after exiting', (done) => { + it('should unmount after exiting', async () => { + let exited = false; const instanceRef = React.createRef(); const { setProps } = render( { in onExited={() => { setTimeout(() => { - expect(instanceRef.current.getStatus()).toEqual(UNMOUNTED); - expect(instanceRef.current.nodeRef.current).not.toExist(); - done(); + exited = true; }); }} /> @@ -549,6 +589,13 @@ describe('Transition', () => { expect(instanceRef.current.nodeRef.current).toExist(); setProps({ in: false }); + + await waitFor(() => { + expect(exited).toEqual(true); + }); + + expect(instanceRef.current.getStatus()).toEqual(UNMOUNTED); + expect(instanceRef.current.nodeRef.current).not.toExist(); }); }); }); From bcdcbbb06c0ffc96cc2e0a80e2d9da5124fcb855 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 17 Jan 2022 21:22:08 +0100 Subject: [PATCH 3/7] Run with next in CI --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 90105289..86b8af6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: # Otherwise how would we know if a specific React version caused the failure? fail-fast: false matrix: - REACT_DIST: [16, 17] + REACT_DIST: [16, 17, next] steps: - uses: actions/checkout@v2 From 870eba0dfd0d7b253db8d267a53117763756dfae Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 17 Jan 2022 21:36:19 +0100 Subject: [PATCH 4/7] Branch tests for React < 18 and 18 --- test/TransitionGroup-test.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/TransitionGroup-test.js b/test/TransitionGroup-test.js index 3fa82438..5a05a74b 100644 --- a/test/TransitionGroup-test.js +++ b/test/TransitionGroup-test.js @@ -92,14 +92,24 @@ describe('TransitionGroup', () => { act(() => { jest.runAllTimers(); }); - expect(log).toEqual(['appear', 'appear', 'appearing', 'appeared']); + expect(log).toEqual( + // FIXME: React 18 introduces an additional `onEnter` call. + React.useTransition !== undefined + ? ['appear', 'appear', 'appearing', 'appeared'] + : ['appear', 'appearing', 'appeared'] + ); log = []; renderStrict(, container); act(() => { jest.runAllTimers(); }); - expect(log).toEqual(['enter', 'enter', 'entering', 'entered']); + expect(log).toEqual( + // FIXME: React 18 introduces an additional `onEnter` call. + React.useTransition !== undefined + ? ['enter', 'enter', 'entering', 'entered'] + : ['enter', 'entering', 'entered'] + ); log = []; renderStrict(, container); From 8a36e9e34996d9bd4472244ef7700fa22aae28fd Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 17 Jan 2022 21:39:50 +0100 Subject: [PATCH 5/7] Don't block releases when React 18 fails (yet) --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 86b8af6d..8ff72bd3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,8 @@ jobs: fail-fast: false matrix: REACT_DIST: [16, 17, next] - + # Unstable release channel so let's not block a potential release of `react-transition-group` + continue-on-error: ${{ matrix.REACT_DIST == 'next' }} steps: - uses: actions/checkout@v2 - name: Use Node.js 14 From c1437f176cc10e6da6bd5904bfb931008789c264 Mon Sep 17 00:00:00 2001 From: eps1lon Date: Mon, 17 Jan 2022 21:56:26 +0100 Subject: [PATCH 6/7] double onEnter is due to StrictEffects --- test/TransitionGroup-test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/TransitionGroup-test.js b/test/TransitionGroup-test.js index 5a05a74b..d05516bc 100644 --- a/test/TransitionGroup-test.js +++ b/test/TransitionGroup-test.js @@ -75,7 +75,7 @@ describe('TransitionGroup', () => { renderStrict(, container); }); - it('should handle transitioning correctly', () => { + it.only('should handle transitioning correctly', () => { function Parent({ count = 1 }) { let children = []; for (let i = 0; i < count; i++) children.push(); @@ -93,7 +93,7 @@ describe('TransitionGroup', () => { jest.runAllTimers(); }); expect(log).toEqual( - // FIXME: React 18 introduces an additional `onEnter` call. + // React 18 StrictEffects will call `componentDidMount` twice causing two `onEnter` calls. React.useTransition !== undefined ? ['appear', 'appear', 'appearing', 'appeared'] : ['appear', 'appearing', 'appeared'] @@ -105,7 +105,7 @@ describe('TransitionGroup', () => { jest.runAllTimers(); }); expect(log).toEqual( - // FIXME: React 18 introduces an additional `onEnter` call. + // React 18 StrictEffects will call `componentDidMount` twice causing two `onEnter` calls. React.useTransition !== undefined ? ['enter', 'enter', 'entering', 'entered'] : ['enter', 'entering', 'entered'] From 8ac97da1ed8c6f52c64e31f86c648b16647cea5b Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 25 Jan 2022 11:29:03 +0100 Subject: [PATCH 7/7] Apply suggestions from code review Co-authored-by: Toru Kobayashi --- test/TransitionGroup-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TransitionGroup-test.js b/test/TransitionGroup-test.js index d05516bc..da07db44 100644 --- a/test/TransitionGroup-test.js +++ b/test/TransitionGroup-test.js @@ -75,7 +75,7 @@ describe('TransitionGroup', () => { renderStrict(, container); }); - it.only('should handle transitioning correctly', () => { + it('should handle transitioning correctly', () => { function Parent({ count = 1 }) { let children = []; for (let i = 0; i < count; i++) children.push();