Skip to content

Commit

Permalink
add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
AndyTWF committed Nov 4, 2024
1 parent b0dc19a commit 377735b
Show file tree
Hide file tree
Showing 7 changed files with 414 additions and 10 deletions.
10 changes: 1 addition & 9 deletions src/react/helper/room-promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class DefaultRoomPromise implements RoomPromise {
return;
}

this._logger.debug('DefaultRoomPromise(); mount resolved', { roomId: this._roomId });
this._onUnmount = this._onResolve(room);
} catch (error) {
this._logger.error('DefaultRoomPromise(); mount error', { roomId: this._roomId, error });
Expand Down Expand Up @@ -61,12 +62,3 @@ export function wrapRoomPromise(
): RoomPromise {
return new DefaultRoomPromise(room, onResolve, logger, id);
}

export function wrapRoomPromiseWithUnmountError(
room: Promise<Room>,
onResolve: RoomResolutionCallback,
logger: Logger,
id?: string,
): RoomPromise {
return new DefaultRoomPromise(room, onResolve, logger, id);
}
94 changes: 94 additions & 0 deletions test/react/helper/room-promise.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { Room } from '@ably/chat';
import { describe, expect, it, vi } from 'vitest';

import { wrapRoomPromise } from '../../../src/react/helper/room-promise.ts';
import { makeTestLogger } from '../../helper/logger.ts';
import { makeRandomRoom } from '../../helper/room.ts';

describe('room-promise', () => {
it('should mount and unmount with promise resolution', async () => {
let shouldResolve = false;
let hasResolved = false;
const roomPromise = new Promise<Room>((resolve) => {
const interval = setInterval(() => {
if (shouldResolve) {
clearInterval(interval);
resolve(makeRandomRoom({}));
}
}, 150);
});

// Wrap the promise
let hasUnmounted = false;
const wrapped = wrapRoomPromise(
roomPromise,
(room) => {
hasResolved = true;
expect(room).toBeDefined();

return () => {
hasUnmounted = true;
};
},
makeTestLogger(),
'test-room',
);

// Now say the promise should resolve
shouldResolve = true;
await vi.waitFor(() => {
expect(hasResolved).toBe(true);
});

// Now call unmount
wrapped.unmount()();

expect(hasUnmounted).toBe(true);
});

it('should mount and unmount before promise resolution', async () => {
let shouldResolve = false;
const roomPromise = new Promise<Room>((resolve) => {
const interval = setInterval(() => {
if (shouldResolve) {
clearInterval(interval);
resolve(makeRandomRoom({}));
}
}, 150);
});

// Wrap the promise
const wrapped = wrapRoomPromise(
roomPromise,
() => {
// Should never be called
expect(true).toBe(false);

return () => {
expect(true).toBe(false);
};
},
makeTestLogger(),
'test-room',
);

// Now call unmount
wrapped.unmount()();

// Now say the promise should resolve
shouldResolve = true;

// Wait for 5 intervals of 150ms to confirm the callback was never called
for (let i = 0; i < 5; i++) {
await new Promise((resolve) => setTimeout(resolve, 150));
}

// Calling unmount again should be a noop
wrapped.unmount()();

// Wait for another set of intervals to confirm the callback was never called
for (let i = 0; i < 5; i++) {
await new Promise((resolve) => setTimeout(resolve, 150));
}
});
});
103 changes: 103 additions & 0 deletions test/react/helper/use-eventual-room.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Logger, Room, RoomLifecycle, RoomOptionsDefaults, RoomStatusChange } from "@ably/chat";

Check failure on line 1 in test/react/helper/use-eventual-room.test.tsx

View workflow job for this annotation

GitHub Actions / lint

'RoomLifecycle' is defined but never used

Check failure on line 1 in test/react/helper/use-eventual-room.test.tsx

View workflow job for this annotation

GitHub Actions / lint

'RoomStatusChange' is defined but never used
import { cleanup, renderHook } from "@testing-library/react";
import * as Ably from 'ably';

Check failure on line 3 in test/react/helper/use-eventual-room.test.tsx

View workflow job for this annotation

GitHub Actions / lint

'Ably' is defined but never used
import { afterEach,beforeEach,describe, expect, it, vi } from "vitest";

import { InternalRoomStatus } from "../../../src/core/room-status.ts";

Check failure on line 6 in test/react/helper/use-eventual-room.test.tsx

View workflow job for this annotation

GitHub Actions / lint

'InternalRoomStatus' is defined but never used
import { useEventualRoom, useEventualRoomProperty } from "../../../src/react/helper/use-eventual-room.ts";
import { useRoomStatus } from "../../../src/react/helper/use-room-status.ts";

Check failure on line 8 in test/react/helper/use-eventual-room.test.tsx

View workflow job for this annotation

GitHub Actions / lint

'useRoomStatus' is defined but never used
import { makeTestLogger } from "../../helper/logger.ts";
import { makeRandomRoom } from "../../helper/room.ts";
import { waitForEventualHookValue } from "../../helper/wait-for-eventual-hook.ts";

Check failure on line 11 in test/react/helper/use-eventual-room.test.tsx

View workflow job for this annotation

GitHub Actions / lint

'waitForEventualHookValue' is defined but never used

let mockRoom: Room;
let mockRoomContext: { room: Promise<Room> };
let mockLogger: Logger

vi.mock('../../../src/react/helper/use-room-context.js', () => ({
useRoomContext: () => mockRoomContext,
}));

vi.mock('../../../src/react/hooks/use-logger.js', () => ({
useLogger: () => mockLogger,
}));

vi.mock('ably');

const updateMockRoom = (newRoom: Room) => {
mockRoom = newRoom;
mockRoomContext = { room: Promise.resolve(newRoom) };
};

describe('eventual rooms', () => {
beforeEach(() => {
mockLogger = makeTestLogger();
updateMockRoom(makeRandomRoom({ options: RoomOptionsDefaults}));
});

afterEach(() => {
cleanup();
});

describe('useEventualRoom', () => {
it('returns the room', async () => {
const {result} = renderHook(() => useEventualRoom());

// We should start with the room being undefined
expect(result.current).toBeUndefined();

// Eventually, the room should resolve
await vi.waitFor(() => { expect(result.current).toBe(mockRoom); });
});

it('updates the room', async () => {
const {result, rerender} = renderHook(() => useEventualRoom());

// We should start with the room being undefined
expect(result.current).toBeUndefined();

// Eventually, the room should resolve
await vi.waitFor(() => { expect(result.current).toBe(mockRoom); });

// Now update the room and re-render
const newRoom = makeRandomRoom({ options: RoomOptionsDefaults});
updateMockRoom(newRoom);

rerender();

// Eventually, the room should resolve
await vi.waitFor(() => { expect(result.current).toBe(newRoom); });
});
});

describe('useEventualRoomProperty', () => {
it('returns the room property', async () => {
const {result} = renderHook(() => useEventualRoomProperty(() => mockRoom.messages));

// We should start with the room being undefined
expect(result.current).toBeUndefined();

// Eventually, the room should resolve
await vi.waitFor(() => { expect(result.current).toBe(mockRoom.messages); });
});

it('updates the room property', async () => {
const {result, rerender} = renderHook(() => useEventualRoomProperty(() => mockRoom.messages));

// We should start with the room being undefined
expect(result.current).toBeUndefined();

// Eventually, the room should resolve
await vi.waitFor(() => { expect(result.current).toBe(mockRoom.messages); });

// Now update the room and re-render
const newRoom = makeRandomRoom({ options: RoomOptionsDefaults});
updateMockRoom(newRoom);

rerender();

// Eventually, the room should resolve
await vi.waitFor(() => { expect(result.current).toBe(newRoom.messages); });
});
});
});
60 changes: 60 additions & 0 deletions test/react/helper/use-room-context.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { ChatClient, RoomOptionsDefaults } from "@ably/chat";
import { cleanup, render } from "@testing-library/react";
import { afterEach,describe, expect, it } from "vitest";

import { useRoomContext } from "../../../src/react/helper/use-room-context.ts";
import { ChatRoomProvider } from "../../../src/react/index.ts";
import { ChatClientProvider } from "../../../src/react/providers/chat-client-provider.tsx";
import { newChatClient as newChatClientLib } from "../../helper/chat.ts";

function newChatClient() {
return newChatClientLib() as unknown as ChatClient;
}

describe('useRoom', () => {
afterEach(() => {
cleanup();
});

it('should throw an error if used outside of ChatRoomProvider', () => {
const chatClient = newChatClient();

const TestThrowError: React.FC = () => {
expect(() => useRoomContext('foo')).toThrowErrorInfo({
code: 40000,
message: 'foo hook must be used within a <ChatRoomProvider>',
});
return null;
};

const TestProvider = () => (
<ChatClientProvider client={chatClient}>
<TestThrowError />
</ChatClientProvider>
);

render(<TestProvider />);
});

it('should return the context if used within ChatRoomProvider', () => {
const chatClient = newChatClient();

const TestUseRoom: React.FC = () => {
const context = useRoomContext('foo');
expect(context).toBeDefined();
expect(context.roomId).toBe('foo');
expect(context.options).toBe(RoomOptionsDefaults);
return null;
};

const TestProvider = () => (
<ChatClientProvider client={chatClient}>
<ChatRoomProvider id="foo" options={RoomOptionsDefaults}>
<TestUseRoom />
</ChatRoomProvider>
</ChatClientProvider>
);

render(<TestProvider />);
});
});
Loading

0 comments on commit 377735b

Please sign in to comment.