stream-chat-react-native-core
Version:
The official React Native and Expo components for Stream Chat, a service for building chat applications
261 lines (198 loc) • 9.02 kB
text/typescript
import { act, renderHook } from '@testing-library/react-native';
import type { Channel } from 'stream-chat';
import { generateChannelResponse } from '../../mock-builders/generator/channel';
import { getTestClientWithUser } from '../../mock-builders/mock';
import type { File } from '../../types/types';
import {
EditChannelDetailsStore,
useAreChannelDetailsEdited,
useIsImageEdited,
useIsNameEdited,
} from '../edit-channel-details-store';
const file: File = {
name: 'pic.png',
size: 1234,
type: 'image/png',
uri: 'file://pic.png',
};
const createChannel = async (data: { image?: string; name?: string } = {}) => {
const client = await getTestClientWithUser({ id: 'me' });
const response = generateChannelResponse({ channel: data });
return client.channel('messaging', response.channel.id, response.channel) as Channel;
};
describe('EditChannelDetailsStore', () => {
describe('constructor', () => {
it('snapshots the initial name and image from the channel', async () => {
const channel = await createChannel({
image: 'http://img/original.png',
name: 'Original',
});
const store = new EditChannelDetailsStore(channel);
const state = store.state.getLatestValue();
expect(state.initialName).toBe('Original');
expect(state.initialImage).toBe('http://img/original.png');
expect(state.currentName).toBe('Original');
expect(state.updatedImage).toBeUndefined();
expect(state.pendingAction).toBeNull();
});
it('defaults to empty name and undefined image when channel has none', async () => {
const channel = await createChannel();
const store = new EditChannelDetailsStore(channel);
const state = store.state.getLatestValue();
expect(state.initialName).toBe('');
expect(state.initialImage).toBeUndefined();
expect(state.currentName).toBe('');
});
});
describe('setCurrentName', () => {
it('updates currentName and leaves initialName untouched', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
store.setCurrentName('Renamed');
const state = store.state.getLatestValue();
expect(state.currentName).toBe('Renamed');
expect(state.initialName).toBe('Original');
});
});
describe('setUpdatedImage', () => {
it('handles File, null and back to undefined', async () => {
const channel = await createChannel();
const store = new EditChannelDetailsStore(channel);
store.setUpdatedImage(file);
expect(store.state.getLatestValue().updatedImage).toBe(file);
store.setUpdatedImage(null);
expect(store.state.getLatestValue().updatedImage).toBeNull();
store.setUpdatedImage(undefined);
expect(store.state.getLatestValue().updatedImage).toBeUndefined();
});
});
describe('setPendingAction', () => {
it('sets each action value and clears back to null', async () => {
const channel = await createChannel();
const store = new EditChannelDetailsStore(channel);
(['camera', 'library', 'reset'] as const).forEach((action) => {
store.setPendingAction(action);
expect(store.state.getLatestValue().pendingAction).toBe(action);
});
store.setPendingAction(null);
expect(store.state.getLatestValue().pendingAction).toBeNull();
});
});
describe('useIsNameEdited', () => {
it('is false initially', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsNameEdited(store));
expect(result.current).toBe(false);
});
it('reacts to name changes', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsNameEdited(store));
expect(result.current).toBe(false);
act(() => store.setCurrentName('Renamed'));
expect(result.current).toBe(true);
act(() => store.setCurrentName('Original'));
expect(result.current).toBe(false);
});
it('treats untrimmed whitespace as edited', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsNameEdited(store));
act(() => store.setCurrentName('Original '));
expect(result.current).toBe(true);
});
it('ignores image changes', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsNameEdited(store));
act(() => store.setUpdatedImage(file));
expect(result.current).toBe(false);
});
});
describe('useIsImageEdited', () => {
it('is false initially', async () => {
const channel = await createChannel({ image: 'http://img/original.png' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsImageEdited(store));
expect(result.current).toBe(false);
});
it('is edited after a new image is picked', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsImageEdited(store));
act(() => store.setUpdatedImage(file));
expect(result.current).toBe(true);
});
it('ignores name changes', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsImageEdited(store));
act(() => store.setCurrentName('Renamed'));
expect(result.current).toBe(false);
});
describe('with an initial image', () => {
it('is edited on reset and clean again when untouched', async () => {
const channel = await createChannel({ image: 'http://img/original.png' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsImageEdited(store));
expect(result.current).toBe(false);
act(() => store.setUpdatedImage(null));
expect(result.current).toBe(true);
act(() => store.setUpdatedImage(undefined));
expect(result.current).toBe(false);
});
});
describe('without an initial image', () => {
it('is not edited on reset but is edited when a file is picked', async () => {
const channel = await createChannel();
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useIsImageEdited(store));
act(() => store.setUpdatedImage(null));
expect(result.current).toBe(false);
act(() => store.setUpdatedImage(file));
expect(result.current).toBe(true);
});
});
});
describe('useAreChannelDetailsEdited', () => {
it('is false initially', async () => {
const channel = await createChannel({ image: 'http://img/original.png', name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useAreChannelDetailsEdited(store));
expect(result.current).toBe(false);
});
it('is edited when the name changes and clean again when reverted', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useAreChannelDetailsEdited(store));
act(() => store.setCurrentName('Renamed'));
expect(result.current).toBe(true);
act(() => store.setCurrentName('Original'));
expect(result.current).toBe(false);
});
it('is edited when a new image is picked', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useAreChannelDetailsEdited(store));
act(() => store.setUpdatedImage(file));
expect(result.current).toBe(true);
});
it('stays edited while either field is edited', async () => {
const channel = await createChannel({ name: 'Original' });
const store = new EditChannelDetailsStore(channel);
const { result } = renderHook(() => useAreChannelDetailsEdited(store));
act(() => {
store.setCurrentName('Renamed');
store.setUpdatedImage(file);
});
expect(result.current).toBe(true);
// Revert the name; image is still edited, so the form stays edited.
act(() => store.setCurrentName('Original'));
expect(result.current).toBe(true);
// Revert the image too; now the form is clean.
act(() => store.setUpdatedImage(undefined));
expect(result.current).toBe(false);
});
});
});