From 6ce5c27f8be9d7682d7d688259261f84d7d3e861 Mon Sep 17 00:00:00 2001 From: Jackson Morgan Date: Thu, 2 Jan 2025 22:39:04 -0500 Subject: [PATCH] Add subscription option and test --- packages/solid-react/.gitignore | 1 + packages/solid-react/jest.setup.ts | 2 +- packages/solid-react/src/useResource.ts | 12 +++ .../solid-react/test/Integration.test.tsx | 81 ++++++++++++++++++- packages/solid/.gitignore | 2 +- packages/solid/test/solidServer.helper.ts | 2 +- 6 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 packages/solid-react/.gitignore diff --git a/packages/solid-react/.gitignore b/packages/solid-react/.gitignore new file mode 100644 index 0000000..0c32b1f --- /dev/null +++ b/packages/solid-react/.gitignore @@ -0,0 +1 @@ +test/test-server/data \ No newline at end of file diff --git a/packages/solid-react/jest.setup.ts b/packages/solid-react/jest.setup.ts index 9d611e2..22eed5f 100644 --- a/packages/solid-react/jest.setup.ts +++ b/packages/solid-react/jest.setup.ts @@ -1,2 +1,2 @@ import "@inrupt/jest-jsdom-polyfills"; -globalThis.fetch = async () => new Response(); \ No newline at end of file +globalThis.fetch = async () => new Response(); diff --git a/packages/solid-react/src/useResource.ts b/packages/solid-react/src/useResource.ts index 0747118..fb0d18a 100644 --- a/packages/solid-react/src/useResource.ts +++ b/packages/solid-react/src/useResource.ts @@ -11,6 +11,7 @@ import { useLdo } from "./SolidLdoProvider"; export interface UseResourceOptions { suppressInitialRead?: boolean; reloadOnMount?: boolean; + subscribe?: boolean; } export function useResource( @@ -62,6 +63,17 @@ export function useResource( { resource?: Resource; callback: () => void } | undefined >(); + useEffect(() => { + if (options?.subscribe) { + resource?.subscribeToNotifications(); + } else { + resource?.unsubscribeFromNotifications(); + } + return () => { + resource?.unsubscribeFromNotifications(); + }; + }, [resource, options?.subscribe]); + // Callback function to force the react dom to reload. const forceReload = useCallback( // Wrap the resource in a proxy so it's techically a different object diff --git a/packages/solid-react/test/Integration.test.tsx b/packages/solid-react/test/Integration.test.tsx index 4d2d28e..8c8ae0b 100644 --- a/packages/solid-react/test/Integration.test.tsx +++ b/packages/solid-react/test/Integration.test.tsx @@ -1,6 +1,6 @@ -import React, { useEffect, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import type { FunctionComponent } from "react"; -import { render, screen, fireEvent } from "@testing-library/react"; +import { render, screen, fireEvent, act } from "@testing-library/react"; import { SAMPLE_BINARY_URI, SAMPLE_DATA_URI, @@ -131,6 +131,11 @@ describe("Integration Tests", () => { }); }); + /** + * =========================================================================== + * useRootContainer + * =========================================================================== + */ describe("useRootContainer", () => { it("gets the root container for a sub-resource", async () => { const RootContainerTest: FunctionComponent = () => { @@ -169,7 +174,12 @@ describe("Integration Tests", () => { }); }); - describe("useLdoMethod", () => { + /** + * =========================================================================== + * useLdoMethods + * =========================================================================== + */ + describe("useLdoMethods", () => { it("uses get subject to get a linked data object", async () => { const GetSubjectTest: FunctionComponent = () => { const [subject, setSubject] = useState(); @@ -217,6 +227,11 @@ describe("Integration Tests", () => { }); }); + /** + * =========================================================================== + * useSubject + * =========================================================================== + */ describe("useSubject", () => { it("renders the article body from the useSubject value", async () => { const UseSubjectTest: FunctionComponent = () => { @@ -352,5 +367,65 @@ describe("Integration Tests", () => { ); warn.mockReset(); }); + + it("rerenders when asked to subscribe to a resource", async () => { + const NotificationTest: FunctionComponent = () => { + const resource = useResource(SAMPLE_DATA_URI, { subscribe: true }); + const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`); + + const addPublisher = useCallback(async () => { + await fetch(SAMPLE_DATA_URI, { + method: "PATCH", + body: `INSERT DATA { <${SAMPLE_DATA_URI}#Post1> . }`, + headers: { + "Content-Type": "application/sparql-update", + }, + }); + }, []); + + if (resource.isLoading() || !post) return

loading

; + + return ( +
+
    + {post.publisher.map((publisher) => { + return
  • {publisher["@id"]}
  • ; + })} +
+ +
+ ); + }; + const { unmount } = render( + + + , + ); + + const list = await screen.findByRole("list"); + expect(list.children[0].innerHTML).toBe("https://example.com/Publisher1"); + expect(list.children[1].innerHTML).toBe("https://example.com/Publisher2"); + + // Wait for subscription to connect + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + }); + + // Click button to add a publisher + await fireEvent.click(screen.getByText("Add Publisher")); + await screen.findByText("https://example.com/Publisher3"); + + // Verify the new publisher is in the list + const updatedList = await screen.findByRole("list"); + expect(updatedList.children[2].innerHTML).toBe( + "https://example.com/Publisher3", + ); + + unmount(); + + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 1000)); + }); + }); }); }); diff --git a/packages/solid/.gitignore b/packages/solid/.gitignore index 6320cd2..5ed9aae 100644 --- a/packages/solid/.gitignore +++ b/packages/solid/.gitignore @@ -1 +1 @@ -data \ No newline at end of file +test/data \ No newline at end of file diff --git a/packages/solid/test/solidServer.helper.ts b/packages/solid/test/solidServer.helper.ts index b27b58b..38069d5 100644 --- a/packages/solid/test/solidServer.helper.ts +++ b/packages/solid/test/solidServer.helper.ts @@ -34,7 +34,7 @@ export async function createApp(customConfigPath?: string): Promise { port: 3_001, loggingLevel: "off", seedConfig: path.join(__dirname, "configs", "solid-css-seed.json"), - rootFilePath: "./data", + rootFilePath: path.join(__dirname, "./data"), }, }); }