Skip to content
This repository has been archived by the owner on Oct 30, 2023. It is now read-only.

Commit

Permalink
wip: use live sandbox client for tests
Browse files Browse the repository at this point in the history
  • Loading branch information
owenpearson committed Jul 26, 2023
1 parent 3531e46 commit 0975e41
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 79 deletions.
63 changes: 20 additions & 43 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"got": "^11.8.6",
"jest": "^29.6.1",
"jest-environment-jsdom": "^29.6.1",
"jsdom": "^20.0.0",
Expand Down
83 changes: 59 additions & 24 deletions src/hooks/useChannel.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,71 +2,106 @@ import React from 'react';
import { provideSdkInstance } from '../AblyReactHooks';
import { useChannel } from './useChannel';
import { useState } from 'react';
import { render, screen } from '@testing-library/react';
import { FakeAblySdk, FakeAblyChannels } from '../fakes/ably';
import { act, render, screen, waitFor } from '@testing-library/react';
import { Types } from 'ably';
import { act } from 'react-dom/test-utils';
import { TestApp } from '../test/testapp';

describe('useChannel', () => {
let channels: FakeAblyChannels;
let ablyClient: FakeAblySdk;
let otherClient: FakeAblySdk;

beforeEach(() => {
channels = new FakeAblyChannels(['blah']);
ablyClient = new FakeAblySdk().connectTo(channels);
otherClient = new FakeAblySdk().connectTo(channels);
provideSdkInstance(ablyClient as any);
let testApp: TestApp;

beforeAll(async () => {
testApp = await TestApp.create();
});

afterAll(async () => {
await testApp.delete();
}, 10_000);

it('component can useChannel and renders nothing by default', async () => {
const client = await testApp.client();
provideSdkInstance(client);
render(<UseChannelComponent></UseChannelComponent>);
const messageUl = screen.getAllByRole('messages')[0];

expect(messageUl.childElementCount).toBe(0);
client.close();
});

it('component updates when message arrives', async () => {
const client = await testApp.client();
const otherClient = await testApp.client();
provideSdkInstance(client);
render(<UseChannelComponent></UseChannelComponent>);

await act(async () => {
otherClient.channels.get('blah').publish({ text: 'message text' });
await otherClient.channels
.get('blah')
.publish('event', { text: 'message text' });
});

const messageUl = screen.getAllByRole('messages')[0];
expect(messageUl.childElementCount).toBe(1);
expect(messageUl.children[0].innerHTML).toBe('message text');

await waitFor(() => {
expect(messageUl.childElementCount).toBe(1);
expect(messageUl.children[0].innerHTML).toBe('message text');
});
client.close();
otherClient.close();
});

it('component updates when multiple messages arrive', async () => {
const client = await testApp.client();
const otherClient = await testApp.client();
provideSdkInstance(client);
render(<UseChannelComponent></UseChannelComponent>);

await act(async () => {
otherClient.channels.get('blah').publish({ text: 'message text1' });
otherClient.channels.get('blah').publish({ text: 'message text2' });
await otherClient.channels
.get('blah')
.publish('event', { text: 'message text1' });
await otherClient.channels
.get('blah')
.publish('event', { text: 'message text2' });
});

const messageUl = screen.getAllByRole('messages')[0];
expect(messageUl.children[0].innerHTML).toBe('message text1');
expect(messageUl.children[1].innerHTML).toBe('message text2');

await waitFor(() => {
expect(messageUl.children[0].innerHTML).toBe('message text1');
expect(messageUl.children[1].innerHTML).toBe('message text2');
});
client.close();
otherClient.close();
});

it('useChannel works with multiple clients', async () => {
const client = await testApp.client();
const otherClient = await testApp.client();
provideSdkInstance(client);
render(
<UseChannelComponentMultipleClients
client1={ablyClient}
client1={client}
client2={otherClient}
></UseChannelComponentMultipleClients>
);

await act(async () => {
ablyClient.channels.get('blah').publish({ text: 'message text1' });
otherClient.channels.get('bleh').publish({ text: 'message text2' });
await client.channels
.get('blah')
.publish('event', { text: 'message text1' });
await otherClient.channels
.get('bleh')
.publish('event', { text: 'message text2' });
});

const messageUl = screen.getAllByRole('messages')[0];
expect(messageUl.children[0].innerHTML).toBe('message text1');
expect(messageUl.children[1].innerHTML).toBe('message text2');

await waitFor(() => {
expect(messageUl.children[0].innerHTML).toBe('message text1');
expect(messageUl.children[1].innerHTML).toBe('message text2');
});
client.close();
otherClient.close();
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/hooks/useChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function useChannel(
// To solve this, we set a timer, and if all the listeners have been removed, we know that the component
// has been removed for good and we can detatch the channel.

if (channel.listeners.length === 0) {
if (channel.listeners.length === 0 && channel.state === "attached") {
await channel.detach();
}
}, 2500);
Expand Down
35 changes: 24 additions & 11 deletions src/hooks/usePresence.test.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,34 @@
import React from 'react';
import { provideSdkInstance } from '../AblyReactHooks';
import { usePresence } from './usePresence';
import { render, screen, act } from '@testing-library/react';
import { FakeAblySdk, FakeAblyChannels } from '../fakes/ably';
import { render, screen, act, waitFor } from '@testing-library/react';
import { Types } from 'ably';
import { TestApp } from '../test/testapp';

const testChannelName = 'testChannel';

describe('usePresence', () => {
let channels: FakeAblyChannels;
let ablyClient: FakeAblySdk;
let otherClient: FakeAblySdk;

beforeEach(() => {
channels = new FakeAblyChannels([testChannelName]);
ablyClient = new FakeAblySdk().connectTo(channels);
otherClient = new FakeAblySdk().connectTo(channels);
provideSdkInstance(ablyClient as any);
let testApp: TestApp;
let ablyClient: Types.RealtimePromise;
let otherClient: Types.RealtimePromise;

beforeAll(async () => {
testApp = await TestApp.create();
});

beforeEach(async () => {
ablyClient = await testApp.client();
otherClient = await testApp.client();
provideSdkInstance(ablyClient);
});

afterEach(() => {
ablyClient.close();
otherClient.close();
});

afterAll(async () => {
await testApp.delete();
});

it('presence data is not visible on first render as it runs in an effect', async () => {
Expand Down
39 changes: 39 additions & 0 deletions src/test/testapp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as Ably from 'ably';
import { Types } from 'ably';
import got from 'got';

export class TestApp {
id: string;
key: string;

static async create() {
const url = 'https://sandbox-rest.ably.io/apps';
const body = { keys: [{}] };

const res: {
appId: string;
keys: { keyStr: string }[];
} = await got.post(url, { json: body }).json();

return new TestApp(res.appId, res.keys[0].keyStr);
}

constructor(id: string, key: string) {
this.id = id;
this.key = key;
}

client(clientId?: string): Types.RealtimePromise {
return new Ably.Realtime.Promise({
key: this.key,
environment: 'sandbox',
clientId: clientId || null,
});
}

delete() {
return got.delete(
`https://sandbox-rest.ably.io/apps/${this.id}?key=${this.key}`
);
}
}

0 comments on commit 0975e41

Please sign in to comment.