Files
react-native/Libraries/Utilities/__tests__/useRefEffect-test.js
T
Mike Vitousek 2c7e89d17c Fix haste module reference
Summary:
Fix a haste module reference inserted by D41226960 (https://github.com/facebook/react-native/commit/ed02d4baf03836b03158deb05dd899bcbbe6d281)

Changelog: [internal]

Reviewed By: SamChou19815

Differential Revision: D41231733

fbshipit-source-id: e3bcdc220946b7dd1783aa321c024972b3bc8e17
2022-11-11 14:14:21 -08:00

267 lines
6.4 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
* @format
* @oncall react_native
*/
import type {
HostComponent,
MeasureInWindowOnSuccessCallback,
MeasureLayoutOnSuccessCallback,
MeasureOnSuccessCallback,
} from '../../Renderer/shims/ReactNativeTypes.js';
import View from '../../Components/View/View';
import useRefEffect from '../useRefEffect';
import * as React from 'react';
import {act, create} from 'react-test-renderer';
/**
* TestView provide a component execution environment to test hooks.
*/
function TestView({
childKey = null,
effect,
}: {
childKey: ?string,
effect: () => (() => void) | void,
}) {
const ref = useRefEffect<{
blur(): void,
focus(): void,
measure(callback: MeasureOnSuccessCallback): void,
measureInWindow(callback: MeasureInWindowOnSuccessCallback): void,
measureLayout(
relativeToNativeNode: number | React.ElementRef<HostComponent<mixed>>,
onSuccess: MeasureLayoutOnSuccessCallback,
onFail?: () => void,
): void,
setNativeProps(nativeProps: {...}): void,
}>(effect);
return <View key={childKey} ref={ref} testID={childKey} />;
}
/**
* TestEffect represents an effect invocation.
*/
class TestEffect {
name: string;
key: ?string;
constructor(name: string, key: ?string) {
this.name = name;
this.key = key;
}
static called(name: string, key: ?string): $FlowFixMe {
// $FlowIssue[prop-missing] - Flow does not support type augmentation.
return expect.effect(name, key);
}
}
/**
* TestEffectCleanup represents an effect cleanup invocation.
*/
class TestEffectCleanup {
name: string;
key: ?string;
constructor(name: string, key: ?string) {
this.name = name;
this.key = key;
}
static called(name: string, key: ?string): $FlowFixMe {
// $FlowIssue[prop-missing] - Flow does not support type augmentation.
return expect.effectCleanup(name, key);
}
}
/**
* extend.effect and expect.extendCleanup make it easier to assert expected
* values. But use TestEffect.called and TestEffectCleanup.called instead of
* extend.effect and expect.extendCleanup because of Flow.
*/
expect.extend({
effect(received, name, key) {
const pass =
received instanceof TestEffect &&
received.name === name &&
received.key === key;
return {pass};
},
effectCleanup(received, name, key) {
const pass =
received instanceof TestEffectCleanup &&
received.name === name &&
received.key === key;
return {pass};
},
});
function mockEffectRegistry(): {
mockEffect: string => () => () => void,
mockEffectWithoutCleanup: string => () => void,
registry: $ReadOnlyArray<TestEffect | TestEffectCleanup>,
} {
const registry: Array<TestEffect | TestEffectCleanup> = [];
return {
mockEffect(name: string): () => () => void {
return instance => {
const key = instance?.props?.testID;
registry.push(new TestEffect(name, key));
return () => {
registry.push(new TestEffectCleanup(name, key));
};
};
},
mockEffectWithoutCleanup(name: string): () => void {
return instance => {
const key = instance?.props?.testID;
registry.push(new TestEffect(name, key));
};
},
registry,
};
}
test('calls effect without cleanup', () => {
let root;
const {mockEffectWithoutCleanup, registry} = mockEffectRegistry();
const effectA = mockEffectWithoutCleanup('A');
act(() => {
root = create(<TestView childKey="foo" effect={effectA} />);
});
expect(registry).toEqual([TestEffect.called('A', 'foo')]);
act(() => {
root.unmount();
});
expect(registry).toEqual([TestEffect.called('A', 'foo')]);
});
test('calls effect and cleanup', () => {
let root;
const {mockEffect, registry} = mockEffectRegistry();
const effectA = mockEffect('A');
act(() => {
root = create(<TestView childKey="foo" effect={effectA} />);
});
expect(registry).toEqual([TestEffect.called('A', 'foo')]);
act(() => {
root.unmount();
});
expect(registry).toEqual([
TestEffect.called('A', 'foo'),
TestEffectCleanup.called('A', 'foo'),
]);
});
test('cleans up old effect before calling new effect', () => {
let root;
const {mockEffect, registry} = mockEffectRegistry();
const effectA = mockEffect('A');
const effectB = mockEffect('B');
act(() => {
root = create(<TestView childKey="foo" effect={effectA} />);
});
act(() => {
root.update(<TestView childKey="foo" effect={effectB} />);
});
expect(registry).toEqual([
TestEffect.called('A', 'foo'),
TestEffectCleanup.called('A', 'foo'),
TestEffect.called('B', 'foo'),
]);
act(() => {
root.unmount();
});
expect(registry).toEqual([
TestEffect.called('A', 'foo'),
TestEffectCleanup.called('A', 'foo'),
TestEffect.called('B', 'foo'),
TestEffectCleanup.called('B', 'foo'),
]);
});
test('calls cleanup and effect on new instance', () => {
let root;
const {mockEffect, registry} = mockEffectRegistry();
const effectA = mockEffect('A');
act(() => {
root = create(<TestView childKey="foo" effect={effectA} />);
});
act(() => {
root.update(<TestView childKey="bar" effect={effectA} />);
});
expect(registry).toEqual([
TestEffect.called('A', 'foo'),
TestEffectCleanup.called('A', 'foo'),
TestEffect.called('A', 'bar'),
]);
act(() => {
root.unmount();
});
expect(registry).toEqual([
TestEffect.called('A', 'foo'),
TestEffectCleanup.called('A', 'foo'),
TestEffect.called('A', 'bar'),
TestEffectCleanup.called('A', 'bar'),
]);
});
test('cleans up old effect before calling new effect with new instance', () => {
let root;
const {mockEffect, registry} = mockEffectRegistry();
const effectA = mockEffect('A');
const effectB = mockEffect('B');
act(() => {
root = create(<TestView childKey="foo" effect={effectA} />);
});
act(() => {
root.update(<TestView childKey="bar" effect={effectB} />);
});
expect(registry).toEqual([
TestEffect.called('A', 'foo'),
TestEffectCleanup.called('A', 'foo'),
TestEffect.called('B', 'bar'),
]);
act(() => {
root.unmount();
});
expect(registry).toEqual([
TestEffect.called('A', 'foo'),
TestEffectCleanup.called('A', 'foo'),
TestEffect.called('B', 'bar'),
TestEffectCleanup.called('B', 'bar'),
]);
});