mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
cb25638a0e
Summary: Creates `useRefEffect` which will be used by components in React Native. Changelog: [Internal] Reviewed By: lunaleaps Differential Revision: D28862440 fbshipit-source-id: 50e0099c1a3e0a0f506bf82e68984fc5a032f101
243 lines
5.7 KiB
JavaScript
243 lines
5.7 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @emails oncall+react_native
|
|
* @flow strict-local
|
|
* @format
|
|
*/
|
|
|
|
import useRefEffect from '../useRefEffect';
|
|
import * as React from 'react';
|
|
import {View} from 'react-native';
|
|
import {act, create} from 'react-test-renderer';
|
|
|
|
/**
|
|
* TestView provide a component execution environment to test hooks.
|
|
*/
|
|
function TestView({childKey = null, effect}) {
|
|
const ref = useRefEffect(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) {
|
|
// $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) {
|
|
// $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 = [];
|
|
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'),
|
|
]);
|
|
});
|