How to use flushPassiveEffects method in Playwright Internal

Best JavaScript code snippet using playwright-internal

ReactHooksWithNoopRenderer.test.js

Source:ReactHooksWithNoopRenderer.test.js Github

copy

Full Screen

...421 }422 ReactNoop.render(<Counter count={0} />);423 expect(ReactNoop.flush()).toEqual(['Count: 0']);424 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);425 ReactNoop.flushPassiveEffects();426 expect(ReactNoop.clearYields()).toEqual(['Did commit [0]']);427 ReactNoop.render(<Counter count={1} />);428 expect(ReactNoop.flush()).toEqual(['Count: 1']);429 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);430 // Effects are deferred until after the commit431 ReactNoop.flushPassiveEffects();432 expect(ReactNoop.clearYields()).toEqual(['Did commit [1]']);433 });434 // tested in react-dom435 it.skip('flushes passive effects even with sibling deletions', () => {436 function LayoutEffect(props) {437 useLayoutEffect(() => {438 ReactNoop.yield(`Layout effect`);439 });440 return <Text text="Layout" />;441 }442 function PassiveEffect(props) {443 useEffect(() => {444 ReactNoop.yield(`Passive effect`);445 }, []);446 return <Text text="Passive" />;447 }448 let passive = <PassiveEffect key="p" />;449 ReactNoop.render([<LayoutEffect key="l" />, passive]);450 expect(ReactNoop.flush()).toEqual(['Layout', 'Passive', 'Layout effect']);451 expect(ReactNoop.getChildren()).toEqual([span('Layout'), span('Passive')]);452 // Destroying the first child shouldn't prevent the passive effect from453 // being executed454 ReactNoop.render([passive]);455 expect(ReactNoop.flush()).toEqual(['Passive effect']);456 expect(ReactNoop.getChildren()).toEqual([span('Passive')]);457 // (No effects are left to flush.)458 ReactNoop.flushPassiveEffects();459 expect(ReactNoop.clearYields()).toEqual(null);460 });461 it.skip('flushes passive effects even if siblings schedule an update', () => {462 function PassiveEffect(props) {463 useEffect(() => {464 ReactNoop.yield('Passive effect');465 });466 return <Text text="Passive" />;467 }468 function LayoutEffect(props) {469 let [count, setCount] = useState(0);470 useLayoutEffect(() => {471 // Scheduling work shouldn't interfere with the queued passive effect472 if (count === 0) {473 setCount(1);474 }475 ReactNoop.yield('Layout effect ' + count);476 });477 return <Text text="Layout" />;478 }479 ReactNoop.render([<PassiveEffect key="p" />, <LayoutEffect key="l" />]);480 act(() => {481 expect(ReactNoop.flush()).toEqual([482 'Passive',483 'Layout',484 'Layout effect 0',485 'Passive effect',486 'Layout',487 'Layout effect 1',488 ]);489 });490 expect(ReactNoop.getChildren()).toEqual([span('Passive'), span('Layout')]);491 });492 it.skip('flushes passive effects even if siblings schedule a new root', () => {493 function PassiveEffect(props) {494 useEffect(() => {495 ReactNoop.yield('Passive effect');496 }, []);497 return <Text text="Passive" />;498 }499 function LayoutEffect(props) {500 useLayoutEffect(() => {501 ReactNoop.yield('Layout effect');502 // Scheduling work shouldn't interfere with the queued passive effect503 ReactNoop.renderToRootWithID(<Text text="New Root" />, 'root2');504 });505 return <Text text="Layout" />;506 }507 ReactNoop.render([<PassiveEffect key="p" />, <LayoutEffect key="l" />]);508 expect(ReactNoop.flush()).toEqual(['Passive', 'Layout', 'Layout effect', 'Passive effect', 'New Root']);509 expect(ReactNoop.getChildren()).toEqual([span('Passive'), span('Layout')]);510 });511 it(512 'flushes effects serially by flushing old effects before flushing ' + "new ones, if they haven't already fired",513 () => {514 function getCommittedText() {515 const children = ReactNoop.getChildren();516 if (children === null) {517 return null;518 }519 return children[0].prop;520 }521 function Counter(props) {522 useEffect(() => {523 ReactNoop.yield(`Committed state when effect was fired: ${getCommittedText()}`);524 });525 return <Text text={props.count} />;526 }527 ReactNoop.render(<Counter count={0} />);528 expect(ReactNoop.flush()).toEqual([0]);529 expect(ReactNoop.getChildren()).toEqual([span(0)]);530 // Before the effects have a chance to flush, schedule another update531 ReactNoop.render(<Counter count={1} />);532 expect(ReactNoop.flush()).toEqual([533 // The previous effect flushes before the reconciliation534 'Committed state when effect was fired: 0',535 1,536 ]);537 expect(ReactNoop.getChildren()).toEqual([span(1)]);538 ReactNoop.flushPassiveEffects();539 expect(ReactNoop.clearYields()).toEqual(['Committed state when effect was fired: 1']);540 },541 );542 it('updates have async priority', () => {543 function Counter(props) {544 const [count, updateCount] = useState('(empty)');545 useEffect(() => {546 ReactNoop.yield(`Schedule update [${props.count}]`);547 updateCount(props.count);548 }, [props.count]);549 return <Text text={'Count: ' + count} />;550 }551 ReactNoop.render(<Counter count={0} />);552 expect(ReactNoop.flush()).toEqual(['Count: (empty)']);553 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);554 ReactNoop.flushPassiveEffects();555 expect(ReactNoop.clearYields()).toEqual(['Schedule update [0]']);556 expect(ReactNoop.flush()).toEqual(['Count: 0']);557 ReactNoop.render(<Counter count={1} />);558 expect(ReactNoop.flush()).toEqual(['Count: 0']);559 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);560 ReactNoop.flushPassiveEffects();561 expect(ReactNoop.clearYields()).toEqual(['Schedule update [1]']);562 expect(ReactNoop.flush()).toEqual(['Count: 1']);563 });564 it.skip('updates have async priority even if effects are flushed early', () => {565 function Counter(props) {566 const [count, updateCount] = useState('(empty)');567 useEffect(() => {568 ReactNoop.yield(`Schedule update [${props.count}]`);569 updateCount(props.count);570 }, [props.count]);571 return <Text text={'Count: ' + count} />;572 }573 ReactNoop.render(<Counter count={0} />);574 expect(ReactNoop.flush()).toEqual(['Count: (empty)']);575 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);576 // Rendering again should flush the previous commit's effects577 ReactNoop.render(<Counter count={1} />);578 ReactNoop.flushThrough(['Schedule update [0]', 'Count: 0']);579 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);580 ReactNoop.batchedUpdates(() => {581 expect(ReactNoop.flush()).toEqual([]);582 });583 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);584 ReactNoop.flushPassiveEffects();585 expect(ReactNoop.flush()).toEqual(['Schedule update [1]', 'Count: 1']);586 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);587 });588 it.skip('flushes serial effects before enqueueing work', () => {589 let _updateCount;590 function Counter(props) {591 const [count, updateCount] = useState(0);592 _updateCount = updateCount;593 useEffect(() => {594 ReactNoop.yield(`Will set count to 1`);595 updateCount(1);596 }, []);597 return <Text text={'Count: ' + count} />;598 }599 ReactNoop.render(<Counter count={0} />);600 expect(ReactNoop.flush()).toEqual(['Count: 0']);601 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);602 // Enqueuing this update forces the passive effect to be flushed --603 // updateCount(1) happens first, so 2 wins.604 act(() => _updateCount(2));605 expect(ReactNoop.flush()).toEqual(['Will set count to 1', 'Count: 2']);606 expect(ReactNoop.getChildren()).toEqual([span('Count: 2')]);607 });608 it.skip('flushes serial effects before enqueueing work (with tracing)', () => {609 const onInteractionScheduledWorkCompleted = jest.fn();610 const onWorkCanceled = jest.fn();611 SchedulerTracing.unstable_subscribe({612 onInteractionScheduledWorkCompleted,613 onInteractionTraced: jest.fn(),614 onWorkCanceled,615 onWorkScheduled: jest.fn(),616 onWorkStarted: jest.fn(),617 onWorkStopped: jest.fn(),618 });619 let _updateCount;620 function Counter(props) {621 const [count, updateCount] = useState(0);622 _updateCount = updateCount;623 useEffect(() => {624 expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([tracingEvent]);625 ReactNoop.yield(`Will set count to 1`);626 updateCount(1);627 }, []);628 return <Text text={'Count: ' + count} />;629 }630 const tracingEvent = { id: 0, name: 'hello', timestamp: 0 };631 SchedulerTracing.unstable_trace(tracingEvent.name, tracingEvent.timestamp, () => {632 ReactNoop.render(<Counter count={0} />);633 });634 expect(ReactNoop.flush()).toEqual(['Count: 0']);635 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);636 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(0);637 // Enqueuing this update forces the passive effect to be flushed --638 // updateCount(1) happens first, so 2 wins.639 act(() => _updateCount(2));640 expect(ReactNoop.flush()).toEqual(['Will set count to 1', 'Count: 2']);641 expect(ReactNoop.getChildren()).toEqual([span('Count: 2')]);642 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);643 expect(onWorkCanceled).toHaveBeenCalledTimes(0);644 });645 it.skip('in sync mode, useEffect is deferred and updates finish synchronously ' + '(in a single batch)', () => {646 function Counter(props) {647 const [count, updateCount] = useState('(empty)');648 useEffect(() => {649 // Update multiple times. These should all be batched together in650 // a single render.651 updateCount(props.count);652 updateCount(props.count);653 updateCount(props.count);654 updateCount(props.count);655 updateCount(props.count);656 updateCount(props.count);657 }, [props.count]);658 return <Text text={'Count: ' + count} />;659 }660 ReactNoop.renderLegacySyncRoot(<Counter count={0} />);661 // Even in sync mode, effects are deferred until after paint662 expect(ReactNoop.flush()).toEqual(['Count: (empty)']);663 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);664 // Now fire the effects665 ReactNoop.flushPassiveEffects();666 // There were multiple updates, but there should only be a667 // single render668 expect(ReactNoop.clearYields()).toEqual(['Count: 0']);669 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);670 });671 it.skip('flushSync is not allowed', () => {672 function Counter(props) {673 const [count, updateCount] = useState('(empty)');674 useEffect(() => {675 ReactNoop.yield(`Schedule update [${props.count}]`);676 ReactNoop.flushSync(() => {677 updateCount(props.count);678 });679 }, [props.count]);680 return <Text text={'Count: ' + count} />;681 }682 ReactNoop.render(<Counter count={0} />);683 expect(ReactNoop.flush()).toEqual(['Count: (empty)']);684 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);685 expect(() => {686 ReactNoop.flushPassiveEffects();687 }).toThrow('flushSync was called from inside a lifecycle method');688 });689 it('unmounts previous effect', () => {690 function Counter(props) {691 useEffect(() => {692 ReactNoop.yield(`Did create [${props.count}]`);693 return () => {694 ReactNoop.yield(`Did destroy [${props.count}]`);695 };696 });697 return <Text text={'Count: ' + props.count} />;698 }699 ReactNoop.render(<Counter count={0} />);700 expect(ReactNoop.flush()).toEqual(['Count: 0']);701 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);702 ReactNoop.flushPassiveEffects();703 expect(ReactNoop.clearYields()).toEqual(['Did create [0]']);704 ReactNoop.render(<Counter count={1} />);705 expect(ReactNoop.flush()).toEqual(['Count: 1']);706 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);707 ReactNoop.flushPassiveEffects();708 expect(ReactNoop.clearYields()).toEqual(['Did destroy [0]', 'Did create [1]']);709 });710 it('unmounts on deletion', () => {711 function Counter(props) {712 useEffect(() => {713 ReactNoop.yield(`Did create [${props.count}]`);714 return () => {715 ReactNoop.yield(`Did destroy [${props.count}]`);716 };717 });718 return <Text text={'Count: ' + props.count} />;719 }720 ReactNoop.render(<Counter count={0} />);721 expect(ReactNoop.flush()).toEqual(['Count: 0']);722 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);723 ReactNoop.flushPassiveEffects();724 expect(ReactNoop.clearYields()).toEqual(['Did create [0]']);725 ReactNoop.render(null);726 expect(ReactNoop.flush()).toEqual(['Did destroy [0]']);727 expect(ReactNoop.getChildren()).toEqual([]);728 });729 it('unmounts on deletion after skipped effect', () => {730 function Counter(props) {731 useEffect(() => {732 ReactNoop.yield(`Did create [${props.count}]`);733 return () => {734 ReactNoop.yield(`Did destroy [${props.count}]`);735 };736 }, []);737 return <Text text={'Count: ' + props.count} />;738 }739 ReactNoop.render(<Counter count={0} />);740 expect(ReactNoop.flush()).toEqual(['Count: 0']);741 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);742 ReactNoop.flushPassiveEffects();743 expect(ReactNoop.clearYields()).toEqual(['Did create [0]']);744 ReactNoop.render(<Counter count={1} />);745 expect(ReactNoop.flush()).toEqual(['Count: 1']);746 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);747 ReactNoop.flushPassiveEffects();748 expect(ReactNoop.clearYields()).toEqual(null);749 ReactNoop.render(null);750 expect(ReactNoop.flush()).toEqual(['Did destroy [0]']);751 expect(ReactNoop.getChildren()).toEqual([]);752 });753 it('always fires effects if no dependencies are provided', () => {754 function effect() {755 ReactNoop.yield(`Did create`);756 return () => {757 ReactNoop.yield(`Did destroy`);758 };759 }760 function Counter(props) {761 useEffect(effect);762 return <Text text={'Count: ' + props.count} />;763 }764 ReactNoop.render(<Counter count={0} />);765 expect(ReactNoop.flush()).toEqual(['Count: 0']);766 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);767 ReactNoop.flushPassiveEffects();768 expect(ReactNoop.clearYields()).toEqual(['Did create']);769 ReactNoop.render(<Counter count={1} />);770 expect(ReactNoop.flush()).toEqual(['Count: 1']);771 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);772 ReactNoop.flushPassiveEffects();773 expect(ReactNoop.clearYields()).toEqual(['Did destroy', 'Did create']);774 ReactNoop.render(null);775 expect(ReactNoop.flush()).toEqual(['Did destroy']);776 expect(ReactNoop.getChildren()).toEqual([]);777 });778 it('skips effect if inputs have not changed', () => {779 function Counter(props) {780 const text = `${props.label}: ${props.count}`;781 useEffect(() => {782 ReactNoop.yield(`Did create [${text}]`);783 return () => {784 ReactNoop.yield(`Did destroy [${text}]`);785 };786 }, [props.label, props.count]);787 return <Text text={text} />;788 }789 ReactNoop.render(<Counter label="Count" count={0} />);790 expect(ReactNoop.flush()).toEqual(['Count: 0']);791 ReactNoop.flushPassiveEffects();792 expect(ReactNoop.clearYields()).toEqual(['Did create [Count: 0]']);793 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);794 ReactNoop.render(<Counter label="Count" count={1} />);795 // Count changed796 expect(ReactNoop.flush()).toEqual(['Count: 1']);797 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);798 ReactNoop.flushPassiveEffects();799 expect(ReactNoop.clearYields()).toEqual(['Did destroy [Count: 0]', 'Did create [Count: 1]']);800 ReactNoop.render(<Counter label="Count" count={1} />);801 // Nothing changed, so no effect should have fired802 expect(ReactNoop.flush()).toEqual(['Count: 1']);803 ReactNoop.flushPassiveEffects();804 expect(ReactNoop.clearYields()).toEqual(null);805 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);806 ReactNoop.render(<Counter label="Total" count={1} />);807 // Label changed808 expect(ReactNoop.flush()).toEqual(['Total: 1']);809 expect(ReactNoop.getChildren()).toEqual([span('Total: 1')]);810 ReactNoop.flushPassiveEffects();811 expect(ReactNoop.clearYields()).toEqual(['Did destroy [Count: 1]', 'Did create [Total: 1]']);812 });813 it('multiple effects', () => {814 function Counter(props) {815 useEffect(() => {816 ReactNoop.yield(`Did commit 1 [${props.count}]`);817 });818 useEffect(() => {819 ReactNoop.yield(`Did commit 2 [${props.count}]`);820 });821 return <Text text={'Count: ' + props.count} />;822 }823 ReactNoop.render(<Counter count={0} />);824 expect(ReactNoop.flush()).toEqual(['Count: 0']);825 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);826 ReactNoop.flushPassiveEffects();827 expect(ReactNoop.clearYields()).toEqual(['Did commit 1 [0]', 'Did commit 2 [0]']);828 ReactNoop.render(<Counter count={1} />);829 expect(ReactNoop.flush()).toEqual(['Count: 1']);830 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);831 ReactNoop.flushPassiveEffects();832 expect(ReactNoop.clearYields()).toEqual(['Did commit 1 [1]', 'Did commit 2 [1]']);833 });834 it('unmounts all previous effects before creating any new ones', () => {835 function Counter(props) {836 useEffect(() => {837 ReactNoop.yield(`Mount A [${props.count}]`);838 return () => {839 ReactNoop.yield(`Unmount A [${props.count}]`);840 };841 });842 useEffect(() => {843 ReactNoop.yield(`Mount B [${props.count}]`);844 return () => {845 ReactNoop.yield(`Unmount B [${props.count}]`);846 };847 });848 return <Text text={'Count: ' + props.count} />;849 }850 ReactNoop.render(<Counter count={0} />);851 expect(ReactNoop.flush()).toEqual(['Count: 0']);852 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);853 ReactNoop.flushPassiveEffects();854 expect(ReactNoop.clearYields()).toEqual(['Mount A [0]', 'Mount B [0]']);855 ReactNoop.render(<Counter count={1} />);856 expect(ReactNoop.flush()).toEqual(['Count: 1']);857 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);858 ReactNoop.flushPassiveEffects();859 expect(ReactNoop.clearYields()).toEqual(['Unmount A [0]', 'Unmount B [0]', 'Mount A [1]', 'Mount B [1]']);860 });861 it.skip('handles errors on mount', () => {862 function Counter(props) {863 useEffect(() => {864 ReactNoop.yield(`Mount A [${props.count}]`);865 return () => {866 ReactNoop.yield(`Unmount A [${props.count}]`);867 };868 });869 useEffect(() => {870 ReactNoop.yield('Oops!');871 throw new Error('Oops!');872 // eslint-disable-next-line no-unreachable873 ReactNoop.yield(`Mount B [${props.count}]`);874 return () => {875 ReactNoop.yield(`Unmount B [${props.count}]`);876 };877 });878 return <Text text={'Count: ' + props.count} />;879 }880 ReactNoop.render(<Counter count={0} />);881 expect(ReactNoop.flush()).toEqual(['Count: 0']);882 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);883 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');884 expect(ReactNoop.clearYields()).toEqual([885 'Mount A [0]',886 'Oops!',887 // Clean up effect A. There's no effect B to clean-up, because it888 // never mounted.889 'Unmount A [0]',890 ]);891 expect(ReactNoop.getChildren()).toEqual([]);892 });893 it.skip('handles errors on update', () => {894 function Counter(props) {895 useEffect(() => {896 ReactNoop.yield(`Mount A [${props.count}]`);897 return () => {898 ReactNoop.yield(`Unmount A [${props.count}]`);899 };900 });901 useEffect(() => {902 if (props.count === 1) {903 ReactNoop.yield('Oops!');904 throw new Error('Oops!');905 }906 ReactNoop.yield(`Mount B [${props.count}]`);907 return () => {908 ReactNoop.yield(`Unmount B [${props.count}]`);909 };910 });911 return <Text text={'Count: ' + props.count} />;912 }913 ReactNoop.render(<Counter count={0} />);914 expect(ReactNoop.flush()).toEqual(['Count: 0']);915 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);916 ReactNoop.flushPassiveEffects();917 expect(ReactNoop.clearYields()).toEqual(['Mount A [0]', 'Mount B [0]']);918 // This update will trigger an errror919 ReactNoop.render(<Counter count={1} />);920 expect(ReactNoop.flush()).toEqual(['Count: 1']);921 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);922 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');923 expect(ReactNoop.clearYields()).toEqual([924 'Unmount A [0]',925 'Unmount B [0]',926 'Mount A [1]',927 'Oops!',928 // Clean up effect A. There's no effect B to clean-up, because it929 // never mounted.930 'Unmount A [1]',931 ]);932 expect(ReactNoop.getChildren()).toEqual([]);933 });934 it.skip('handles errors on unmount', () => {935 function Counter(props) {936 useEffect(() => {937 ReactNoop.yield(`Mount A [${props.count}]`);938 return () => {939 ReactNoop.yield('Oops!');940 throw new Error('Oops!');941 // eslint-disable-next-line no-unreachable942 ReactNoop.yield(`Unmount A [${props.count}]`);943 };944 });945 useEffect(() => {946 ReactNoop.yield(`Mount B [${props.count}]`);947 return () => {948 ReactNoop.yield(`Unmount B [${props.count}]`);949 };950 });951 return <Text text={'Count: ' + props.count} />;952 }953 ReactNoop.render(<Counter count={0} />);954 expect(ReactNoop.flush()).toEqual(['Count: 0']);955 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);956 ReactNoop.flushPassiveEffects();957 expect(ReactNoop.clearYields()).toEqual(['Mount A [0]', 'Mount B [0]']);958 // This update will trigger an errror959 ReactNoop.render(<Counter count={1} />);960 expect(ReactNoop.flush()).toEqual(['Count: 1']);961 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);962 expect(() => ReactNoop.flushPassiveEffects()).toThrow('Oops');963 expect(ReactNoop.clearYields()).toEqual([964 'Oops!',965 // B unmounts even though an error was thrown in the previous effect966 'Unmount B [0]',967 ]);968 expect(ReactNoop.getChildren()).toEqual([]);969 });970 it('works with memo', () => {971 function Counter({ count }) {972 useLayoutEffect(() => {973 ReactNoop.yield('Mount: ' + count);974 return () => ReactNoop.yield('Unmount: ' + count);975 });976 return <Text text={'Count: ' + count} />;977 }978 Counter = memo(Counter);979 ReactNoop.render(<Counter count={0} />);980 expect(ReactNoop.flush()).toEqual(['Count: 0', 'Mount: 0']);981 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);982 ReactNoop.render(<Counter count={1} />);983 expect(ReactNoop.flush()).toEqual(['Count: 1', 'Unmount: 0', 'Mount: 1']);984 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);985 ReactNoop.render(null);986 expect(ReactNoop.flush()).toEqual(['Unmount: 1']);987 expect(ReactNoop.getChildren()).toEqual([]);988 });989 });990 describe('useLayoutEffect', () => {991 it('fires layout effects after the host has been mutated', () => {992 function getCommittedText() {993 const children = ReactNoop.getChildren();994 if (children === null) {995 return null;996 }997 return children[0].prop;998 }999 function Counter(props) {1000 useLayoutEffect(() => {1001 ReactNoop.yield(`Current: ${getCommittedText()}`);1002 });1003 return <Text text={props.count} />;1004 }1005 ReactNoop.render(<Counter count={0} />);1006 expect(ReactNoop.flush()).toEqual([0, 'Current: 0']);1007 expect(ReactNoop.getChildren()).toEqual([span(0)]);1008 ReactNoop.render(<Counter count={1} />);1009 expect(ReactNoop.flush()).toEqual([1, 'Current: 1']);1010 expect(ReactNoop.getChildren()).toEqual([span(1)]);1011 });1012 it.skip('force flushes passive effects before firing new layout effects', () => {1013 let committedText = '(empty)';1014 function Counter(props) {1015 useLayoutEffect(() => {1016 // Normally this would go in a mutation effect, but this test1017 // intentionally omits a mutation effect.1018 committedText = props.count + '';1019 ReactNoop.yield(`Mount layout [current: ${committedText}]`);1020 return () => {1021 ReactNoop.yield(`Unmount layout [current: ${committedText}]`);1022 };1023 });1024 useEffect(() => {1025 ReactNoop.yield(`Mount normal [current: ${committedText}]`);1026 return () => {1027 ReactNoop.yield(`Unmount normal [current: ${committedText}]`);1028 };1029 });1030 return null;1031 }1032 ReactNoop.render(<Counter count={0} />);1033 expect(ReactNoop.flush()).toEqual(['Mount layout [current: 0]']);1034 expect(committedText).toEqual('0');1035 ReactNoop.render(<Counter count={1} />);1036 expect(ReactNoop.flush()).toEqual([1037 'Mount normal [current: 0]',1038 'Unmount layout [current: 0]',1039 'Mount layout [current: 1]',1040 ]);1041 expect(committedText).toEqual('1');1042 ReactNoop.flushPassiveEffects();1043 expect(ReactNoop.clearYields()).toEqual(['Unmount normal [current: 1]', 'Mount normal [current: 1]']);1044 });1045 });1046 describe('useCallback', () => {1047 it('memoizes callback by comparing inputs', () => {1048 class IncrementButton extends React.PureComponent {1049 increment = () => {1050 this.props.increment();1051 };1052 render() {1053 return <Text text="Increment" />;1054 }1055 }1056 function Counter({ incrementBy }) {1057 const [count, updateCount] = useState(0);1058 const increment = useCallback(() => updateCount(c => c + incrementBy), [incrementBy]);1059 return (1060 <React.Fragment>1061 <IncrementButton increment={increment} ref={button} />1062 <Text text={'Count: ' + count} />1063 </React.Fragment>1064 );1065 }1066 const button = React.createRef(null);1067 ReactNoop.render(<Counter incrementBy={1} />);1068 expect(ReactNoop.flush()).toEqual(['Increment', 'Count: 0']);1069 expect(ReactNoop.getChildren()).toEqual([span('Increment'), span('Count: 0')]);1070 act(button.current.increment);1071 expect(ReactNoop.flush()).toEqual([1072 // Button should not re-render, because its props haven't changed1073 // 'Increment',1074 'Count: 1',1075 ]);1076 expect(ReactNoop.getChildren()).toEqual([span('Increment'), span('Count: 1')]);1077 // Increase the increment amount1078 ReactNoop.render(<Counter incrementBy={10} />);1079 expect(ReactNoop.flush()).toEqual([1080 // Inputs did change this time1081 'Increment',1082 'Count: 1',1083 ]);1084 expect(ReactNoop.getChildren()).toEqual([span('Increment'), span('Count: 1')]);1085 // Callback should have updated1086 act(button.current.increment);1087 expect(ReactNoop.flush()).toEqual(['Count: 11']);1088 expect(ReactNoop.getChildren()).toEqual([span('Increment'), span('Count: 11')]);1089 });1090 });1091 describe('useMemo', () => {1092 it('memoizes value by comparing to previous inputs', () => {1093 function CapitalizedText(props) {1094 const text = props.text;1095 const capitalizedText = useMemo(() => {1096 ReactNoop.yield(`Capitalize '${text}'`);1097 return text.toUpperCase();1098 }, [text]);1099 return <Text text={capitalizedText} />;1100 }1101 ReactNoop.render(<CapitalizedText text="hello" />);1102 expect(ReactNoop.flush()).toEqual(["Capitalize 'hello'", 'HELLO']);1103 expect(ReactNoop.getChildren()).toEqual([span('HELLO')]);1104 ReactNoop.render(<CapitalizedText text="hi" />);1105 expect(ReactNoop.flush()).toEqual(["Capitalize 'hi'", 'HI']);1106 expect(ReactNoop.getChildren()).toEqual([span('HI')]);1107 ReactNoop.render(<CapitalizedText text="hi" />);1108 expect(ReactNoop.flush()).toEqual(['HI']);1109 expect(ReactNoop.getChildren()).toEqual([span('HI')]);1110 ReactNoop.render(<CapitalizedText text="goodbye" />);1111 expect(ReactNoop.flush()).toEqual(["Capitalize 'goodbye'", 'GOODBYE']);1112 expect(ReactNoop.getChildren()).toEqual([span('GOODBYE')]);1113 });1114 it('always re-computes if no inputs are provided', () => {1115 function LazyCompute(props) {1116 const computed = useMemo(props.compute);1117 return <Text text={computed} />;1118 }1119 function computeA() {1120 ReactNoop.yield('compute A');1121 return 'A';1122 }1123 function computeB() {1124 ReactNoop.yield('compute B');1125 return 'B';1126 }1127 ReactNoop.render(<LazyCompute compute={computeA} />);1128 expect(ReactNoop.flush()).toEqual(['compute A', 'A']);1129 ReactNoop.render(<LazyCompute compute={computeA} />);1130 expect(ReactNoop.flush()).toEqual(['compute A', 'A']);1131 ReactNoop.render(<LazyCompute compute={computeA} />);1132 expect(ReactNoop.flush()).toEqual(['compute A', 'A']);1133 ReactNoop.render(<LazyCompute compute={computeB} />);1134 expect(ReactNoop.flush()).toEqual(['compute B', 'B']);1135 });1136 it('should not invoke memoized function during re-renders unless inputs change', () => {1137 function LazyCompute(props) {1138 const computed = useMemo(() => props.compute(props.input), [props.input]);1139 const [count, setCount] = useState(0);1140 if (count < 3) {1141 setCount(count + 1);1142 }1143 return <Text text={computed} />;1144 }1145 function compute(val) {1146 ReactNoop.yield('compute ' + val);1147 return val;1148 }1149 ReactNoop.render(<LazyCompute compute={compute} input="A" />);1150 expect(ReactNoop.flush()).toEqual(['compute A', 'A']);1151 ReactNoop.render(<LazyCompute compute={compute} input="A" />);1152 expect(ReactNoop.flush()).toEqual(['A']);1153 ReactNoop.render(<LazyCompute compute={compute} input="B" />);1154 expect(ReactNoop.flush()).toEqual(['compute B', 'B']);1155 });1156 });1157 describe('useRef', () => {1158 it('creates a ref object initialized with the provided value', () => {1159 jest.useFakeTimers();1160 function useDebouncedCallback(callback, ms, inputs) {1161 const timeoutID = useRef(-1);1162 useEffect(() => {1163 return function unmount() {1164 clearTimeout(timeoutID.current);1165 };1166 }, []);1167 const debouncedCallback = useCallback(1168 (...args) => {1169 clearTimeout(timeoutID.current);1170 timeoutID.current = setTimeout(callback, ms, ...args);1171 },1172 [callback, ms],1173 );1174 return useCallback(debouncedCallback, inputs);1175 }1176 let ping;1177 function App() {1178 ping = useDebouncedCallback(1179 value => {1180 ReactNoop.yield('ping: ' + value);1181 },1182 100,1183 [],1184 );1185 return null;1186 }1187 ReactNoop.render(<App />);1188 expect(ReactNoop.flush()).toEqual([]);1189 ping(1);1190 ping(2);1191 ping(3);1192 expect(ReactNoop.flush()).toEqual([]);1193 jest.advanceTimersByTime(100);1194 expect(ReactNoop.flush()).toEqual(['ping: 3']);1195 ping(4);1196 jest.advanceTimersByTime(20);1197 ping(5);1198 ping(6);1199 jest.advanceTimersByTime(80);1200 expect(ReactNoop.flush()).toEqual([]);1201 jest.advanceTimersByTime(20);1202 expect(ReactNoop.flush()).toEqual(['ping: 6']);1203 });1204 it('should return the same ref during re-renders', () => {1205 function Counter() {1206 const ref = useRef('val');1207 const [count, setCount] = useState(0);1208 const [firstRef] = useState(ref);1209 if (firstRef !== ref) {1210 throw new Error('should never change');1211 }1212 if (count < 3) {1213 setCount(count + 1);1214 }1215 return <Text text={ref.current} />;1216 }1217 ReactNoop.render(<Counter />);1218 expect(ReactNoop.flush()).toEqual(['val']);1219 ReactNoop.render(<Counter />);1220 expect(ReactNoop.flush()).toEqual(['val']);1221 });1222 });1223 describe('useImperativeHandle', () => {1224 it('does not update when deps are the same', () => {1225 const INCREMENT = 'INCREMENT';1226 function reducer(state, action) {1227 return action === INCREMENT ? state + 1 : state;1228 }1229 function Counter(props, ref) {1230 const [count, dispatch] = useReducer(reducer, 0);1231 useImperativeHandle(ref, () => ({ count, dispatch }), []);1232 return <Text text={'Count: ' + count} />;1233 }1234 Counter = forwardRef(Counter);1235 const counter = React.createRef(null);1236 ReactNoop.render(<Counter ref={counter} />);1237 ReactNoop.flush();1238 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);1239 expect(counter.current.count).toBe(0);1240 act(() => {1241 counter.current.dispatch(INCREMENT);1242 });1243 ReactNoop.flush();1244 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);1245 // Intentionally not updated because of [] deps:1246 expect(counter.current.count).toBe(0);1247 });1248 // Regression test for https://github.com/facebook/react/issues/147821249 it.skip('automatically updates when deps are not specified', () => {1250 const INCREMENT = 'INCREMENT';1251 function reducer(state, action) {1252 return action === INCREMENT ? state + 1 : state;1253 }1254 function Counter(props, ref) {1255 const [count, dispatch] = useReducer(reducer, 0);1256 useImperativeHandle(ref, () => ({ count, dispatch }));1257 return <Text text={'Count: ' + count} />;1258 }1259 Counter = forwardRef(Counter);1260 const counter = React.createRef(null);1261 ReactNoop.render(<Counter ref={counter} />);1262 ReactNoop.flush();1263 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);1264 expect(counter.current.count).toBe(0);1265 act(() => {1266 counter.current.dispatch(INCREMENT);1267 });1268 ReactNoop.flush();1269 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);1270 expect(counter.current.count).toBe(1);1271 });1272 it('updates when deps are different', () => {1273 const INCREMENT = 'INCREMENT';1274 function reducer(state, action) {1275 return action === INCREMENT ? state + 1 : state;1276 }1277 let totalRefUpdates = 0;1278 function Counter(props, ref) {1279 const [count, dispatch] = useReducer(reducer, 0);1280 useImperativeHandle(1281 ref,1282 () => {1283 totalRefUpdates++;1284 return { count, dispatch };1285 },1286 [count],1287 );1288 return <Text text={'Count: ' + count} />;1289 }1290 Counter = forwardRef(Counter);1291 const counter = React.createRef(null);1292 ReactNoop.render(<Counter ref={counter} />);1293 ReactNoop.flush();1294 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);1295 expect(counter.current.count).toBe(0);1296 expect(totalRefUpdates).toBe(1);1297 act(() => {1298 counter.current.dispatch(INCREMENT);1299 });1300 ReactNoop.flush();1301 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);1302 expect(counter.current.count).toBe(1);1303 expect(totalRefUpdates).toBe(2);1304 // Update that doesn't change the ref dependencies1305 ReactNoop.render(<Counter ref={counter} />);1306 ReactNoop.flush();1307 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);1308 expect(counter.current.count).toBe(1);1309 expect(totalRefUpdates).toBe(2); // Should not increase since last time1310 });1311 });1312 describe('progressive enhancement (not supported)', () => {1313 it('mount additional state', () => {1314 let updateA;1315 let updateB;1316 // let updateC;1317 function App(props) {1318 const [A, _updateA] = useState(0);1319 const [B, _updateB] = useState(0);1320 updateA = _updateA;1321 updateB = _updateB;1322 let C;1323 if (props.loadC) {1324 useState(0);1325 } else {1326 C = '[not loaded]';1327 }1328 return <Text text={`A: ${A}, B: ${B}, C: ${C}`} />;1329 }1330 ReactNoop.render(<App loadC={false} />);1331 expect(ReactNoop.flush()).toEqual(['A: 0, B: 0, C: [not loaded]']);1332 expect(ReactNoop.getChildren()).toEqual([span('A: 0, B: 0, C: [not loaded]')]);1333 act(() => {1334 updateA(2);1335 updateB(3);1336 });1337 expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: [not loaded]']);1338 expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: [not loaded]')]);1339 ReactNoop.render(<App loadC={true} />);1340 expect(() => {1341 expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: 0']);1342 }).toThrow('Rendered more hooks than during the previous render');1343 // Uncomment if/when we support this again1344 // expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 0')]);1345 // updateC(4);1346 // expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: 4']);1347 // expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 4')]);1348 });1349 it('unmount state', () => {1350 let updateA;1351 let updateB;1352 let updateC;1353 function App(props) {1354 const [A, _updateA] = useState(0);1355 const [B, _updateB] = useState(0);1356 updateA = _updateA;1357 updateB = _updateB;1358 let C;1359 if (props.loadC) {1360 const [_C, _updateC] = useState(0);1361 C = _C;1362 updateC = _updateC;1363 } else {1364 C = '[not loaded]';1365 }1366 return <Text text={`A: ${A}, B: ${B}, C: ${C}`} />;1367 }1368 ReactNoop.render(<App loadC={true} />);1369 expect(ReactNoop.flush()).toEqual(['A: 0, B: 0, C: 0']);1370 expect(ReactNoop.getChildren()).toEqual([span('A: 0, B: 0, C: 0')]);1371 act(() => {1372 updateA(2);1373 updateB(3);1374 updateC(4);1375 });1376 expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: 4']);1377 expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 4')]);1378 // Skip1379 // ReactNoop.render(<App loadC={false} />);1380 // expect(() => ReactNoop.flush()).toThrow(1381 // 'Rendered fewer hooks than expected. This may be caused by an ' + 'accidental early return statement.',1382 // );1383 });1384 it.skip('unmount effects', () => {1385 function App(props) {1386 useEffect(() => {1387 ReactNoop.yield('Mount A');1388 return () => {1389 ReactNoop.yield('Unmount A');1390 };1391 }, []);1392 if (props.showMore) {1393 useEffect(() => {1394 ReactNoop.yield('Mount B');1395 return () => {1396 ReactNoop.yield('Unmount B');1397 };1398 }, []);1399 }1400 return null;1401 }1402 ReactNoop.render(<App showMore={false} />);1403 expect(ReactNoop.flush()).toEqual([]);1404 ReactNoop.flushPassiveEffects();1405 expect(ReactNoop.clearYields()).toEqual(['Mount A']);1406 ReactNoop.render(<App showMore={true} />);1407 expect(() => {1408 expect(ReactNoop.flush()).toEqual([]);1409 }).toThrow('Rendered more hooks than during the previous render');1410 // Uncomment if/when we support this again1411 // ReactNoop.flushPassiveEffects();1412 // expect(ReactNoop.clearYields()).toEqual(['Mount B']);1413 // ReactNoop.render(<App showMore={false} />);1414 // expect(() => ReactNoop.flush()).toThrow(1415 // 'Rendered fewer hooks than expected. This may be caused by an ' +1416 // 'accidental early return statement.',1417 // );1418 });1419 });1420 describe('useContext', () => {1421 it('simple use', () => {1422 const CounterContext = createContext();1423 const Counter = () => {1424 const count = useContext(CounterContext);1425 return <Text text={count} />;...

Full Screen

Full Screen

ReactHooksWithNoopRenderer-test.internal.js

Source:ReactHooksWithNoopRenderer-test.internal.js Github

copy

Full Screen

...532 }533 ReactNoop.render(<Counter count={0} />);534 expect(ReactNoop.flush()).toEqual(['Count: 0']);535 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);536 flushPassiveEffects();537 expect(ReactNoop.clearYields()).toEqual(['Did commit [0]']);538 ReactNoop.render(<Counter count={1} />);539 expect(ReactNoop.flush()).toEqual(['Count: 1']);540 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);541 // Effects are deferred until after the commit542 flushPassiveEffects();543 expect(ReactNoop.clearYields()).toEqual(['Did commit [1]']);544 });545 it('flushes passive effects even with sibling deletions', () => {546 function LayoutEffect(props) {547 useLayoutEffect(() => {548 ReactNoop.yield(`Layout effect`);549 });550 return <Text text="Layout" />;551 }552 function PassiveEffect(props) {553 useEffect(() => {554 ReactNoop.yield(`Passive effect`);555 }, []);556 return <Text text="Passive" />;557 }558 let passive = <PassiveEffect key="p" />;559 ReactNoop.render([<LayoutEffect key="l" />, passive]);560 expect(ReactNoop.flush()).toEqual(['Layout', 'Passive', 'Layout effect']);561 expect(ReactNoop.getChildren()).toEqual([562 span('Layout'),563 span('Passive'),564 ]);565 // Destroying the first child shouldn't prevent the passive effect from566 // being executed567 ReactNoop.render([passive]);568 expect(ReactNoop.flush()).toEqual(['Passive effect']);569 expect(ReactNoop.getChildren()).toEqual([span('Passive')]);570 // (No effects are left to flush.)571 flushPassiveEffects();572 expect(ReactNoop.clearYields()).toEqual(null);573 });574 it('flushes passive effects even if siblings schedule an update', () => {575 function PassiveEffect(props) {576 useEffect(() => {577 ReactNoop.yield('Passive effect');578 });579 return <Text text="Passive" />;580 }581 function LayoutEffect(props) {582 let [count, setCount] = useState(0);583 useLayoutEffect(() => {584 // Scheduling work shouldn't interfere with the queued passive effect585 if (count === 0) {586 setCount(1);587 }588 ReactNoop.yield('Layout effect ' + count);589 });590 return <Text text="Layout" />;591 }592 ReactNoop.render([<PassiveEffect key="p" />, <LayoutEffect key="l" />]);593 expect(ReactNoop.flush()).toEqual([594 'Passive',595 'Layout',596 'Layout effect 0',597 'Passive effect',598 'Layout',599 'Layout effect 1',600 ]);601 expect(ReactNoop.getChildren()).toEqual([602 span('Passive'),603 span('Layout'),604 ]);605 });606 it('flushes passive effects even if siblings schedule a new root', () => {607 function PassiveEffect(props) {608 useEffect(() => {609 ReactNoop.yield('Passive effect');610 }, []);611 return <Text text="Passive" />;612 }613 function LayoutEffect(props) {614 useLayoutEffect(() => {615 ReactNoop.yield('Layout effect');616 // Scheduling work shouldn't interfere with the queued passive effect617 ReactNoop.renderToRootWithID(<Text text="New Root" />, 'root2');618 });619 return <Text text="Layout" />;620 }621 ReactNoop.render([<PassiveEffect key="p" />, <LayoutEffect key="l" />]);622 expect(ReactNoop.flush()).toEqual([623 'Passive',624 'Layout',625 'Layout effect',626 'Passive effect',627 'New Root',628 ]);629 expect(ReactNoop.getChildren()).toEqual([630 span('Passive'),631 span('Layout'),632 ]);633 });634 it(635 'flushes effects serially by flushing old effects before flushing ' +636 "new ones, if they haven't already fired",637 () => {638 function getCommittedText() {639 const children = ReactNoop.getChildren();640 if (children === null) {641 return null;642 }643 return children[0].prop;644 }645 function Counter(props) {646 useEffect(() => {647 ReactNoop.yield(648 `Committed state when effect was fired: ${getCommittedText()}`,649 );650 });651 return <Text text={props.count} />;652 }653 ReactNoop.render(<Counter count={0} />);654 expect(ReactNoop.flush()).toEqual([0]);655 expect(ReactNoop.getChildren()).toEqual([span(0)]);656 // Before the effects have a chance to flush, schedule another update657 ReactNoop.render(<Counter count={1} />);658 expect(ReactNoop.flush()).toEqual([659 // The previous effect flushes before the reconciliation660 'Committed state when effect was fired: 0',661 1,662 ]);663 expect(ReactNoop.getChildren()).toEqual([span(1)]);664 flushPassiveEffects();665 expect(ReactNoop.clearYields()).toEqual([666 'Committed state when effect was fired: 1',667 ]);668 },669 );670 it('updates have async priority', () => {671 function Counter(props) {672 const [count, updateCount] = useState('(empty)');673 useEffect(674 () => {675 ReactNoop.yield(`Schedule update [${props.count}]`);676 updateCount(props.count);677 },678 [props.count],679 );680 return <Text text={'Count: ' + count} />;681 }682 ReactNoop.render(<Counter count={0} />);683 expect(ReactNoop.flush()).toEqual(['Count: (empty)']);684 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);685 flushPassiveEffects();686 expect(ReactNoop.clearYields()).toEqual(['Schedule update [0]']);687 expect(ReactNoop.flush()).toEqual(['Count: 0']);688 ReactNoop.render(<Counter count={1} />);689 expect(ReactNoop.flush()).toEqual(['Count: 0']);690 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);691 flushPassiveEffects();692 expect(ReactNoop.clearYields()).toEqual(['Schedule update [1]']);693 expect(ReactNoop.flush()).toEqual(['Count: 1']);694 });695 it('updates have async priority even if effects are flushed early', () => {696 function Counter(props) {697 const [count, updateCount] = useState('(empty)');698 useEffect(699 () => {700 ReactNoop.yield(`Schedule update [${props.count}]`);701 updateCount(props.count);702 },703 [props.count],704 );705 return <Text text={'Count: ' + count} />;706 }707 ReactNoop.render(<Counter count={0} />);708 expect(ReactNoop.flush()).toEqual(['Count: (empty)']);709 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);710 // Rendering again should flush the previous commit's effects711 ReactNoop.render(<Counter count={1} />);712 ReactNoop.flushThrough(['Schedule update [0]', 'Count: 0']);713 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);714 expect(ReactNoop.flush()).toEqual([]);715 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);716 flushPassiveEffects();717 expect(ReactNoop.flush()).toEqual(['Schedule update [1]', 'Count: 1']);718 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);719 });720 it('flushes serial effects before enqueueing work', () => {721 let _updateCount;722 function Counter(props) {723 const [count, updateCount] = useState(0);724 _updateCount = updateCount;725 useEffect(() => {726 ReactNoop.yield(`Will set count to 1`);727 updateCount(1);728 }, []);729 return <Text text={'Count: ' + count} />;730 }731 ReactNoop.render(<Counter count={0} />);732 expect(ReactNoop.flush()).toEqual(['Count: 0']);733 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);734 // Enqueuing this update forces the passive effect to be flushed --735 // updateCount(1) happens first, so 2 wins.736 _updateCount(2);737 expect(ReactNoop.flush()).toEqual(['Will set count to 1', 'Count: 2']);738 expect(ReactNoop.getChildren()).toEqual([span('Count: 2')]);739 });740 it('flushes serial effects before enqueueing work (with tracing)', () => {741 const onInteractionScheduledWorkCompleted = jest.fn();742 const onWorkCanceled = jest.fn();743 SchedulerTracing.unstable_subscribe({744 onInteractionScheduledWorkCompleted,745 onInteractionTraced: jest.fn(),746 onWorkCanceled,747 onWorkScheduled: jest.fn(),748 onWorkStarted: jest.fn(),749 onWorkStopped: jest.fn(),750 });751 let _updateCount;752 function Counter(props) {753 const [count, updateCount] = useState(0);754 _updateCount = updateCount;755 useEffect(() => {756 expect(SchedulerTracing.unstable_getCurrent()).toMatchInteractions([757 tracingEvent,758 ]);759 ReactNoop.yield(`Will set count to 1`);760 updateCount(1);761 }, []);762 return <Text text={'Count: ' + count} />;763 }764 const tracingEvent = {id: 0, name: 'hello', timestamp: 0};765 SchedulerTracing.unstable_trace(766 tracingEvent.name,767 tracingEvent.timestamp,768 () => {769 ReactNoop.render(<Counter count={0} />);770 },771 );772 expect(ReactNoop.flush()).toEqual(['Count: 0']);773 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);774 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(0);775 // Enqueuing this update forces the passive effect to be flushed --776 // updateCount(1) happens first, so 2 wins.777 _updateCount(2);778 expect(ReactNoop.flush()).toEqual(['Will set count to 1', 'Count: 2']);779 expect(ReactNoop.getChildren()).toEqual([span('Count: 2')]);780 expect(onInteractionScheduledWorkCompleted).toHaveBeenCalledTimes(1);781 expect(onWorkCanceled).toHaveBeenCalledTimes(0);782 });783 it(784 'in sync mode, useEffect is deferred and updates finish synchronously ' +785 '(in a single batch)',786 () => {787 function Counter(props) {788 const [count, updateCount] = useState('(empty)');789 useEffect(790 () => {791 // Update multiple times. These should all be batched together in792 // a single render.793 updateCount(props.count);794 updateCount(props.count);795 updateCount(props.count);796 updateCount(props.count);797 updateCount(props.count);798 updateCount(props.count);799 },800 [props.count],801 );802 return <Text text={'Count: ' + count} />;803 }804 ReactNoop.renderLegacySyncRoot(<Counter count={0} />);805 // Even in sync mode, effects are deferred until after paint806 expect(ReactNoop.flush()).toEqual(['Count: (empty)']);807 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);808 // Now fire the effects809 flushPassiveEffects();810 // There were multiple updates, but there should only be a811 // single render812 expect(ReactNoop.clearYields()).toEqual(['Count: 0']);813 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);814 },815 );816 it('flushSync is not allowed', () => {817 function Counter(props) {818 const [count, updateCount] = useState('(empty)');819 useEffect(820 () => {821 ReactNoop.yield(`Schedule update [${props.count}]`);822 ReactNoop.flushSync(() => {823 updateCount(props.count);824 });825 },826 [props.count],827 );828 return <Text text={'Count: ' + count} />;829 }830 ReactNoop.render(<Counter count={0} />);831 expect(ReactNoop.flush()).toEqual(['Count: (empty)']);832 expect(ReactNoop.getChildren()).toEqual([span('Count: (empty)')]);833 expect(() => {834 flushPassiveEffects();835 }).toThrow('flushSync was called from inside a lifecycle method');836 });837 it('unmounts previous effect', () => {838 function Counter(props) {839 useEffect(() => {840 ReactNoop.yield(`Did create [${props.count}]`);841 return () => {842 ReactNoop.yield(`Did destroy [${props.count}]`);843 };844 });845 return <Text text={'Count: ' + props.count} />;846 }847 ReactNoop.render(<Counter count={0} />);848 expect(ReactNoop.flush()).toEqual(['Count: 0']);849 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);850 flushPassiveEffects();851 expect(ReactNoop.clearYields()).toEqual(['Did create [0]']);852 ReactNoop.render(<Counter count={1} />);853 expect(ReactNoop.flush()).toEqual(['Count: 1']);854 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);855 flushPassiveEffects();856 expect(ReactNoop.clearYields()).toEqual([857 'Did destroy [0]',858 'Did create [1]',859 ]);860 });861 it('unmounts on deletion', () => {862 function Counter(props) {863 useEffect(() => {864 ReactNoop.yield(`Did create [${props.count}]`);865 return () => {866 ReactNoop.yield(`Did destroy [${props.count}]`);867 };868 });869 return <Text text={'Count: ' + props.count} />;870 }871 ReactNoop.render(<Counter count={0} />);872 expect(ReactNoop.flush()).toEqual(['Count: 0']);873 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);874 flushPassiveEffects();875 expect(ReactNoop.clearYields()).toEqual(['Did create [0]']);876 ReactNoop.render(null);877 expect(ReactNoop.flush()).toEqual(['Did destroy [0]']);878 expect(ReactNoop.getChildren()).toEqual([]);879 });880 it('unmounts on deletion after skipped effect', () => {881 function Counter(props) {882 useEffect(() => {883 ReactNoop.yield(`Did create [${props.count}]`);884 return () => {885 ReactNoop.yield(`Did destroy [${props.count}]`);886 };887 }, []);888 return <Text text={'Count: ' + props.count} />;889 }890 ReactNoop.render(<Counter count={0} />);891 expect(ReactNoop.flush()).toEqual(['Count: 0']);892 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);893 flushPassiveEffects();894 expect(ReactNoop.clearYields()).toEqual(['Did create [0]']);895 ReactNoop.render(<Counter count={1} />);896 expect(ReactNoop.flush()).toEqual(['Count: 1']);897 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);898 flushPassiveEffects();899 expect(ReactNoop.clearYields()).toEqual(null);900 ReactNoop.render(null);901 expect(ReactNoop.flush()).toEqual(['Did destroy [0]']);902 expect(ReactNoop.getChildren()).toEqual([]);903 });904 it('skips effect if constructor has not changed', () => {905 function effect() {906 ReactNoop.yield(`Did mount`);907 return () => {908 ReactNoop.yield(`Did unmount`);909 };910 }911 function Counter(props) {912 useEffect(effect);913 return <Text text={'Count: ' + props.count} />;914 }915 ReactNoop.render(<Counter count={0} />);916 expect(ReactNoop.flush()).toEqual(['Count: 0']);917 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);918 flushPassiveEffects();919 expect(ReactNoop.clearYields()).toEqual(['Did mount']);920 ReactNoop.render(<Counter count={1} />);921 // No effect, because constructor was hoisted outside render922 expect(ReactNoop.flush()).toEqual(['Count: 1']);923 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);924 ReactNoop.render(null);925 expect(ReactNoop.flush()).toEqual(['Did unmount']);926 expect(ReactNoop.getChildren()).toEqual([]);927 });928 it('skips effect if inputs have not changed', () => {929 function Counter(props) {930 const text = `${props.label}: ${props.count}`;931 useEffect(932 () => {933 ReactNoop.yield(`Did create [${text}]`);934 return () => {935 ReactNoop.yield(`Did destroy [${text}]`);936 };937 },938 [props.label, props.count],939 );940 return <Text text={text} />;941 }942 ReactNoop.render(<Counter label="Count" count={0} />);943 expect(ReactNoop.flush()).toEqual(['Count: 0']);944 flushPassiveEffects();945 expect(ReactNoop.clearYields()).toEqual(['Did create [Count: 0]']);946 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);947 ReactNoop.render(<Counter label="Count" count={1} />);948 // Count changed949 expect(ReactNoop.flush()).toEqual(['Count: 1']);950 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);951 flushPassiveEffects();952 expect(ReactNoop.clearYields()).toEqual([953 'Did destroy [Count: 0]',954 'Did create [Count: 1]',955 ]);956 ReactNoop.render(<Counter label="Count" count={1} />);957 // Nothing changed, so no effect should have fired958 expect(ReactNoop.flush()).toEqual(['Count: 1']);959 flushPassiveEffects();960 expect(ReactNoop.clearYields()).toEqual(null);961 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);962 ReactNoop.render(<Counter label="Total" count={1} />);963 // Label changed964 expect(ReactNoop.flush()).toEqual(['Total: 1']);965 expect(ReactNoop.getChildren()).toEqual([span('Total: 1')]);966 flushPassiveEffects();967 expect(ReactNoop.clearYields()).toEqual([968 'Did destroy [Count: 1]',969 'Did create [Total: 1]',970 ]);971 });972 it('multiple effects', () => {973 function Counter(props) {974 useEffect(() => {975 ReactNoop.yield(`Did commit 1 [${props.count}]`);976 });977 useEffect(() => {978 ReactNoop.yield(`Did commit 2 [${props.count}]`);979 });980 return <Text text={'Count: ' + props.count} />;981 }982 ReactNoop.render(<Counter count={0} />);983 expect(ReactNoop.flush()).toEqual(['Count: 0']);984 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);985 flushPassiveEffects();986 expect(ReactNoop.clearYields()).toEqual([987 'Did commit 1 [0]',988 'Did commit 2 [0]',989 ]);990 ReactNoop.render(<Counter count={1} />);991 expect(ReactNoop.flush()).toEqual(['Count: 1']);992 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);993 flushPassiveEffects();994 expect(ReactNoop.clearYields()).toEqual([995 'Did commit 1 [1]',996 'Did commit 2 [1]',997 ]);998 });999 it('unmounts all previous effects before creating any new ones', () => {1000 function Counter(props) {1001 useEffect(() => {1002 ReactNoop.yield(`Mount A [${props.count}]`);1003 return () => {1004 ReactNoop.yield(`Unmount A [${props.count}]`);1005 };1006 });1007 useEffect(() => {1008 ReactNoop.yield(`Mount B [${props.count}]`);1009 return () => {1010 ReactNoop.yield(`Unmount B [${props.count}]`);1011 };1012 });1013 return <Text text={'Count: ' + props.count} />;1014 }1015 ReactNoop.render(<Counter count={0} />);1016 expect(ReactNoop.flush()).toEqual(['Count: 0']);1017 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);1018 flushPassiveEffects();1019 expect(ReactNoop.clearYields()).toEqual(['Mount A [0]', 'Mount B [0]']);1020 ReactNoop.render(<Counter count={1} />);1021 expect(ReactNoop.flush()).toEqual(['Count: 1']);1022 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);1023 flushPassiveEffects();1024 expect(ReactNoop.clearYields()).toEqual([1025 'Unmount A [0]',1026 'Unmount B [0]',1027 'Mount A [1]',1028 'Mount B [1]',1029 ]);1030 });1031 it('handles errors on mount', () => {1032 function Counter(props) {1033 useEffect(() => {1034 ReactNoop.yield(`Mount A [${props.count}]`);1035 return () => {1036 ReactNoop.yield(`Unmount A [${props.count}]`);1037 };1038 });1039 useEffect(() => {1040 ReactNoop.yield('Oops!');1041 throw new Error('Oops!');1042 // eslint-disable-next-line no-unreachable1043 ReactNoop.yield(`Mount B [${props.count}]`);1044 return () => {1045 ReactNoop.yield(`Unmount B [${props.count}]`);1046 };1047 });1048 return <Text text={'Count: ' + props.count} />;1049 }1050 ReactNoop.render(<Counter count={0} />);1051 expect(ReactNoop.flush()).toEqual(['Count: 0']);1052 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);1053 expect(() => flushPassiveEffects()).toThrow('Oops');1054 expect(ReactNoop.clearYields()).toEqual([1055 'Mount A [0]',1056 'Oops!',1057 // Clean up effect A. There's no effect B to clean-up, because it1058 // never mounted.1059 'Unmount A [0]',1060 ]);1061 expect(ReactNoop.getChildren()).toEqual([]);1062 });1063 it('handles errors on update', () => {1064 function Counter(props) {1065 useEffect(() => {1066 ReactNoop.yield(`Mount A [${props.count}]`);1067 return () => {1068 ReactNoop.yield(`Unmount A [${props.count}]`);1069 };1070 });1071 useEffect(() => {1072 if (props.count === 1) {1073 ReactNoop.yield('Oops!');1074 throw new Error('Oops!');1075 }1076 ReactNoop.yield(`Mount B [${props.count}]`);1077 return () => {1078 ReactNoop.yield(`Unmount B [${props.count}]`);1079 };1080 });1081 return <Text text={'Count: ' + props.count} />;1082 }1083 ReactNoop.render(<Counter count={0} />);1084 expect(ReactNoop.flush()).toEqual(['Count: 0']);1085 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);1086 flushPassiveEffects();1087 expect(ReactNoop.clearYields()).toEqual(['Mount A [0]', 'Mount B [0]']);1088 // This update will trigger an errror1089 ReactNoop.render(<Counter count={1} />);1090 expect(ReactNoop.flush()).toEqual(['Count: 1']);1091 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);1092 expect(() => flushPassiveEffects()).toThrow('Oops');1093 expect(ReactNoop.clearYields()).toEqual([1094 'Unmount A [0]',1095 'Unmount B [0]',1096 'Mount A [1]',1097 'Oops!',1098 // Clean up effect A. There's no effect B to clean-up, because it1099 // never mounted.1100 'Unmount A [1]',1101 ]);1102 expect(ReactNoop.getChildren()).toEqual([]);1103 });1104 it('handles errors on unmount', () => {1105 function Counter(props) {1106 useEffect(() => {1107 ReactNoop.yield(`Mount A [${props.count}]`);1108 return () => {1109 ReactNoop.yield('Oops!');1110 throw new Error('Oops!');1111 // eslint-disable-next-line no-unreachable1112 ReactNoop.yield(`Unmount A [${props.count}]`);1113 };1114 });1115 useEffect(() => {1116 ReactNoop.yield(`Mount B [${props.count}]`);1117 return () => {1118 ReactNoop.yield(`Unmount B [${props.count}]`);1119 };1120 });1121 return <Text text={'Count: ' + props.count} />;1122 }1123 ReactNoop.render(<Counter count={0} />);1124 expect(ReactNoop.flush()).toEqual(['Count: 0']);1125 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);1126 flushPassiveEffects();1127 expect(ReactNoop.clearYields()).toEqual(['Mount A [0]', 'Mount B [0]']);1128 // This update will trigger an errror1129 ReactNoop.render(<Counter count={1} />);1130 expect(ReactNoop.flush()).toEqual(['Count: 1']);1131 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);1132 expect(() => flushPassiveEffects()).toThrow('Oops');1133 expect(ReactNoop.clearYields()).toEqual([1134 'Oops!',1135 // B unmounts even though an error was thrown in the previous effect1136 'Unmount B [0]',1137 ]);1138 expect(ReactNoop.getChildren()).toEqual([]);1139 });1140 it('works with memo', () => {1141 function Counter({count}) {1142 useLayoutEffect(() => {1143 ReactNoop.yield('Mount: ' + count);1144 return () => ReactNoop.yield('Unmount: ' + count);1145 });1146 return <Text text={'Count: ' + count} />;1147 }1148 Counter = memo(Counter);1149 ReactNoop.render(<Counter count={0} />);1150 expect(ReactNoop.flush()).toEqual(['Count: 0', 'Mount: 0']);1151 expect(ReactNoop.getChildren()).toEqual([span('Count: 0')]);1152 ReactNoop.render(<Counter count={1} />);1153 expect(ReactNoop.flush()).toEqual(['Count: 1', 'Unmount: 0', 'Mount: 1']);1154 expect(ReactNoop.getChildren()).toEqual([span('Count: 1')]);1155 ReactNoop.render(null);1156 expect(ReactNoop.flush()).toEqual(['Unmount: 1']);1157 expect(ReactNoop.getChildren()).toEqual([]);1158 });1159 });1160 describe('useLayoutEffect', () => {1161 it('fires layout effects after the host has been mutated', () => {1162 function getCommittedText() {1163 const children = ReactNoop.getChildren();1164 if (children === null) {1165 return null;1166 }1167 return children[0].prop;1168 }1169 function Counter(props) {1170 useLayoutEffect(() => {1171 ReactNoop.yield(`Current: ${getCommittedText()}`);1172 });1173 return <Text text={props.count} />;1174 }1175 ReactNoop.render(<Counter count={0} />);1176 expect(ReactNoop.flush()).toEqual([0, 'Current: 0']);1177 expect(ReactNoop.getChildren()).toEqual([span(0)]);1178 ReactNoop.render(<Counter count={1} />);1179 expect(ReactNoop.flush()).toEqual([1, 'Current: 1']);1180 expect(ReactNoop.getChildren()).toEqual([span(1)]);1181 });1182 it('force flushes passive effects before firing new layout effects', () => {1183 let committedText = '(empty)';1184 function Counter(props) {1185 useLayoutEffect(() => {1186 // Normally this would go in a mutation effect, but this test1187 // intentionally omits a mutation effect.1188 committedText = props.count + '';1189 ReactNoop.yield(`Mount layout [current: ${committedText}]`);1190 return () => {1191 ReactNoop.yield(`Unmount layout [current: ${committedText}]`);1192 };1193 });1194 useEffect(() => {1195 ReactNoop.yield(`Mount normal [current: ${committedText}]`);1196 return () => {1197 ReactNoop.yield(`Unmount normal [current: ${committedText}]`);1198 };1199 });1200 return null;1201 }1202 ReactNoop.render(<Counter count={0} />);1203 expect(ReactNoop.flush()).toEqual(['Mount layout [current: 0]']);1204 expect(committedText).toEqual('0');1205 ReactNoop.render(<Counter count={1} />);1206 expect(ReactNoop.flush()).toEqual([1207 'Mount normal [current: 0]',1208 'Unmount layout [current: 0]',1209 'Mount layout [current: 1]',1210 ]);1211 expect(committedText).toEqual('1');1212 flushPassiveEffects();1213 expect(ReactNoop.clearYields()).toEqual([1214 'Unmount normal [current: 1]',1215 'Mount normal [current: 1]',1216 ]);1217 });1218 });1219 describe('useCallback', () => {1220 it('memoizes callback by comparing inputs', () => {1221 class IncrementButton extends React.PureComponent {1222 increment = () => {1223 this.props.increment();1224 };1225 render() {1226 return <Text text="Increment" />;1227 }1228 }1229 function Counter({incrementBy}) {1230 const [count, updateCount] = useState(0);1231 const increment = useCallback(() => updateCount(c => c + incrementBy), [1232 incrementBy,1233 ]);1234 return (1235 <React.Fragment>1236 <IncrementButton increment={increment} ref={button} />1237 <Text text={'Count: ' + count} />1238 </React.Fragment>1239 );1240 }1241 const button = React.createRef(null);1242 ReactNoop.render(<Counter incrementBy={1} />);1243 expect(ReactNoop.flush()).toEqual(['Increment', 'Count: 0']);1244 expect(ReactNoop.getChildren()).toEqual([1245 span('Increment'),1246 span('Count: 0'),1247 ]);1248 button.current.increment();1249 expect(ReactNoop.flush()).toEqual([1250 // Button should not re-render, because its props haven't changed1251 // 'Increment',1252 'Count: 1',1253 ]);1254 expect(ReactNoop.getChildren()).toEqual([1255 span('Increment'),1256 span('Count: 1'),1257 ]);1258 // Increase the increment amount1259 ReactNoop.render(<Counter incrementBy={10} />);1260 expect(ReactNoop.flush()).toEqual([1261 // Inputs did change this time1262 'Increment',1263 'Count: 1',1264 ]);1265 expect(ReactNoop.getChildren()).toEqual([1266 span('Increment'),1267 span('Count: 1'),1268 ]);1269 // Callback should have updated1270 button.current.increment();1271 expect(ReactNoop.flush()).toEqual(['Count: 11']);1272 expect(ReactNoop.getChildren()).toEqual([1273 span('Increment'),1274 span('Count: 11'),1275 ]);1276 });1277 });1278 describe('useMemo', () => {1279 it('memoizes value by comparing to previous inputs', () => {1280 function CapitalizedText(props) {1281 const text = props.text;1282 const capitalizedText = useMemo(1283 () => {1284 ReactNoop.yield(`Capitalize '${text}'`);1285 return text.toUpperCase();1286 },1287 [text],1288 );1289 return <Text text={capitalizedText} />;1290 }1291 ReactNoop.render(<CapitalizedText text="hello" />);1292 expect(ReactNoop.flush()).toEqual(["Capitalize 'hello'", 'HELLO']);1293 expect(ReactNoop.getChildren()).toEqual([span('HELLO')]);1294 ReactNoop.render(<CapitalizedText text="hi" />);1295 expect(ReactNoop.flush()).toEqual(["Capitalize 'hi'", 'HI']);1296 expect(ReactNoop.getChildren()).toEqual([span('HI')]);1297 ReactNoop.render(<CapitalizedText text="hi" />);1298 expect(ReactNoop.flush()).toEqual(['HI']);1299 expect(ReactNoop.getChildren()).toEqual([span('HI')]);1300 ReactNoop.render(<CapitalizedText text="goodbye" />);1301 expect(ReactNoop.flush()).toEqual(["Capitalize 'goodbye'", 'GOODBYE']);1302 expect(ReactNoop.getChildren()).toEqual([span('GOODBYE')]);1303 });1304 it('compares function if no inputs are provided', () => {1305 function LazyCompute(props) {1306 const computed = useMemo(props.compute);1307 return <Text text={computed} />;1308 }1309 function computeA() {1310 ReactNoop.yield('compute A');1311 return 'A';1312 }1313 function computeB() {1314 ReactNoop.yield('compute B');1315 return 'B';1316 }1317 ReactNoop.render(<LazyCompute compute={computeA} />);1318 expect(ReactNoop.flush()).toEqual(['compute A', 'A']);1319 ReactNoop.render(<LazyCompute compute={computeA} />);1320 expect(ReactNoop.flush()).toEqual(['A']);1321 ReactNoop.render(<LazyCompute compute={computeA} />);1322 expect(ReactNoop.flush()).toEqual(['A']);1323 ReactNoop.render(<LazyCompute compute={computeB} />);1324 expect(ReactNoop.flush()).toEqual(['compute B', 'B']);1325 });1326 it('should not invoke memoized function during re-renders unless inputs change', () => {1327 function LazyCompute(props) {1328 const computed = useMemo(() => props.compute(props.input), [1329 props.input,1330 ]);1331 const [count, setCount] = useState(0);1332 if (count < 3) {1333 setCount(count + 1);1334 }1335 return <Text text={computed} />;1336 }1337 function compute(val) {1338 ReactNoop.yield('compute ' + val);1339 return val;1340 }1341 ReactNoop.render(<LazyCompute compute={compute} input="A" />);1342 expect(ReactNoop.flush()).toEqual(['compute A', 'A']);1343 ReactNoop.render(<LazyCompute compute={compute} input="A" />);1344 expect(ReactNoop.flush()).toEqual(['A']);1345 ReactNoop.render(<LazyCompute compute={compute} input="B" />);1346 expect(ReactNoop.flush()).toEqual(['compute B', 'B']);1347 });1348 });1349 describe('useRef', () => {1350 it('creates a ref object initialized with the provided value', () => {1351 jest.useFakeTimers();1352 function useDebouncedCallback(callback, ms, inputs) {1353 const timeoutID = useRef(-1);1354 useEffect(() => {1355 return function unmount() {1356 clearTimeout(timeoutID.current);1357 };1358 }, []);1359 const debouncedCallback = useCallback(1360 (...args) => {1361 clearTimeout(timeoutID.current);1362 timeoutID.current = setTimeout(callback, ms, ...args);1363 },1364 [callback, ms],1365 );1366 return useCallback(debouncedCallback, inputs);1367 }1368 let ping;1369 function App() {1370 ping = useDebouncedCallback(1371 value => {1372 ReactNoop.yield('ping: ' + value);1373 },1374 100,1375 [],1376 );1377 return null;1378 }1379 ReactNoop.render(<App />);1380 expect(ReactNoop.flush()).toEqual([]);1381 ping(1);1382 ping(2);1383 ping(3);1384 expect(ReactNoop.flush()).toEqual([]);1385 jest.advanceTimersByTime(100);1386 expect(ReactNoop.flush()).toEqual(['ping: 3']);1387 ping(4);1388 jest.advanceTimersByTime(20);1389 ping(5);1390 ping(6);1391 jest.advanceTimersByTime(80);1392 expect(ReactNoop.flush()).toEqual([]);1393 jest.advanceTimersByTime(20);1394 expect(ReactNoop.flush()).toEqual(['ping: 6']);1395 });1396 it('should return the same ref during re-renders', () => {1397 function Counter() {1398 const ref = useRef('val');1399 const [count, setCount] = useState(0);1400 const [firstRef] = useState(ref);1401 if (firstRef !== ref) {1402 throw new Error('should never change');1403 }1404 if (count < 3) {1405 setCount(count + 1);1406 }1407 return <Text text={ref.current} />;1408 }1409 ReactNoop.render(<Counter />);1410 expect(ReactNoop.flush()).toEqual(['val']);1411 ReactNoop.render(<Counter />);1412 expect(ReactNoop.flush()).toEqual(['val']);1413 });1414 });1415 describe('progressive enhancement', () => {1416 it('mount additional state', () => {1417 let updateA;1418 let updateB;1419 let updateC;1420 function App(props) {1421 const [A, _updateA] = useState(0);1422 const [B, _updateB] = useState(0);1423 updateA = _updateA;1424 updateB = _updateB;1425 let C;1426 if (props.loadC) {1427 const [_C, _updateC] = useState(0);1428 C = _C;1429 updateC = _updateC;1430 } else {1431 C = '[not loaded]';1432 }1433 return <Text text={`A: ${A}, B: ${B}, C: ${C}`} />;1434 }1435 ReactNoop.render(<App loadC={false} />);1436 expect(ReactNoop.flush()).toEqual(['A: 0, B: 0, C: [not loaded]']);1437 expect(ReactNoop.getChildren()).toEqual([1438 span('A: 0, B: 0, C: [not loaded]'),1439 ]);1440 updateA(2);1441 updateB(3);1442 expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: [not loaded]']);1443 expect(ReactNoop.getChildren()).toEqual([1444 span('A: 2, B: 3, C: [not loaded]'),1445 ]);1446 ReactNoop.render(<App loadC={true} />);1447 expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: 0']);1448 expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 0')]);1449 updateC(4);1450 expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: 4']);1451 expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 4')]);1452 });1453 it('unmount state', () => {1454 let updateA;1455 let updateB;1456 let updateC;1457 function App(props) {1458 const [A, _updateA] = useState(0);1459 const [B, _updateB] = useState(0);1460 updateA = _updateA;1461 updateB = _updateB;1462 let C;1463 if (props.loadC) {1464 const [_C, _updateC] = useState(0);1465 C = _C;1466 updateC = _updateC;1467 } else {1468 C = '[not loaded]';1469 }1470 return <Text text={`A: ${A}, B: ${B}, C: ${C}`} />;1471 }1472 ReactNoop.render(<App loadC={true} />);1473 expect(ReactNoop.flush()).toEqual(['A: 0, B: 0, C: 0']);1474 expect(ReactNoop.getChildren()).toEqual([span('A: 0, B: 0, C: 0')]);1475 updateA(2);1476 updateB(3);1477 updateC(4);1478 expect(ReactNoop.flush()).toEqual(['A: 2, B: 3, C: 4']);1479 expect(ReactNoop.getChildren()).toEqual([span('A: 2, B: 3, C: 4')]);1480 ReactNoop.render(<App loadC={false} />);1481 expect(() => ReactNoop.flush()).toThrow(1482 'Rendered fewer hooks than expected. This may be caused by an ' +1483 'accidental early return statement.',1484 );1485 });1486 it('unmount effects', () => {1487 function App(props) {1488 useEffect(() => {1489 ReactNoop.yield('Mount A');1490 return () => {1491 ReactNoop.yield('Unmount A');1492 };1493 }, []);1494 if (props.showMore) {1495 useEffect(() => {1496 ReactNoop.yield('Mount B');1497 return () => {1498 ReactNoop.yield('Unmount B');1499 };1500 }, []);1501 }1502 return null;1503 }1504 ReactNoop.render(<App showMore={false} />);1505 expect(ReactNoop.flush()).toEqual([]);1506 flushPassiveEffects();1507 expect(ReactNoop.clearYields()).toEqual(['Mount A']);1508 ReactNoop.render(<App showMore={true} />);1509 expect(ReactNoop.flush()).toEqual([]);1510 flushPassiveEffects();1511 expect(ReactNoop.clearYields()).toEqual(['Mount B']);1512 ReactNoop.render(<App showMore={false} />);1513 expect(() => ReactNoop.flush()).toThrow(1514 'Rendered fewer hooks than expected. This may be caused by an ' +1515 'accidental early return statement.',1516 );1517 });1518 });...

Full Screen

Full Screen

ReactFiberClassComponent.js

Source:ReactFiberClassComponent.js Github

copy

Full Screen

...17 }18 update.callback = callback;19 }20 if (revertPassiveEffectsChange) {21 flushPassiveEffects();22 }23 enqueueUpdate(fiber, update);24 scheduleWork(fiber, expirationTime);25 },26 enqueueReplaceState(inst, payload, callback) {27 const fiber = getInstance(inst);28 const currentTime = requestCurrentTime();29 const suspenseConfig = requestCurrentSuspenseConfig();30 const expirationTime = computeExpirationForFiber(31 currentTime,32 fiber,33 suspenseConfig,34 );35 const update = createUpdate(expirationTime, suspenseConfig);36 update.tag = ReplaceState;37 update.payload = payload;38 if (callback !== undefined && callback !== null) {39 if (__DEV__) {40 warnOnInvalidCallback(callback, 'replaceState');41 }42 update.callback = callback;43 }44 if (revertPassiveEffectsChange) {45 flushPassiveEffects();46 }47 enqueueUpdate(fiber, update);48 scheduleWork(fiber, expirationTime);49 },50 enqueueForceUpdate(inst, callback) {51 const fiber = getInstance(inst);52 const currentTime = requestCurrentTime();53 const suspenseConfig = requestCurrentSuspenseConfig();54 const expirationTime = computeExpirationForFiber(55 currentTime,56 fiber,57 suspenseConfig,58 );59 const update = createUpdate(expirationTime, suspenseConfig);60 update.tag = ForceUpdate;61 if (callback !== undefined && callback !== null) {62 if (__DEV__) {63 warnOnInvalidCallback(callback, 'forceUpdate');64 }65 update.callback = callback;66 }67 if (revertPassiveEffectsChange) {68 flushPassiveEffects();69 }70 enqueueUpdate(fiber, update);71 scheduleWork(fiber, expirationTime);72 },73};74function mapSingleChildIntoContext(bookKeeping, child, childKey) {75 const { result, keyPrefix, func, context } = bookKeeping;76 let mappedChild = func.call(context, child, bookKeeping.count++);77 if (Array.isArray(mappedChild)) {78 mapIntoWithKeyPrefixInternal(mappedChild, result, childKey, c => c);79 } else if (mappedChild != null) {80 if (isValidElement(mappedChild)) {81 mappedChild = cloneAndReplaceKey(82 mappedChild,...

Full Screen

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.click('text=Get Started');7 await page.waitForSelector('text=Quick Start');8 await page.flushPassiveEffects();9 await page.screenshot({ path: 'example.png' });10 await browser.close();11})();

Full Screen

Using AI Code Generation

copy

Full Screen

1const playwright = require('playwright');2(async () => {3 const browser = await playwright['chromium'].launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.evaluate(() => {7 document.querySelector('input[name="q"]').value = 'Playwright';8 document.querySelector('input[name="q"]').dispatchEvent(new Event('input', { bubbles: true }));9 document.querySelector('input[name="q"]').dispatchEvent(new Event('keydown', { bubbles: true }));10 });11 await page.flushPassiveEffects();12 await page.screenshot({ path: 'page.png' });13 await browser.close();14})();15flushPassiveEffects()16flushMicrotasks()17flushPromises()18frame()19goBack(options?: NavigationOptions): Promise<Response | null>20goForward(options?: NavigationOptions): Promise<Response | null>21goto(url: string, options?: NavigationOptions): Promise<Response>22isClosed()23isJavaScriptEnabled()24keyboard()25mainFrame()26mouse()27off(event: string

Full Screen

Using AI Code Generation

copy

Full Screen

1const { chromium } = require('playwright');2(async () => {3 const browser = await chromium.launch();4 const context = await browser.newContext();5 const page = await context.newPage();6 await page.waitForLoadState('networkidle');7 await page.evaluate(() => {8 window.flushPassiveEffects();9 });10 await browser.close();11})();12 at CDPSession.send (C:\Users\user\playwright\node_modules\playwright\lib\cdp\cdpSession.js:111:25)13 at ExecutionContext._evaluateInternal (C:\Users\user\playwright\node_modules\playwright\lib\client\executionContext.js:91:51)14 at ExecutionContext.evaluate (C:\Users\user\playwright\node_modules\playwright\lib\client\executionContext.js:38:17)15 at processTicksAndRejections (internal/process/task_queues.js:97:5)

Full Screen

Using AI Code Generation

copy

Full Screen

1const playwright = require('playwright');2(async () => {3 const browser = await playwright.chromium.launch();4 const page = await browser.newPage();5 await page.evaluate(() => {6 window.flushPassiveEffects();7 });8 await browser.close();9})();

Full Screen

Using AI Code Generation

copy

Full Screen

1const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');2const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');3const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');4const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');5const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');6const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');7const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');8const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');9const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');10const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');11const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');12const { flushPassiveEffects } = require('playwright/lib/server/supplements/recorder/recorderSupplement.js');13const { flushPassiveEffects } = require('playwright/lib/server/supplements

Full Screen

Using AI Code Generation

copy

Full Screen

1const { flushPassiveEffects } = require('@playwright/test/lib/server/frames');2const { chromium, webkit } = require('playwright');3async function main() {4 const browser = await chromium.launch({ headless: false });5 const context = await browser.newContext();6 const page = await context.newPage();7 await flushPassiveEffects(page);8 await page.screenshot({ path: 'example.png' });9 await browser.close();10}11main();12const { test } = require('@playwright/test');13test('should wait for passive effects to flush', async ({ page }) => {14 await page.waitForPassiveEffects();15 await page.screenshot({ path: 'example.png' });16});

Full Screen

Playwright tutorial

LambdaTest’s Playwright tutorial will give you a broader idea about the Playwright automation framework, its unique features, and use cases with examples to exceed your understanding of Playwright testing. This tutorial will give A to Z guidance, from installing the Playwright framework to some best practices and advanced concepts.

Chapters:

  1. What is Playwright : Playwright is comparatively new but has gained good popularity. Get to know some history of the Playwright with some interesting facts connected with it.
  2. How To Install Playwright : Learn in detail about what basic configuration and dependencies are required for installing Playwright and run a test. Get a step-by-step direction for installing the Playwright automation framework.
  3. Playwright Futuristic Features: Launched in 2020, Playwright gained huge popularity quickly because of some obliging features such as Playwright Test Generator and Inspector, Playwright Reporter, Playwright auto-waiting mechanism and etc. Read up on those features to master Playwright testing.
  4. What is Component Testing: Component testing in Playwright is a unique feature that allows a tester to test a single component of a web application without integrating them with other elements. Learn how to perform Component testing on the Playwright automation framework.
  5. Inputs And Buttons In Playwright: Every website has Input boxes and buttons; learn about testing inputs and buttons with different scenarios and examples.
  6. Functions and Selectors in Playwright: Learn how to launch the Chromium browser with Playwright. Also, gain a better understanding of some important functions like “BrowserContext,” which allows you to run multiple browser sessions, and “newPage” which interacts with a page.
  7. Handling Alerts and Dropdowns in Playwright : Playwright interact with different types of alerts and pop-ups, such as simple, confirmation, and prompt, and different types of dropdowns, such as single selector and multi-selector get your hands-on with handling alerts and dropdown in Playright testing.
  8. Playwright vs Puppeteer: Get to know about the difference between two testing frameworks and how they are different than one another, which browsers they support, and what features they provide.
  9. Run Playwright Tests on LambdaTest: Playwright testing with LambdaTest leverages test performance to the utmost. You can run multiple Playwright tests in Parallel with the LammbdaTest test cloud. Get a step-by-step guide to run your Playwright test on the LambdaTest platform.
  10. Playwright Python Tutorial: Playwright automation framework support all major languages such as Python, JavaScript, TypeScript, .NET and etc. However, there are various advantages to Python end-to-end testing with Playwright because of its versatile utility. Get the hang of Playwright python testing with this chapter.
  11. Playwright End To End Testing Tutorial: Get your hands on with Playwright end-to-end testing and learn to use some exciting features such as TraceViewer, Debugging, Networking, Component testing, Visual testing, and many more.
  12. Playwright Video Tutorial: Watch the video tutorials on Playwright testing from experts and get a consecutive in-depth explanation of Playwright automation testing.

Run Playwright Internal automation tests on LambdaTest cloud grid

Perform automation testing on 3000+ real desktop and mobile devices online.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful