Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve testing with Recoil state #421

Merged
merged 4 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
(<https://github.com/aws/graph-explorer/pull/416>)
- Refactored several graph views for readability
(<https://github.com/aws/graph-explorer/pull/419>)
- Improved testing coverage by having less mocked logic
(<https://github.com/aws/graph-explorer/pull/421>)

## Release 1.7.0

Expand Down
13 changes: 9 additions & 4 deletions packages/graph-explorer/jest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ const config: Config = {
displayName: "graph-explorer",
verbose: true,
preset: "ts-jest",
testEnvironment: "node",
moduleFileExtensions: ["js", "ts", "tsx", "json"],
testEnvironment: "jsdom",
transform: {
"^.+\\.tsx?$": [
"ts-jest",
{
tsconfig: "<rootDir>/../tsconfig.json",
},
],
},
rootDir: "src",
testRegex: ".test.ts$",
setupFilesAfterEnv: ["<rootDir>/utils/testing/setupTests.ts"],
transformIgnorePatterns: [
"node_modules/(?!(swiper|dom7)/)",
"node_modules/(?!(react-dnd-html5-backend)/)",
Expand Down
1 change: 0 additions & 1 deletion packages/graph-explorer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@
"jest-environment-jsdom": "^29.7.0",
"jest-github-actions-reporter": "^1.0.3",
"jest-junit": "^16.0.0",
"jest-localstorage-mock": "^2.4.26",
"lint-staged": "^13.3.0",
"react-test-renderer": "^18.3.1",
"serve": "^14.2.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { act, renderHook } from "@testing-library/react-hooks";
import { RecoilRoot, useRecoilState, useSetRecoilState } from "recoil";
import { act } from "@testing-library/react-hooks";
import { useRecoilValue } from "recoil";
import useEntities from "./useEntities";
import { Vertex } from "../@types/entities";
import {
Expand All @@ -11,6 +11,7 @@ import { schemaAtom } from "../core/StateProvider/schema";
import { activeConfigurationAtom } from "../core/StateProvider/configuration";
import { Schema } from "../core";
import { Entities } from "../core/StateProvider/entitiesSelector";
import renderHookWithRecoilRoot from "../utils/testing/renderHookWithRecoilRoot";

describe("useEntities", () => {
beforeEach(() => {
Expand Down Expand Up @@ -39,15 +40,10 @@ describe("useEntities", () => {
},
};

const { result, waitForNextUpdate } = renderHook(
() => {
const [entities, setEntities] = useEntities({ disableFilters: true });
return { entities, setEntities };
},
{
wrapper: RecoilRoot,
}
);
const { result, waitForNextUpdate } = renderHookWithRecoilRoot(() => {
const [entities, setEntities] = useEntities({ disableFilters: true });
return { entities, setEntities };
});

act(() => {
result.current.setEntities({ nodes: [randomNode], edges: [] });
Expand Down Expand Up @@ -149,15 +145,10 @@ describe("useEntities", () => {
},
];

const { result, waitForNextUpdate } = renderHook(
() => {
const [entities, setEntities] = useEntities({ disableFilters: true });
return { entities, setEntities };
},
{
wrapper: RecoilRoot,
}
);
const { result, waitForNextUpdate } = renderHookWithRecoilRoot(() => {
const [entities, setEntities] = useEntities({ disableFilters: true });
return { entities, setEntities };
});

act(() => {
result.current.setEntities({ nodes: [node1, node2, node3], edges: [] });
Expand Down Expand Up @@ -265,9 +256,7 @@ describe("useEntities", () => {
jest.doMock("../../src/hooks/useEntities", () => useEntitiesMock);

// Render the hook
const { result } = renderHook(() => useEntitiesMock(), {
wrapper: RecoilRoot,
});
const { result } = renderHookWithRecoilRoot(() => useEntitiesMock());

// Since we have mocked useEntitiesMock, it should return the originalEntities immediately
expect(result.current[0]).toEqual(originalEntities);
Expand Down Expand Up @@ -367,15 +356,11 @@ async function setupAndPerformSetEntities(
initialSchema: Schema,
updatedEntities: Entities
) {
const { result, waitForNextUpdate } = renderHook(
const configId = createRandomName("configId");
const { result, waitForNextUpdate } = renderHookWithRecoilRoot(
() => {
const configId = createRandomName("configId");
const [entities, setEntities] = useEntities();
const [schemas, setSchemas] = useRecoilState(schemaAtom);
const setActiveConfigId = useSetRecoilState(activeConfigurationAtom);

setSchemas(prev => prev.set(configId, initialSchema));
setActiveConfigId(configId);
const schemas = useRecoilValue(schemaAtom);
const schema = schemas.get(configId)!;

return {
Expand All @@ -384,8 +369,9 @@ async function setupAndPerformSetEntities(
schema,
};
},
{
wrapper: RecoilRoot,
snapshot => {
snapshot.set(schemaAtom, new Map([[configId, initialSchema]]));
snapshot.set(activeConfigurationAtom, configId);
}
);

Expand Down
57 changes: 32 additions & 25 deletions packages/graph-explorer/src/hooks/useNeighborsOptions.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import "jest-localstorage-mock";
import { renderHook } from "@testing-library/react-hooks";
import { useConfiguration } from "../core";
import useTextTransform from "./useTextTransform";
import useNeighborsOptions from "./useNeighborsOptions";
import { Vertex } from "../@types/entities";

jest.mock("../core/ConfigurationProvider/useConfiguration");
jest.mock("./useTextTransform");

jest.mock("../core/ConfigurationProvider/ConfigurationProvider.tsx", () => ({
ConfigurationProvider: ({ children }: any) => children,
}));

jest.mock("../core/ConnectedProvider/ConnectedProvider.tsx", () => ({
ConnectedProvider: ({ children }: any) => children,
}));
import renderHookWithRecoilRoot from "../utils/testing/renderHookWithRecoilRoot";
import {
activeConfigurationAtom,
configurationAtom,
} from "../core/StateProvider/configuration";
import { createRandomRawConfiguration } from "../utils/testing/randomData";

describe("useNeighborsOptions", () => {
const vertex = {
Expand All @@ -25,18 +16,34 @@ describe("useNeighborsOptions", () => {
} as unknown as Vertex;

it("should return neighbors options correctly", () => {
(useConfiguration as jest.Mock).mockReturnValue({
getVertexTypeConfig: (type: any) => {
return { displayLabel: `Label ${type}` };
},
});
(useTextTransform as jest.Mock).mockReturnValue((str: string) =>
str.toUpperCase()
const { result } = renderHookWithRecoilRoot(
() => useNeighborsOptions(vertex),
snapshot => {
const config = createRandomRawConfiguration();
config.schema!.vertices = [
{
type: "nodeType1",
displayLabel: "Label nodeType1",
attributes: [],
},
{
type: "nodeType2",
displayLabel: "Label nodeType2",
attributes: [],
},
];
snapshot.set(configurationAtom, new Map([[config.id, config]]));
snapshot.set(activeConfigurationAtom, config.id);
}
);

const { result } = renderHook(() => useNeighborsOptions(vertex));

expect(result.current).toEqual([
expect(
result.current.map(option => ({
...option,
// We only care about verifying the displayLabel property
config: { displayLabel: option.config?.displayLabel },
}))
).toEqual([
{
label: "Label nodeType1",
value: "nodeType1",
Expand Down
86 changes: 35 additions & 51 deletions packages/graph-explorer/src/hooks/useTextTransform.test.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import "jest-localstorage-mock";
import { MutableSnapshot } from "recoil";
import {
activeConfigurationAtom,
configurationAtom,
} from "../core/StateProvider/configuration";
import { createRandomRawConfiguration } from "../utils/testing/randomData";
import renderHookWithRecoilRoot from "../utils/testing/renderHookWithRecoilRoot";
import useTextTransform from "./useTextTransform";
import { renderHook } from "@testing-library/react-hooks";
import { useConfiguration } from "../core";

jest.mock("../core/ConnectedProvider/ConnectedProvider.tsx", () => ({
ConnectedProvider: ({ children }: any) => children,
}));
function initializeConfigWithPrefix(snapshot: MutableSnapshot) {
// Create config and setup schema
const config = createRandomRawConfiguration();
config.connection!.queryEngine = "sparql";
config.schema!.prefixes = [
{
prefix: "rdf",
uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
},
];
snapshot.set(configurationAtom, new Map([[config.id, config]]));

jest.mock("../core", () => ({
__esModule: true,
useConfiguration: jest.fn(),
}));
// Make config active
snapshot.set(activeConfigurationAtom, config.id);
}

describe("useTextTransform", () => {
beforeEach(() => {
Expand All @@ -19,20 +31,10 @@ describe("useTextTransform", () => {
it("should replace prefixes in URIs", () => {
const text = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
const expected = "rdf:type";
(useConfiguration as jest.Mock).mockReturnValue({
connection: {
queryEngine: "sparql",
},
schema: {
prefixes: [
{
prefix: "rdf",
uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
},
],
},
});
const { result } = renderHook(() => useTextTransform());
const { result } = renderHookWithRecoilRoot(
() => useTextTransform(),
initializeConfigWithPrefix
);
expect(result.current(text)).toEqual(expected);
});

Expand Down Expand Up @@ -65,39 +67,21 @@ describe("useTextTransform", () => {
// Boundary cases
it("should return original input if it's a URI not in schema.prefixes", () => {
const input = "http://www.some-uri.com/";
(useConfiguration as jest.Mock).mockReturnValue({
connection: {
queryEngine: "sparql",
},
schema: {
prefixes: [
{
prefix: "rdf",
uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
},
],
},
});
const { result } = renderHook(() => useTextTransform());
const { result } = renderHookWithRecoilRoot(
() => useTextTransform(),
initializeConfigWithPrefix
);
expect(result.current(input)).toBe(input);
});

it("should return original input if the connection.queryEngine is 'sparql' and input doesn't contain a URI", () => {
const input = "Some Text Without URI";
(useConfiguration as jest.Mock).mockReturnValue({
connection: {
queryEngine: "sparql",
},
schema: {
prefixes: [
{
prefix: "rdf",
uri: "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
},
],
},
});
const { result } = renderHook(() => useTextTransform());

const { result } = renderHookWithRecoilRoot(
() => useTextTransform(),
initializeConfigWithPrefix
);

expect(result.current(input)).toBe(input);
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
import { expect, jest } from "@jest/globals";
import { act, renderHook } from "@testing-library/react-hooks";
import { act } from "@testing-library/react-hooks";
import useFiltersConfig from "./useFiltersConfig";
import { useTestSchema } from "../../utils/testing/useTestSchema";
import { createRandomSchema } from "../../utils/testing/randomData";
import { TestableRootProviders } from "../../utils/testing/TestableRootProviders";
import {
createRandomRawConfiguration,
createRandomSchema,
} from "../../utils/testing/randomData";
import { sample, sortBy } from "lodash";
import { Schema } from "../../core";
import renderHookWithRecoilRoot from "../../utils/testing/renderHookWithRecoilRoot";
import {
activeConfigurationAtom,
configurationAtom,
} from "../../core/StateProvider/configuration";

/** Creates a config with the schema and makes it active, then renders the `useFiltersConfig` hook. */
function renderFilterConfigHook(schema: Schema) {
return renderHook(
() => {
useTestSchema(schema);
return useFiltersConfig();
},
{
wrapper: TestableRootProviders,
return renderHookWithRecoilRoot(
() => useFiltersConfig(),
snapshot => {
// Initialize the configuration atom with a test config
const config = createRandomRawConfiguration();
config.schema = schema;
snapshot.set(configurationAtom, new Map([[config.id, config]]));
snapshot.set(activeConfigurationAtom, config.id);
}
);
}
Expand Down
19 changes: 19 additions & 0 deletions packages/graph-explorer/src/setupTests.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
/*
* Global Jest setup for all tests
*/

// Sets up `fetch` for JSDom environment.
// https://github.com/jsdom/jsdom/issues/1724#issuecomment-720727999
import "whatwg-fetch";

// Mock the env module
jest.mock("./utils/env", () => {
return {
DEV: true,
PROD: false,
};
});

// Mock localforage
jest.mock("localforage", () => ({
config: jest.fn(),
getItem: jest.fn(),
setItem: jest.fn(),
}));
Loading
Loading