diff --git a/src/explorer/legacy/App.js b/src/explorer/legacy/App.js index 49cf0e1..e226b40 100644 --- a/src/explorer/legacy/App.js +++ b/src/explorer/legacy/App.js @@ -121,7 +121,7 @@ export function createApp( weightFileManager={weightFileManager} manualWeights={this.state.weights.nodeManualWeights} declarations={[githubDeclaration]} - graph={appState.graph} + graph={appState.timelineCred.graph()} onManualWeightsChange={(addr: NodeAddressT, weight: number) => this.setState(({weights}) => { weights.nodeManualWeights.set(addr, weight); @@ -156,7 +156,7 @@ export function createApp( appState.loading === "LOADING" } onClick={() => - this.stateTransitionMachine.loadGraphAndRunPagerank( + this.stateTransitionMachine.loadTimelineCredAndRunPagerank( this.props.assets, this.state.weights, { diff --git a/src/explorer/legacy/App.test.js b/src/explorer/legacy/App.test.js index b1983d9..ee53c62 100644 --- a/src/explorer/legacy/App.test.js +++ b/src/explorer/legacy/App.test.js @@ -9,23 +9,25 @@ import testLocalStore from "../../webutil/testLocalStore"; import {PagerankTable} from "./pagerankTable/Table"; import {createApp, LoadingIndicator, ProjectDetail} from "./App"; +import {TimelineCred} from "../../analysis/timeline/timelineCred"; +import {defaultParams} from "../../analysis/timeline/params"; require("../../webutil/testUtil").configureEnzyme(); describe("explorer/legacy/App", () => { function example() { let setState, getState; - const loadGraph = jest.fn(); + const loadTimelineCred = jest.fn(); const runPagerank = jest.fn(); - const loadGraphAndRunPagerank = jest.fn(); + const loadTimelineCredAndRunPagerank = jest.fn(); const localStore = testLocalStore(); function createMockSTM(_getState, _setState) { setState = _setState; getState = _getState; return { - loadGraph, + loadTimelineCred, runPagerank, - loadGraphAndRunPagerank, + loadTimelineCredAndRunPagerank, }; } const App = createApp(createMockSTM); @@ -43,9 +45,9 @@ describe("explorer/legacy/App", () => { el, setState, getState, - loadGraph, + loadTimelineCred, runPagerank, - loadGraphAndRunPagerank, + loadTimelineCredAndRunPagerank, localStore, }; } @@ -63,7 +65,13 @@ describe("explorer/legacy/App", () => { type: "READY_TO_RUN_PAGERANK", projectId: "foo/bar", loading: loadingState, - graph: new Graph(), + timelineCred: new TimelineCred( + new Graph(), + [], + new Map(), + defaultParams(), + [] + ), }); }, pagerankEvaluated: (loadingState) => { @@ -71,7 +79,13 @@ describe("explorer/legacy/App", () => { type: "PAGERANK_EVALUATED", projectId: "foo/bar", loading: loadingState, - graph: new Graph(), + timelineCred: new TimelineCred( + new Graph(), + [], + new Map(), + defaultParams(), + [] + ), pagerankNodeDecomposition: new Map(), }); }, @@ -123,7 +137,7 @@ describe("explorer/legacy/App", () => { function testAnalyzeCredButton(stateFn, {disabled}) { const adjective = disabled ? "disabled" : "working"; it(`has a ${adjective} analyze cred button`, () => { - const {el, loadGraphAndRunPagerank, setState} = example(); + const {el, loadTimelineCredAndRunPagerank, setState} = example(); setState(stateFn()); el.update(); const button = el.findWhere( @@ -134,7 +148,7 @@ describe("explorer/legacy/App", () => { } else { expect(button.props().disabled).toBe(false); button.simulate("click"); - expect(loadGraphAndRunPagerank).toBeCalledTimes(1); + expect(loadTimelineCredAndRunPagerank).toBeCalledTimes(1); } }); } diff --git a/src/explorer/legacy/state.js b/src/explorer/legacy/state.js index 1b98df2..f2cbe74 100644 --- a/src/explorer/legacy/state.js +++ b/src/explorer/legacy/state.js @@ -12,6 +12,7 @@ import { type PagerankOptions, pagerank, } from "../../analysis/pagerank"; +import {TimelineCred} from "../../analysis/timeline/timelineCred"; import type {Weights} from "../../analysis/weights"; import {weightsToEdgeEvaluator} from "../../analysis/weightsToEdgeEvaluator"; @@ -38,12 +39,12 @@ export type ReadyToLoadGraph = {| export type ReadyToRunPagerank = {| +type: "READY_TO_RUN_PAGERANK", +projectId: string, - +graph: Graph, + +timelineCred: TimelineCred, +loading: LoadingState, |}; export type PagerankEvaluated = {| +type: "PAGERANK_EVALUATED", - +graph: Graph, + +timelineCred: TimelineCred, +projectId: string, +pagerankNodeDecomposition: PagerankNodeDecomposition, +loading: LoadingState, @@ -57,14 +58,19 @@ export function createStateTransitionMachine( getState: () => AppState, setState: (AppState) => void ): StateTransitionMachine { - return new StateTransitionMachine(getState, setState, doLoadGraph, pagerank); + return new StateTransitionMachine( + getState, + setState, + doLoadTimelineCred, + pagerank + ); } // Exported for testing purposes. export interface StateTransitionMachineInterface { - +loadGraph: (Assets) => Promise; + +loadTimelineCred: (Assets) => Promise; +runPagerank: (Weights, NodeAndEdgeTypes, NodeAddressT) => Promise; - +loadGraphAndRunPagerank: ( + +loadTimelineCredAndRunPagerank: ( Assets, Weights, NodeAndEdgeTypes, @@ -72,13 +78,16 @@ export interface StateTransitionMachineInterface { ) => Promise; } /* In production, instantiate via createStateTransitionMachine; the constructor - * implementation allows specification of the loadGraph and + * implementation allows specification of the loadTimelineCred and * pagerank functions for DI/testing purposes. **/ export class StateTransitionMachine implements StateTransitionMachineInterface { getState: () => AppState; setState: (AppState) => void; - doLoadGraph: (assets: Assets, projectId: string) => Promise; + doLoadTimelineCred: ( + assets: Assets, + projectId: string + ) => Promise; pagerank: ( Graph, EdgeEvaluator, @@ -88,7 +97,10 @@ export class StateTransitionMachine implements StateTransitionMachineInterface { constructor( getState: () => AppState, setState: (AppState) => void, - doLoadGraph: (assets: Assets, projectId: string) => Promise, + doLoadTimelineCred: ( + assets: Assets, + projectId: string + ) => Promise, pagerank: ( Graph, EdgeEvaluator, @@ -97,15 +109,15 @@ export class StateTransitionMachine implements StateTransitionMachineInterface { ) { this.getState = getState; this.setState = setState; - this.doLoadGraph = doLoadGraph; + this.doLoadTimelineCred = doLoadTimelineCred; this.pagerank = pagerank; } /** Loads the graph, reports whether it was successful */ - async loadGraph(assets: Assets): Promise { + async loadTimelineCred(assets: Assets): Promise { const state = this.getState(); if (state.type !== "READY_TO_LOAD_GRAPH") { - throw new Error("Tried to loadGraph in incorrect state"); + throw new Error("Tried to loadTimelineCred in incorrect state"); } const {projectId} = state; const loadingState = {...state, loading: "LOADING"}; @@ -113,10 +125,10 @@ export class StateTransitionMachine implements StateTransitionMachineInterface { let newState: ?AppState; let success = true; try { - const graph = await this.doLoadGraph(assets, projectId); + const timelineCred = await this.doLoadTimelineCred(assets, projectId); newState = { type: "READY_TO_RUN_PAGERANK", - graph, + timelineCred, projectId, loading: "NOT_LOADING", }; @@ -150,7 +162,7 @@ export class StateTransitionMachine implements StateTransitionMachineInterface { ? {...state, loading: "LOADING"} : {...state, loading: "LOADING"}; this.setState(loadingState); - const graph = state.graph; + const graph = state.timelineCred.graph(); let newState: ?AppState; try { const pagerankNodeDecomposition = await this.pagerank( @@ -164,7 +176,7 @@ export class StateTransitionMachine implements StateTransitionMachineInterface { newState = { type: "PAGERANK_EVALUATED", pagerankNodeDecomposition, - graph: state.graph, + timelineCred: state.timelineCred, projectId: state.projectId, loading: "NOT_LOADING", }; @@ -181,7 +193,7 @@ export class StateTransitionMachine implements StateTransitionMachineInterface { } } - async loadGraphAndRunPagerank( + async loadTimelineCredAndRunPagerank( assets: Assets, weights: Weights, types: NodeAndEdgeTypes, @@ -191,8 +203,8 @@ export class StateTransitionMachine implements StateTransitionMachineInterface { const type = state.type; switch (type) { case "READY_TO_LOAD_GRAPH": - const loadedGraph = await this.loadGraph(assets); - if (loadedGraph) { + const loadedTimelineCred = await this.loadTimelineCred(assets); + if (loadedTimelineCred) { await this.runPagerank(weights, types, totalScoreNodePrefix); } break; @@ -206,13 +218,13 @@ export class StateTransitionMachine implements StateTransitionMachineInterface { } } -export async function doLoadGraph( +export async function doLoadTimelineCred( assets: Assets, projectId: string -): Promise { +): Promise { const loadResult = await defaultLoader(assets, projectId); if (loadResult.type !== "SUCCESS") { throw new Error(loadResult); } - return loadResult.timelineCred.graph(); + return loadResult.timelineCred; } diff --git a/src/explorer/legacy/state.test.js b/src/explorer/legacy/state.test.js index 08bfdcc..3c8eb5d 100644 --- a/src/explorer/legacy/state.test.js +++ b/src/explorer/legacy/state.test.js @@ -10,6 +10,8 @@ import type { PagerankNodeDecomposition, PagerankOptions, } from "../../analysis/pagerank"; +import {TimelineCred} from "../../analysis/timeline/timelineCred"; +import {defaultParams} from "../../analysis/timeline/params"; describe("explorer/legacy/state", () => { function example(startingState: AppState) { @@ -18,9 +20,9 @@ describe("explorer/legacy/state", () => { const setState = (appState) => { stateContainer.appState = appState; }; - const loadGraphMock: JestMockFn< + const loadTimelineCredMock: JestMockFn< [Assets, string], - Promise + Promise > = jest.fn(); const pagerankMock: JestMockFn< @@ -30,10 +32,10 @@ describe("explorer/legacy/state", () => { const stm = new StateTransitionMachine( getState, setState, - loadGraphMock, + loadTimelineCredMock, pagerankMock ); - return {getState, stm, loadGraphMock, pagerankMock}; + return {getState, stm, loadTimelineCredMock, pagerankMock}; } function readyToLoadGraph(): AppState { return { @@ -47,14 +49,26 @@ describe("explorer/legacy/state", () => { type: "READY_TO_RUN_PAGERANK", projectId: "foo/bar", loading: "NOT_LOADING", - graph: new Graph(), + timelineCred: new TimelineCred( + new Graph(), + [], + new Map(), + defaultParams(), + [] + ), }; } function pagerankEvaluated(): AppState { return { type: "PAGERANK_EVALUATED", projectId: "foo/bar", - graph: new Graph(), + timelineCred: new TimelineCred( + new Graph(), + [], + new Map(), + defaultParams(), + [] + ), pagerankNodeDecomposition: pagerankNodeDecomposition(), loading: "NOT_LOADING", }; @@ -69,36 +83,43 @@ describe("explorer/legacy/state", () => { return state.loading; } - describe("loadGraph", () => { + describe("loadTimelineCred", () => { it("can only be called when READY_TO_LOAD_GRAPH", async () => { const badStates = [readyToRunPagerank(), pagerankEvaluated()]; for (const b of badStates) { const {stm} = example(b); - await expect(stm.loadGraph(new Assets("/my/gateway/"))).rejects.toThrow( - "incorrect state" - ); + await expect( + stm.loadTimelineCred(new Assets("/my/gateway/")) + ).rejects.toThrow("incorrect state"); } }); it("passes along the projectId", () => { - const {stm, loadGraphMock} = example(readyToLoadGraph()); - expect(loadGraphMock).toHaveBeenCalledTimes(0); + const {stm, loadTimelineCredMock} = example(readyToLoadGraph()); + expect(loadTimelineCredMock).toHaveBeenCalledTimes(0); const assets = new Assets("/my/gateway/"); - stm.loadGraph(assets); - expect(loadGraphMock).toHaveBeenCalledTimes(1); - expect(loadGraphMock).toHaveBeenCalledWith(assets, "foo/bar"); + stm.loadTimelineCred(assets); + expect(loadTimelineCredMock).toHaveBeenCalledTimes(1); + expect(loadTimelineCredMock).toHaveBeenCalledWith(assets, "foo/bar"); }); it("immediately sets loading status", () => { const {getState, stm} = example(readyToLoadGraph()); expect(loading(getState())).toBe("NOT_LOADING"); - stm.loadGraph(new Assets("/my/gateway/")); + stm.loadTimelineCred(new Assets("/my/gateway/")); expect(loading(getState())).toBe("LOADING"); expect(getState().type).toBe("READY_TO_LOAD_GRAPH"); }); it("transitions to READY_TO_RUN_PAGERANK on success", async () => { - const {getState, stm, loadGraphMock} = example(readyToLoadGraph()); - const graph = new Graph(); - loadGraphMock.mockReturnValue(Promise.resolve(graph)); - const succeeded = await stm.loadGraph(new Assets("/my/gateway/")); + const {getState, stm, loadTimelineCredMock} = example(readyToLoadGraph()); + + const timelineCred = new TimelineCred( + new Graph(), + [], + new Map(), + defaultParams(), + [] + ); + loadTimelineCredMock.mockReturnValue(Promise.resolve(timelineCred)); + const succeeded = await stm.loadTimelineCred(new Assets("/my/gateway/")); expect(succeeded).toBe(true); const state = getState(); expect(loading(state)).toBe("NOT_LOADING"); @@ -106,15 +127,15 @@ describe("explorer/legacy/state", () => { if (state.type !== "READY_TO_RUN_PAGERANK") { throw new Error("Impossible"); } - expect(state.graph).toBe(graph); + expect(state.timelineCred).toBe(timelineCred); }); it("sets loading state FAILED on reject", async () => { - const {getState, stm, loadGraphMock} = example(readyToLoadGraph()); + const {getState, stm, loadTimelineCredMock} = example(readyToLoadGraph()); const error = new Error("Oh no!"); // $ExpectFlowError console.error = jest.fn(); - loadGraphMock.mockReturnValue(Promise.reject(error)); - const succeeded = await stm.loadGraph(new Assets("/my/gateway/")); + loadTimelineCredMock.mockReturnValue(Promise.reject(error)); + const succeeded = await stm.loadTimelineCred(new Assets("/my/gateway/")); expect(succeeded).toBe(false); const state = getState(); expect(loading(state)).toBe("FAILED"); @@ -183,69 +204,69 @@ describe("explorer/legacy/state", () => { }); }); - describe("loadGraphAndRunPagerank", () => { + describe("loadTimelineCredAndRunPagerank", () => { it("when READY_TO_LOAD_GRAPH, loads graph then runs pagerank", async () => { const {stm} = example(readyToLoadGraph()); - (stm: any).loadGraph = jest.fn(); + (stm: any).loadTimelineCred = jest.fn(); (stm: any).runPagerank = jest.fn(); - stm.loadGraph.mockResolvedValue(true); + stm.loadTimelineCred.mockResolvedValue(true); const assets = new Assets("/gateway/"); const prefix = NodeAddress.fromParts(["bar"]); const types = defaultTypes(); const wt = defaultWeights(); - await stm.loadGraphAndRunPagerank(assets, wt, types, prefix); - expect(stm.loadGraph).toHaveBeenCalledTimes(1); - expect(stm.loadGraph).toHaveBeenCalledWith(assets); + await stm.loadTimelineCredAndRunPagerank(assets, wt, types, prefix); + expect(stm.loadTimelineCred).toHaveBeenCalledTimes(1); + expect(stm.loadTimelineCred).toHaveBeenCalledWith(assets); expect(stm.runPagerank).toHaveBeenCalledTimes(1); expect(stm.runPagerank).toHaveBeenCalledWith(wt, types, prefix); }); - it("does not run pagerank if loadGraph did not succeed", async () => { + it("does not run pagerank if loadTimelineCred did not succeed", async () => { const {stm} = example(readyToLoadGraph()); - (stm: any).loadGraph = jest.fn(); + (stm: any).loadTimelineCred = jest.fn(); (stm: any).runPagerank = jest.fn(); - stm.loadGraph.mockResolvedValue(false); + stm.loadTimelineCred.mockResolvedValue(false); const assets = new Assets("/gateway/"); const prefix = NodeAddress.fromParts(["bar"]); - await stm.loadGraphAndRunPagerank( + await stm.loadTimelineCredAndRunPagerank( assets, defaultWeights(), defaultTypes(), prefix ); - expect(stm.loadGraph).toHaveBeenCalledTimes(1); + expect(stm.loadTimelineCred).toHaveBeenCalledTimes(1); expect(stm.runPagerank).toHaveBeenCalledTimes(0); }); it("when READY_TO_RUN_PAGERANK, runs pagerank", async () => { const {stm} = example(readyToRunPagerank()); - (stm: any).loadGraph = jest.fn(); + (stm: any).loadTimelineCred = jest.fn(); (stm: any).runPagerank = jest.fn(); const prefix = NodeAddress.fromParts(["bar"]); const wt = defaultWeights(); const types = defaultTypes(); - await stm.loadGraphAndRunPagerank( + await stm.loadTimelineCredAndRunPagerank( new Assets("/gateway/"), wt, types, prefix ); - expect(stm.loadGraph).toHaveBeenCalledTimes(0); + expect(stm.loadTimelineCred).toHaveBeenCalledTimes(0); expect(stm.runPagerank).toHaveBeenCalledTimes(1); expect(stm.runPagerank).toHaveBeenCalledWith(wt, types, prefix); }); it("when PAGERANK_EVALUATED, runs pagerank", async () => { const {stm} = example(pagerankEvaluated()); - (stm: any).loadGraph = jest.fn(); + (stm: any).loadTimelineCred = jest.fn(); (stm: any).runPagerank = jest.fn(); const prefix = NodeAddress.fromParts(["bar"]); const wt = defaultWeights(); const types = defaultTypes(); - await stm.loadGraphAndRunPagerank( + await stm.loadTimelineCredAndRunPagerank( new Assets("/gateway/"), wt, types, prefix ); - expect(stm.loadGraph).toHaveBeenCalledTimes(0); + expect(stm.loadTimelineCred).toHaveBeenCalledTimes(0); expect(stm.runPagerank).toHaveBeenCalledTimes(1); expect(stm.runPagerank).toHaveBeenCalledWith(wt, types, prefix); });