Skip to content

Commit

Permalink
Add useDig, useFindSubject, useStatus, useTryEntity
Browse files Browse the repository at this point in the history
  • Loading branch information
Thom van Kalkeren committed Apr 13, 2021
1 parent 3024f46 commit 0452814
Show file tree
Hide file tree
Showing 17 changed files with 529 additions and 56 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
"@pika/plugin-build-web": "^0.8.1",
"@pika/plugin-ts-standard-pkg": "^0.8.1",
"@rdfdev/iri": "^1.0.0",
"@testing-library/react": "^11.2.6",
"@types/enzyme": "^3.10.5",
"@types/enzyme-adapter-react-16": "^1.0.6",
"@types/hoist-non-react-statics": "^3.3.1",
Expand All @@ -75,7 +76,7 @@
"http-status-codes": "1.x",
"jest": "^25.2.4",
"jest-enzyme": "^7.1.2",
"link-lib": "0.0.0-890c0509",
"link-lib": "0.0.0-fd98a597",
"n-quads-parser": "^2.1.0",
"prop-types": "^15.x",
"react": "^16.13.1",
Expand Down
45 changes: 27 additions & 18 deletions src/__tests__/helpers/fixtures.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import rdfFactory, { NamedNode, Quad, SomeTerm } from "@ontologies/core";
import rdfFactory, { NamedNode, Quad, Quadruple, SomeTerm } from "@ontologies/core";
import * as ld from "@ontologies/ld";
import * as rdfx from "@ontologies/rdf";
import * as schema from "@ontologies/schema";
import {
Expand All @@ -20,6 +21,8 @@ import {
} from "../../index";
import ex from "../../ontology/ex";
import example from "../../ontology/example";
import http from "../../ontology/http";
import ll from "../../ontology/ll";

import { TestContext } from "./types";

Expand All @@ -34,24 +37,28 @@ interface CWResource extends CWOpts {
}
export type TestCtxCreator = (id?: NamedNode, attrs?: CWOpts) => TestContext<React.ComponentType<any>>;

const typeObject = (id: NamedNode) => [
const toDelta = (statements: Array<Quad | Quadruple>): Quadruple[] => statements
.map<Quadruple>((st) => Array.isArray(st) ? st : [st.subject, st.predicate, st.object, st.graph]);

const typeObject = (id: NamedNode) => toDelta([
rdfFactory.quad(id, rdfx.type, schema.CreativeWork),
];
]);

const sTitle = (id: NamedNode, title: string) => [
const sTitle = (id: NamedNode, title: string) => toDelta([
rdfFactory.quad(id, schema.name, rdfFactory.literal(title)),
];
]);

const sFull = (id: NamedNode, attrs: CWOpts = {}) => {
const createQuad = (predicate: NamedNode, object: SomeTerm) => rdfFactory.quad(
const sFull = (id: NamedNode, attrs: CWOpts = {}): Quadruple[] => {
const createQuad = (predicate: NamedNode, object: SomeTerm, graph = ld.add) => rdfFactory.quad(
id,
predicate,
object,
example.ns("default"),
graph,
);

return [
return toDelta([
typeObject(id)[0],
createQuad(http.statusCode, rdfFactory.literal(200), ll.meta),
createQuad(schema.name, rdfFactory.literal(attrs.title || "title")),
createQuad(schema.text, rdfFactory.literal(attrs.text || "text")),
createQuad(schema.author, rdfFactory.namedNode(attrs.author || "http://example.org/people/0")),
Expand All @@ -61,7 +68,7 @@ const sFull = (id: NamedNode, attrs: CWOpts = {}) => {
createQuad(example.ns("tags"), example.ns("tag/1")),
createQuad(example.ns("tags"), example.ns("tag/2")),
createQuad(example.ns("tags"), example.ns("tag/3")),
];
].filter(Boolean));
};

function createComponentWrapper(lrs: LinkReduxLRSType, subject: SubjectType) {
Expand All @@ -80,7 +87,7 @@ function createComponentWrapper(lrs: LinkReduxLRSType, subject: SubjectType) {
};
}

export function chargeLRS(statements: Quad[] = [], subject: SomeNode): TestContext<React.ComponentType<any>> {
export function chargeLRS(delta: Quadruple[] = [], subject: SomeNode): TestContext<React.ComponentType<any>> {
const store = new RDFStore();
const s = new Schema(store);
const lrsOpts = {
Expand All @@ -90,7 +97,8 @@ export function chargeLRS(statements: Quad[] = [], subject: SomeNode): TestConte
store,
};
const lrs = new LinkedRenderStore<React.ComponentType>(lrsOpts);
store.addQuads(statements);
lrs.api.processDelta(delta);
store.processDelta(delta);
store.flush();

return {
Expand Down Expand Up @@ -123,19 +131,20 @@ export const fullCW = (id = example.ns("3"), attrs: CWOpts = {}) => chargeLRS(
);

export const multipleCW = (id = example.ns("3"), attrs: CWOpts & { second?: CWResource } = {}) => {
const opts = chargeLRS(sFull(id, attrs), id);
const second = attrs.second || { id: example.ns("4") };
opts.store.addQuads(sFull(example.ns(second.id.value), second));
opts.store.flush();
const secondOpts = attrs.second || { id: example.ns("4") };
const delta = [
...sFull(secondOpts.id, secondOpts),
...sFull(id, attrs),
];

return opts;
return chargeLRS(delta, id);
};

export const multipleCWArr = (attrs: CWResource[] = []) => {
const first = attrs.pop()!;
const opts = chargeLRS(sFull(first.id, first), first.id);
attrs.forEach((obj) => {
opts.store.addQuads(sFull(obj.id, obj));
opts.store.processDelta(sFull(obj.id, obj));
});
opts.store.flush();

Expand Down
45 changes: 21 additions & 24 deletions src/components/__tests__/Resource.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ describe("Resource component", () => {
const bn = rdfFactory.blankNode();
const opts = ctx.chargeLRS(
[
rdfFactory.quad(bn, rdfx.type, schema.Thing),
rdfFactory.quad(bn, schema.name, rdfFactory.literal("title")),
rdfFactory.quadruple(bn, rdfx.type, schema.Thing),
rdfFactory.quadruple(bn, schema.name, rdfFactory.literal("title")),
],
bn,
);
Expand Down Expand Up @@ -129,18 +129,25 @@ describe("Resource component", () => {
expect(elem.find("span").last()).toHaveText("override");
});

const renderThroughOpts = () => {
const opts = ctx.multipleCW(iri, { second: { id: example.ns("resources/10") } });

opts.lrs.registerAll(LinkedRenderStore.registerRenderer(
createTestElement("normalRendered"),
schema.CreativeWork,
));
opts.lrs.registerAll(LinkedRenderStore.registerRenderer(
createTestElement("collectionRendered"),
schema.CreativeWork,
RENDER_CLASS_NAME,
ex.ns("collection"),
));

return opts;
};

it("renders correct topology through children", () => {
const opts = ctx.multipleCW(iri, { second: { id: rdfFactory.namedNode("resources/10") } });
opts.lrs.registerAll(LinkedRenderStore.registerRenderer(
createTestElement("normalRendered"),
schema.CreativeWork,
));
opts.lrs.registerAll(LinkedRenderStore.registerRenderer(
createTestElement("collectionRendered"),
schema.CreativeWork,
RENDER_CLASS_NAME,
ex.ns("collection"),
));
const opts = renderThroughOpts();

const comp = React.createElement(
Resource,
Expand All @@ -161,17 +168,7 @@ describe("Resource component", () => {
});

it("renders default topology through children", () => {
const opts = ctx.multipleCW(iri, { second: { id: rdfFactory.namedNode("resources/10") } });
opts.lrs.registerAll(LinkedRenderStore.registerRenderer(
createTestElement("normalRendered"),
schema.CreativeWork,
));
opts.lrs.registerAll(LinkedRenderStore.registerRenderer(
createTestElement("collectionRendered"),
schema.CreativeWork,
RENDER_CLASS_NAME,
ex.ns("collection"),
));
const opts = renderThroughOpts();

const comp = React.createElement(
Resource,
Expand Down
2 changes: 1 addition & 1 deletion src/hocs/__tests__/link.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ describe("link", () => {
iri,
schema.name,
rdfFactory.literal("title"),
example.ns("default"),
rdfFactory.defaultGraph(),
));
});
});
Expand Down
2 changes: 1 addition & 1 deletion src/hooks/__tests__/useLink.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { act } from "react-dom/test-utils";
import * as ctx from "../../__tests__/helpers/fixtures";
import { useLink } from "../useLink";

describe("useView", () => {
describe("useLink", () => {
let container: HTMLElement | undefined;

beforeEach(() => {
Expand Down
102 changes: 102 additions & 0 deletions src/hooks/__tests__/useStatus.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import React from "react";
import ReactDOM from "react-dom";
import { act } from "react-dom/test-utils";

import * as ctx from "../../__tests__/helpers/fixtures";
import example from "../../ontology/example";
import { useStatus } from "../useStatus";

describe("useStatus", () => {
let container: HTMLElement | undefined;

beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});

afterEach(() => {
document.body.removeChild(container!);
container = undefined;
});

it("defaults to context subject", () => {
const opts = ctx.fullCW();

const UpdateComp = () => {
const [status] = useStatus();

return (
<>
<span id="type">{typeof status}</span>
<span id="subject">{status?.subject.value}</span>
<span id="status">{status?.status}</span>
<span id="requested">{status?.requested?.toString()}</span>
<span id="lastRequested">{status?.lastRequested?.toISOString()}</span>
<span id="timesRequested">{status?.timesRequested}</span>
</>
);
};

act(() => {
// @ts-ignore
ReactDOM.render(opts.wrapComponent(<UpdateComp />), container);
});

expect(container!.querySelector("#type")!.textContent).toBe("object");
expect(container!.querySelector("#subject")!.textContent).toBe(opts.subject!.value);
expect(container!.querySelector("#status")!.textContent).toBe("200");
expect(container!.querySelector("#requested")!.textContent).toBe("true");
expect(container!.querySelector("#lastRequested")!.textContent)
.toMatch(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/);
expect(container!.querySelector("#timesRequested")!.textContent).toBe("1");
});

it("updates with many subject", () => {
const subject1 = example.ns("1");
const subject2 = example.ns("2");
const opts = ctx.multipleCW(subject1, { second: { id: subject2, title: "title 2" } });

const UpdateComp = () => {
const [status2, status1] = useStatus([subject2, subject1]);

return (
<>
<span id="type2">{typeof status2}</span>
<span id="subject2">{status2?.subject?.value}</span>
<span id="status2">{status2?.status}</span>
<span id="requested2">{status2?.requested?.toString()}</span>
<span id="lastRequested2">{status2?.lastRequested?.toISOString()}</span>
<span id="timesRequested2">{status2?.timesRequested}</span>

<span id="type1">{typeof status1}</span>
<span id="subject1">{status1?.subject?.value}</span>
<span id="status1">{status1?.status}</span>
<span id="requested1">{status1?.requested?.toString()}</span>
<span id="lastRequested1">{status1?.lastRequested?.toISOString()}</span>
<span id="timesRequested1">{status1?.timesRequested}</span>
</>
);
};

act(() => {
// @ts-ignore
ReactDOM.render(opts.wrapComponent(<UpdateComp />), container);
});

expect(container!.querySelector("#type1")!.textContent).toBe("object");
expect(container!.querySelector("#subject1")!.textContent).toBe(subject1.value);
expect(container!.querySelector("#status1")!.textContent).toBe("200");
expect(container!.querySelector("#requested1")!.textContent).toBe("true");
expect(container!.querySelector("#lastRequested1")!.textContent)
.toMatch(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/);
expect(container!.querySelector("#timesRequested1")!.textContent).toBe("1");

expect(container!.querySelector("#type2")!.textContent).toBe("object");
expect(container!.querySelector("#subject2")!.textContent).toBe(subject2.value);
expect(container!.querySelector("#status2")!.textContent).toBe("200");
expect(container!.querySelector("#requested2")!.textContent).toBe("true");
expect(container!.querySelector("#lastRequested2")!.textContent)
.toMatch(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/);
expect(container!.querySelector("#timesRequested2")!.textContent).toBe("1");
});
});
63 changes: 63 additions & 0 deletions src/hooks/__tests__/useSubject.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import rdfFactory from "@ontologies/core";
import { render, screen } from "@testing-library/react";
import React from "react";
import ReactDOM from "react-dom";
import { act } from "react-dom/test-utils";

import * as ctx from "../../__tests__/helpers/fixtures";
import { LaxNode } from "../../types";
import { useSubject } from "../useSubject";

describe("useSubject", () => {
let container: HTMLElement | undefined;

beforeEach(() => {
container = document.createElement("div");
document.body.appendChild(container);
});

afterEach(() => {
document.body.removeChild(container!);
container = undefined;
});

it("defaults to context subject", () => {
const opts = ctx.fullCW();

const UpdateComp = () => {
const [[subject]] = useSubject();

return (
<span id="subject">{subject!.value}</span>
);
};

act(() => {
// @ts-ignore
ReactDOM.render(opts.wrapComponent(<UpdateComp />), container);
});

expect(container!.querySelector("#subject")!.textContent).toBe(opts.subject!.value);
});

it("updates the subjects on change", () => {
const opts = ctx.fullCW();

const UpdateComp = ({ subjects }: { subjects?: LaxNode[] }) => {
const [[subject]] = useSubject(subjects);

return (
<span data-testid="subject">{subject!.value}</span>
);
};

const { rerender } = render(opts.wrapComponent(<UpdateComp />));

expect(screen.getByTestId("subject").textContent).toBe(opts.subject!.value);

const next = rdfFactory.blankNode();
rerender(opts.wrapComponent(<UpdateComp subjects={[next]} />));

expect(screen.getByTestId("subject").textContent).toBe(next.value);
});
});
2 changes: 2 additions & 0 deletions src/hooks/useDataInvalidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export function normalizeDataSubjects(props: Partial<DataInvalidationProps>): Su

/**
* Re-renders when one of the given {resources} changes in the store.
*
* Should only be necessary when using imperative code.
*/
export function useDataInvalidation(subjects: LaxNode | LaxNode[]): number {
const resources = normalizeType(subjects!).filter<Node>(Boolean as any);
Expand Down
Loading

0 comments on commit 0452814

Please sign in to comment.