import React, { useEffect, useState } from "react";
import type { FunctionComponent } from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import {
SAMPLE_BINARY_URI,
SAMPLE_DATA_URI,
SERVER_DOMAIN,
setUpServer,
} from "./setUpServer";
import { UnauthenticatedSolidLdoProvider } from "../src/UnauthenticatedSolidLdoProvider";
import { useResource } from "../src/useResource";
import { useRootContainerFor } from "../src/useRootContainer";
import { useLdo } from "../src/SolidLdoProvider";
import { PostShShapeType } from "./.ldo/post.shapeTypes";
import type { PostSh } from "./.ldo/post.typings";
import { useSubject } from "../src/useSubject";
// Use an increased timeout, since the CSS server takes too much setup time.
jest.setTimeout(40_000);
describe("Integration Tests", () => {
setUpServer();
/**
* ===========================================================================
* useResource
* ===========================================================================
*/
describe("useResource", () => {
it("Fetches a resource and indicates it is loading while doing so", async () => {
const UseResourceTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI);
if (resource?.isLoading()) return
Loading
;
return {resource.status.type}
;
};
render(
,
);
await screen.findByText("Loading");
const resourceStatus = await screen.findByRole("status");
expect(resourceStatus.innerHTML).toBe("dataReadSuccess");
});
it("returns undefined when no uri is provided, then rerenders when one is", async () => {
const UseResourceUndefinedTest: FunctionComponent = () => {
const [uri, setUri] = useState(undefined);
const resource = useResource(uri, { suppressInitialRead: true });
if (!resource)
return (
Undefined
setUri(SAMPLE_DATA_URI)}>Next
);
return {resource.status.type}
;
};
render(
,
);
await screen.findByText("Undefined");
fireEvent.click(screen.getByText("Next"));
const resourceStatus = await screen.findByRole("status");
expect(resourceStatus.innerHTML).toBe("unfetched");
});
it("Reloads the data on mount", async () => {
const ReloadTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI, { reloadOnMount: true });
if (resource?.isLoading()) return Loading
;
return {resource.status.type}
;
};
const ReloadParent: FunctionComponent = () => {
const [showComponent, setShowComponent] = useState(true);
return (
setShowComponent(!showComponent)}>
Show Component
{showComponent ?
:
Hidden
}
);
};
render(
,
);
await screen.findByText("Loading");
const resourceStatus1 = await screen.findByRole("status");
expect(resourceStatus1.innerHTML).toBe("dataReadSuccess");
fireEvent.click(screen.getByText("Show Component"));
await screen.findByText("Hidden");
fireEvent.click(screen.getByText("Show Component"));
await screen.findByText("Loading");
const resourceStatus2 = await screen.findByRole("status");
expect(resourceStatus2.innerHTML).toBe("dataReadSuccess");
});
it("handles swapping to a new resource", async () => {
const SwapResourceTest: FunctionComponent = () => {
const [uri, setUri] = useState(SAMPLE_DATA_URI);
const resource = useResource(uri);
if (resource?.isLoading()) return Loading
;
return (
{resource.status.type}
setUri(SAMPLE_BINARY_URI)}>
Update URI
);
};
render(
,
);
await screen.findByText("Loading");
const resourceStatus1 = await screen.findByRole("status");
expect(resourceStatus1.innerHTML).toBe("dataReadSuccess");
fireEvent.click(screen.getByText("Update URI"));
await screen.findByText("Loading");
const resourceStatus2 = await screen.findByRole("status");
expect(resourceStatus2.innerHTML).toBe("binaryReadSuccess");
});
});
describe("useRootContainer", () => {
it("gets the root container for a sub-resource", async () => {
const RootContainerTest: FunctionComponent = () => {
const rootContainer = useRootContainerFor(SAMPLE_DATA_URI, {
suppressInitialRead: true,
});
return rootContainer ? {rootContainer?.uri}
: <>>;
};
render(
,
);
const container = await screen.findByRole("root");
expect(container.innerHTML).toBe(SERVER_DOMAIN);
});
it("returns undefined when a URI is not provided", async () => {
const RootContainerTest: FunctionComponent = () => {
const rootContainer = useRootContainerFor(undefined, {
suppressInitialRead: true,
});
return rootContainer ? (
{rootContainer?.uri}
) : (
Undefined
);
};
render(
,
);
const container = await screen.findByRole("undefined");
expect(container.innerHTML).toBe("Undefined");
});
});
describe("useLdoMethod", () => {
it("uses get subject to get a linked data object", async () => {
const GetSubjectTest: FunctionComponent = () => {
const [subject, setSubject] = useState();
const { getSubject } = useLdo();
useEffect(() => {
const someSubject = getSubject(
PostShShapeType,
"https://example.com/subject",
);
setSubject(someSubject);
}, []);
return subject ? {subject["@id"]}
: <>>;
};
render(
,
);
const container = await screen.findByRole("subject");
expect(container.innerHTML).toBe("https://example.com/subject");
});
it("uses createData to create a new data object", async () => {
const GetSubjectTest: FunctionComponent = () => {
const [subject, setSubject] = useState();
const { createData, getResource } = useLdo();
useEffect(() => {
const someSubject = createData(
PostShShapeType,
"https://example.com/subject",
getResource("https://example.com/"),
);
someSubject.articleBody = "Cool Article";
setSubject(someSubject);
}, []);
return subject ? {subject.articleBody}
: <>>;
};
render(
,
);
const container = await screen.findByRole("subject");
expect(container.innerHTML).toBe("Cool Article");
});
});
describe("useSubject", () => {
it("renders the article body from the useSubject value", async () => {
const UseSubjectTest: FunctionComponent = () => {
useResource(SAMPLE_DATA_URI);
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`);
return {post.articleBody}
;
};
render(
,
);
await screen.findByText("test");
});
it("renders the array value from the useSubject value", async () => {
const UseSubjectTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI);
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`);
if (resource.isLoading() || !post) return loading
;
return (
{post.publisher[0]["@id"]}
{post.publisher.map((publisher) => {
return {publisher["@id"]} ;
})}
);
};
render(
,
);
const single = await screen.findByRole("single");
expect(single.innerHTML).toBe("https://example.com/Publisher1");
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");
});
it("returns undefined in the subject URI is undefined", async () => {
const UseSubjectTest: FunctionComponent = () => {
useResource(SAMPLE_DATA_URI, { suppressInitialRead: true });
const post = useSubject(PostShShapeType, undefined);
return (
{post === undefined ? "Undefined" : "Not Undefined"}
);
};
render(
,
);
const article = await screen.findByRole("article");
expect(article.innerHTML).toBe("Undefined");
});
it("returns nothing if a symbol key is provided", async () => {
const UseSubjectTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI);
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`);
if (resource.isLoading() || !post) return loading
;
return {typeof post[Symbol.hasInstance]}
;
};
render(
,
);
const article = await screen.findByRole("value");
expect(article.innerHTML).toBe("undefined");
});
it("returns an id if an id key is provided", async () => {
const UseSubjectTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI);
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`);
if (resource.isLoading() || !post) return loading
;
return {post["@id"]}
;
};
render(
,
);
const article = await screen.findByRole("value");
expect(article.innerHTML).toBe(`${SAMPLE_DATA_URI}#Post1`);
});
it("does not set a value if a value is attempted to be set", async () => {
const warn = jest.spyOn(console, "warn").mockImplementation(() => {});
const UseSubjectTest: FunctionComponent = () => {
const resource = useResource(SAMPLE_DATA_URI);
const post = useSubject(PostShShapeType, `${SAMPLE_DATA_URI}#Post1`);
if (resource.isLoading() || !post) return loading
;
return (
{post.articleBody}
(post.articleBody = "bad")}>
Attempt Change
);
};
render(
,
);
const article = await screen.findByRole("value");
expect(article.innerHTML).toBe(`test`);
fireEvent.click(screen.getByText("Attempt Change"));
expect(article.innerHTML).not.toBe("bad");
expect(warn).toHaveBeenCalledWith(
"You've attempted to set a value on a Linked Data Object from the useSubject, useMatchingSubject, or useMatchingObject hooks. These linked data objects should only be used to render data, not modify it. To modify data, use the `changeData` function.",
);
warn.mockReset();
});
});
});