From 6649ab73ebf76ba66d87b8f48fd30bd1b0b2318d Mon Sep 17 00:00:00 2001 From: Peter van Hardenberg Date: Fri, 16 Feb 2024 12:24:30 -0800 Subject: [PATCH 1/3] a non-working start on tests with some notes --- .../test/useAwareness.test.atsx | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 packages/automerge-repo-react-hooks/test/useAwareness.test.atsx diff --git a/packages/automerge-repo-react-hooks/test/useAwareness.test.atsx b/packages/automerge-repo-react-hooks/test/useAwareness.test.atsx new file mode 100644 index 000000000..186b01751 --- /dev/null +++ b/packages/automerge-repo-react-hooks/test/useAwareness.test.atsx @@ -0,0 +1,31 @@ +import { renderHook } from "@testing-library/react" +import { describe, expect, test, vi } from "vitest" +import { RepoContext, useRepo } from "../src/useRepo.js" +import { Repo } from "@automerge/automerge-repo" +import React from "react" + +describe("use{Local,Remote}Awareness", () => { + test("it should allow a user to create a local awareness", () => { + const repo = new Repo({ network: [] }) + // Prevent console spam by swallowing console.error "uncaught error" message + const spy = vi.spyOn(console, "error") + spy.mockImplementation(() => {}) + expect(() => renderHook(() => useRepo())).toThrow( + /Repo was not found on RepoContext/ + ) + spy.mockRestore() + }) +}) + +// basic send/receive +// it should allow a user to set a local awareness +// it should not notify the local peer when they set a local awareness +// it should return another peer's awareness when it gets set remotely +// heartbeats & timeouts +// it should remove a peer that hasn't sent an update since the heartbeat timeout +// it should send a heartbeat at an configurable interval +// multiple docs +// it shouldn't receive awareness across docs +// multiple clients with awareness +// when there are more than one client with the same userid it shouldn't compete for the same awareness +// if a user has two clients, the value should be the last one set by the user (most recent wins) From 0b1a3e8d190be943b20f984607c17e16772608b2 Mon Sep 17 00:00:00 2001 From: Mauve Signweaver Date: Fri, 16 Feb 2024 15:27:20 -0500 Subject: [PATCH 2/3] Rename useAwareness test extension --- .../test/{useAwareness.test.atsx => useAwareness.test.tsx} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/automerge-repo-react-hooks/test/{useAwareness.test.atsx => useAwareness.test.tsx} (100%) diff --git a/packages/automerge-repo-react-hooks/test/useAwareness.test.atsx b/packages/automerge-repo-react-hooks/test/useAwareness.test.tsx similarity index 100% rename from packages/automerge-repo-react-hooks/test/useAwareness.test.atsx rename to packages/automerge-repo-react-hooks/test/useAwareness.test.tsx From 85e391b4e6b1dcb233ee276a666ff76de4846a9e Mon Sep 17 00:00:00 2001 From: Mauve Signweaver Date: Fri, 23 Feb 2024 19:00:06 -0500 Subject: [PATCH 3/3] initial tests for useAwareness react hooks --- .../test/useAwareness.test.tsx | 143 ++++++++++++++++-- 1 file changed, 132 insertions(+), 11 deletions(-) diff --git a/packages/automerge-repo-react-hooks/test/useAwareness.test.tsx b/packages/automerge-repo-react-hooks/test/useAwareness.test.tsx index 186b01751..3902d2487 100644 --- a/packages/automerge-repo-react-hooks/test/useAwareness.test.tsx +++ b/packages/automerge-repo-react-hooks/test/useAwareness.test.tsx @@ -1,19 +1,140 @@ -import { renderHook } from "@testing-library/react" -import { describe, expect, test, vi } from "vitest" -import { RepoContext, useRepo } from "../src/useRepo.js" -import { Repo } from "@automerge/automerge-repo" -import React from "react" +import { renderHook, act, waitFor } from "@testing-library/react" +import { describe, expect, test } from "vitest" +import { MessageChannelNetworkAdapter } from "../../automerge-repo-network-messagechannel/src/index.js" +import { useLocalAwareness } from "../src/useLocalAwareness.js" +import { useRemoteAwareness } from "../src/useRemoteAwareness.js" +import { Repo, PeerId } from "@automerge/automerge-repo" + +function setupMesh(n = 2) { + const abChannel = new MessageChannel() + + const { port1: ab, port2: ba } = abChannel + + const alice = "alice" as PeerId + const aliceRepo = new Repo({ + network: [new MessageChannelNetworkAdapter(ab)], + peerId: alice, + }) + + const bob = "bob" as PeerId + const bobRepo = new Repo({ + network: [new MessageChannelNetworkAdapter(ba)], + peerId: bob, + }) + + return { alice, bob, aliceRepo, bobRepo } +} describe("use{Local,Remote}Awareness", () => { test("it should allow a user to create a local awareness", () => { const repo = new Repo({ network: [] }) - // Prevent console spam by swallowing console.error "uncaught error" message - const spy = vi.spyOn(console, "error") - spy.mockImplementation(() => {}) - expect(() => renderHook(() => useRepo())).toThrow( - /Repo was not found on RepoContext/ + const handle = repo.create("Hello World") + + const { result, rerender } = renderHook(() => + useLocalAwareness({ + handle, + userId: "alice", + initialState: "Hello World", + }) ) - spy.mockRestore() + + const [state, setState] = result.current + + expect(state).toEqual("Hello World") + + act(() => { + setState("Goodbye") + }) + + rerender() + + const [state2, setState2] = result.current + + expect(state2).toEqual("Goodbye") + }) + + test("it should show state on remote awareness for same doc handle", () => { + const userId = "alice" + const initialState = "Hello World" + + const repo = new Repo({ network: [] }) + const handle = repo.create("Hello World") + + const { result, rerender } = renderHook(() => [ + useLocalAwareness({ + handle, + userId, + initialState, + }), + useRemoteAwareness({ + handle, + localUserId: userId, + }), + ]) + + const [local, remote] = result.current + + const [peerStates, heartbeats] = remote + + // For just the local peer it should ignore itself + expect(peerStates).toEqual({}) + expect(heartbeats).toEqual({}) + }) + + test("it should send local state from alice to remote state of bob", async () => { + let timeCount = 0 + function getTime() { + return timeCount++ + } + + const initialState = "Hello World" + const { alice, bob, aliceRepo, bobRepo } = setupMesh() + + const aliceHandle = aliceRepo.create("Hello World") + const bobHandle = bobRepo.find(aliceHandle.url) + + const { result: resultA, rerender: rerenderA } = renderHook(() => [ + useLocalAwareness({ + handle: aliceHandle, + userId: alice, + initialState, + }), + useRemoteAwareness({ + handle: aliceHandle, + localUserId: alice, + getTime, + }), + ]) + + // TODO: Need to wait for initialization somehow? + await new Promise(resolve => setTimeout(resolve, 100)) + + const { result: resultB, rerender: rerenderB } = renderHook(() => [ + useLocalAwareness({ + handle: bobHandle, + userId: bob, + initialState, + }), + useRemoteAwareness({ + handle: bobHandle, + localUserId: bob, + getTime, + }), + ]) + + await waitFor(() => { + rerenderA() + rerenderB() + const [localB, remoteB] = resultB.current + + const [peerStates, heartbeats] = remoteB + + // For just the local peer it should ignore itself + expect(peerStates).toEqual({ [alice]: initialState }) + // expect two heartbeats from alice + // their inital, then second once seeing bob + expect(heartbeats).toEqual({ [alice]: 2 }) + }) }) })