rescribet/link-redux

View on GitHub
src/hooks/__tests__/useDataInvalidation.spec.tsx

Summary

Maintainability
F
6 days
Test Coverage
import "../../__tests__/useHashFactory";

import rdfFactory, { NamedNode } from "@ontologies/core";
import { render } from "@testing-library/react";
import { renderHook } from "@testing-library/react-hooks";
import { SubscriptionRegistrationBase } from "link-lib";
import React from "react";
import { act } from "react-dom/test-utils";

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

import { DataInvalidationProps } from "../../types";
import { normalizeDataSubjects, useDataInvalidation } from "../useDataInvalidation";

describe("useDataInvalidation", () => {
    describe("normalizeDataSubjects", () => {
        it("handles empty objects", () => {
            expect(normalizeDataSubjects({} as DataInvalidationProps))
                .toHaveLength(0);
        });

        it("keeps the reference", () => {
            expect(normalizeDataSubjects({ subject: example.ns("Tim") }))
                .toStrictEqual(example.ns("Tim"));
        });

        it("merges dataSubjects with the subject", () => {
            expect(normalizeDataSubjects({
                dataSubjects: [example.ns("Roy")],
                subject: example.ns("Tim"),
            })).toEqual([
                example.ns("Tim"),
                example.ns("Roy"),
            ]);
        });

        it("adds the base document for the subject", () => {
            expect(normalizeDataSubjects({
                subject: example.ns("Tim#me"),
            })).toEqual([
                example.ns("Tim#me"),
                example.ns("Tim"),
            ]);
        });
    });

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

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

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

      it("handles literals", () => {
        const { wrapper } = ctx.fullCW();
        const { result: { current } } = renderHook(() => useDataInvalidation(rdfFactory.literal("")), { wrapper });

        expect(current).toEqual(-1);
      });

      it("updates with data updates", () => {
        const iri = example.ns("");
        const opts = ctx.fullCW();

        const cb: Array<(_: any, u: number) => void> = [];
        const subscribe = jest.fn((o: SubscriptionRegistrationBase<any>) => {
          cb.push(o.callback);

          return () => undefined;
        });
        opts.lrs.subscribe = subscribe;

        const UpdateComp = () => {
          const lastUpdate = useDataInvalidation(iri);

          return <div data-testid="update">{lastUpdate}</div>;
        };

        const { getByTestId, rerender } = render(<UpdateComp />, { wrapper: opts.wrapper });

        expect(Number(getByTestId("update").textContent)).toBe(-1);

        act(() => {
          opts.lrs.store.getInternalStore().store.touch(iri.value);
          cb.map((f) => f(undefined, 1234));
        });

        rerender(<UpdateComp />);

        expect(Number(getByTestId("update").textContent)).toBe(1234);
      });

      it("invalidates after resource removal", () => {
        const firstId = example.ns("3");
        let idCounter = 1;
        const opts = ctx.fullCW(firstId);

        const Comp = ({ resource }: { resource: NamedNode | undefined }) => {
          const id = React.useRef(idCounter++);
          const lastUpdate = useDataInvalidation(resource);

          return (
            <div>
              <div data-testid="update">{lastUpdate}</div>
              <span data-testid="instance-id">{id.current}</span>
            </div>
          );
        };

        const { getByTestId, rerender } = render(<Comp resource={firstId} />, { wrapper: opts.wrapper });

        const firstUpdate = Number(getByTestId("update").textContent);
        expect(getByTestId("instance-id")).toHaveTextContent("1");

        act(() => {
          opts.lrs.removeResource(firstId, true);
        });

        rerender(<Comp resource={firstId} />);

        const secondUpdate = Number(getByTestId("update").textContent);
        expect(secondUpdate).toBeGreaterThan(firstUpdate);
        expect(getByTestId("instance-id")).toHaveTextContent("1");
      });

      it("invalidates when adding newer item", () => {
        const firstId = example.ns("3");
        const secondId = example.ns("4");
        let idCounter = 1;
        const opts = ctx.fullCW(firstId);
        const store = opts.lrs.store.getInternalStore().store;

        ctx.fullCW(secondId);
        act(() => {
          store.touch(secondId.value);
        });
        const firstResourceUpdate = store.getStatus(firstId.value).lastUpdate;
        const secondResourceUpdate = store.getStatus(secondId.value).lastUpdate;
        expect(secondResourceUpdate).toBeGreaterThan(firstResourceUpdate);

        const Comp = ({ resources }: { resources: NamedNode[] | undefined }) => {
          const id = React.useRef(idCounter++);
          const lastUpdate = useDataInvalidation(resources);

          return (
            <div>
              <div data-testid="update">{lastUpdate}</div>
              <span data-testid="instance-id">{id.current}</span>
            </div>
          );
        };

        const { getByTestId, rerender } = render(<Comp resources={[firstId]} />, { wrapper: opts.wrapper });

        const firstUpdate = Number(getByTestId("update").textContent);
        expect(firstUpdate).toEqual(firstResourceUpdate);
        expect(getByTestId("instance-id")).toHaveTextContent("1");

        rerender(<Comp resources={[firstId, secondId]} />);

        const secondUpdate = Number(getByTestId("update").textContent);
        expect(secondUpdate).toBeGreaterThan(firstResourceUpdate);
        expect(secondUpdate).toBeGreaterThan(secondResourceUpdate);
        expect(getByTestId("instance-id")).toHaveTextContent("1");
      });

      it("invalidates when adding older item", () => {
        const firstId = example.ns("3");
        const secondId = example.ns("4");
        let idCounter = 1;
        const opts = ctx.fullCW(firstId);
        const store = opts.lrs.store.getInternalStore().store;

        ctx.fullCW(secondId);
        act(() => {
          store.touch(secondId.value);
        });
        const firstResourceUpdate = store.getStatus(firstId.value).lastUpdate;
        const secondResourceUpdate = store.getStatus(secondId.value).lastUpdate;
        expect(secondResourceUpdate).toBeGreaterThan(firstResourceUpdate);

        const Comp = ({ resources }: { resources: NamedNode[] | undefined }) => {
          const id = React.useRef(idCounter++);
          const lastUpdate = useDataInvalidation(resources);

          return (
            <div>
              <div data-testid="update">{lastUpdate}</div>
              <span data-testid="instance-id">{id.current}</span>
            </div>
          );
        };

        const { getByTestId, rerender } = render(<Comp resources={[secondId]} />, { wrapper: opts.wrapper });

        const firstUpdate = Number(getByTestId("update").textContent);
        expect(firstUpdate).toEqual(secondResourceUpdate);
        expect(getByTestId("instance-id")).toHaveTextContent("1");

        rerender(<Comp resources={[firstId, secondId]} />);

        const secondUpdate = Number(getByTestId("update").textContent);
        expect(secondUpdate).toBeGreaterThan(firstResourceUpdate);
        expect(secondUpdate).toBeGreaterThan(secondResourceUpdate);
        expect(getByTestId("instance-id")).toHaveTextContent("1");
      });
    });
});