From e9be75fa6e25e50c24dd0a78349fbbaa0ebd33c5 Mon Sep 17 00:00:00 2001 From: Guy Carmeli Date: Tue, 19 Mar 2024 21:48:55 +0200 Subject: [PATCH] Fix issues while mocking graphs in tests (#134) Fix issues while mocking graphs in tests This PR fixes two issues that happened only while mocking graphs: 1. Requiring a singleton graph before it was mocked resulted in Obsidian always using the original graph instead of the mock 2. Obsidian's graph registry, which is intended to be a singleton, was instantiated multiple times. Apparently this was caused by issues with Jest/Node compatibility --- package.json | 4 +-- src/graph/registry/GraphRegistry.ts | 21 ++++++++++++++- test/integration/mockingGraphs.test.tsx | 34 ++++++++++++++++++++++++- testkit/mockGraphs.ts | 10 ++++++++ 4 files changed, 65 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 92835f59..0a9ec6ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-obsidian", - "version": "2.2.0", + "version": "2.3.0-rc.1", "description": "Dependency injection framework for React and React Native applications", "scripts": { "prepack": "npm run lint && tsc --project tsconfig.prod.json", @@ -53,7 +53,7 @@ "eslint-plugin-local-rules": "^1.3.2", "eslint-plugin-react": "^7.26.1", "eslint-plugin-react-hooks": "^4.2.0", - "eslint-plugin-unused-imports": "2.x.x", + "eslint-plugin-unused-imports": "3.1.x", "husky": "8.0.1", "jest": "29.5.x", "jest-create-mock-instance": "2.0.0", diff --git a/src/graph/registry/GraphRegistry.ts b/src/graph/registry/GraphRegistry.ts index c8a2b106..2656bb9c 100644 --- a/src/graph/registry/GraphRegistry.ts +++ b/src/graph/registry/GraphRegistry.ts @@ -70,6 +70,22 @@ export class GraphRegistry { return Reflect.getMetadata('isLifecycleBound', Graph) ?? false; } + clearGraphAfterItWasMockedInTests(graphName: string) { + const graphNames = this.nameToInstance.keys(); + for (const name of graphNames) { + if (name.match(graphName)) { + const graph = this.nameToInstance.get(name); + if (!graph) return; + const Graph = this.instanceToConstructor.get(graph); + if (!Graph) return; + + this.instanceToConstructor.delete(graph); + this.constructorToInstance.get(Graph)!.delete(graph); + this.nameToInstance.delete(graph.name); + } + } + } + clear(graph: Graph) { const Graph = this.instanceToConstructor.get(graph); if (!Graph || this.isSingleton(Graph)) return; @@ -93,4 +109,7 @@ export class GraphRegistry { } } -export default new GraphRegistry(); +// @ts-ignore +global.graphRegistry = global.graphRegistry || new GraphRegistry(); +// @ts-ignore +export default global.graphRegistry as GraphRegistry; diff --git a/test/integration/mockingGraphs.test.tsx b/test/integration/mockingGraphs.test.tsx index ed767909..568c0ea9 100644 --- a/test/integration/mockingGraphs.test.tsx +++ b/test/integration/mockingGraphs.test.tsx @@ -1,8 +1,14 @@ import React from 'react'; import { render } from '@testing-library/react'; +import SingletonGraph from '../fixtures/SingletonGraph'; import MainGraph from '../fixtures/MainGraph'; import Subgraph from '../fixtures/Subgraph'; -import { Graph, Provides } from '../../src'; +import { + Graph, + Obsidian, + Provides, + Singleton, +} from '../../src'; import InjectedComponent from '../fixtures/InjectedComponent'; import { mockGraphs } from '../../testkit'; @@ -23,6 +29,32 @@ describe('Test doubles', () => { expect(container.textContent).toBe('MockedContent'); }); + it('Mocks graphs when using Obsidian.obtain', () => { + const stringToCompare = Obsidian.obtain(MainGraph).someString(); + expect(stringToCompare).toBe('Mocked'); + }); + + it('Mocks graphs when using Obsidian.obtain on a Singleton graph' + + 'even after the singleton graph was already registered in graph registry', () => { + registerSingletonGraphBeforeMocking(); + + const stringToCompare = Obsidian.obtain(SingletonGraph).instanceId(); + expect(stringToCompare).toBe('MockSingleton'); + }); + + function registerSingletonGraphBeforeMocking() { + Obsidian.obtain(SingletonGraph); + mockGraphs({ SingletonGraph: MockSingletonGraph }); + } + + @Singleton() @Graph() + class MockSingletonGraph extends SingletonGraph { + @Provides() + override instanceId(): string { + return 'MockSingleton'; + } + } + @Graph() class MockMainGraph extends MainGraph { @Provides() diff --git a/testkit/mockGraphs.ts b/testkit/mockGraphs.ts index 109f9b90..bbdef655 100644 --- a/testkit/mockGraphs.ts +++ b/testkit/mockGraphs.ts @@ -16,5 +16,15 @@ export function mockGraphs( return resolveChain.proceed(Graph, props); } }(); + + clearRegisteredGraphs(graphNameToGraph); graphRegistry.addGraphMiddleware(graphMiddleware); } + +function clearRegisteredGraphs( + graphNameToGraph: Record | ((props: any) => ObjectGraph)>, +) { + for (const graphName of Object.keys(graphNameToGraph)) { + graphRegistry.clearGraphAfterItWasMockedInTests(graphName); + } +}