Skip to content

Commit

Permalink
make tests reactive
Browse files Browse the repository at this point in the history
  • Loading branch information
macfarlandian committed Dec 10, 2020
1 parent d2f36be commit 06551f4
Show file tree
Hide file tree
Showing 8 changed files with 99 additions and 21 deletions.
1 change: 1 addition & 0 deletions spotlight-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"assert-never": "^1.2.1",
"jest-fetch-mock": "^3.0.3",
"mobx": "^6.0.4",
"mobx-utils": "^6.0.1",
"react": "^16.13.1",
"react-app-polyfill": "^1.0.6",
"react-dom": "^16.13.1",
Expand Down
20 changes: 18 additions & 2 deletions spotlight-client/src/DataStore/DataStore.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,21 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =============================================================================

import { autorun } from "mobx";
import Tenant from "../contentModels/Tenant";
import RootStore from "./RootStore";

let DataStore: RootStore;

/**
* Convenience method to run an immediate, one-time reactive effect
*/
function reactImmediately(effect: () => void) {
// this will call the effect function immediately,
// and then immediately call the disposer to tear down the reaction
autorun(effect)();
}

beforeEach(() => {
DataStore = new RootStore();
});
Expand All @@ -36,11 +46,17 @@ describe("tenant store", () => {
});

test("has no default tenant", () => {
expect(tenantStore.currentTenant).toBeUndefined();
reactImmediately(() => {
expect(tenantStore.currentTenant).toBeUndefined();
});
expect.hasAssertions();
});

test("can set current tenant", () => {
tenantStore.setCurrentTenant({ tenantId: "US_ND" });
expect(tenantStore.currentTenant).toBeInstanceOf(Tenant);
reactImmediately(() => {
expect(tenantStore.currentTenant).toBeInstanceOf(Tenant);
});
expect.hasAssertions();
});
});
65 changes: 48 additions & 17 deletions spotlight-client/src/contentModels/Metric.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// =============================================================================

import fetchMock from "jest-fetch-mock";
import { when } from "mobx";
import { fromPromise } from "mobx-utils";
import { createMetricMapping } from "./Metric";
import retrieveContent from "../contentApi/retrieveContent";
import { MetricTypeId, MetricTypeIdList } from "../contentApi/types";
Expand Down Expand Up @@ -65,34 +67,63 @@ describe("data fetching", () => {
testMetricMapping = getTestMapping();
});

test.each(MetricTypeIdList)("for metric %s", async (metricId) => {
test.each(MetricTypeIdList)("for metric %s", (metricId, done) => {
expect.hasAssertions();
const metric = getTestMetric(metricId);

await metric.fetch();

// Be advised, these snapshots are huge! However, the only expected failure cases here are:
// 1. you intentionally changed the contents of the fixture in spotlight-api
// 2. you intentionally changed the record format for this Metric type
// Be especially careful inspecting snapshots for Metrics that filter their sources,
// e.g. Parole/Probation metrics. Verify that they use the right rows!
expect(metric.records).toMatchSnapshot();
metric.fetch();

when(
() => metric.records !== undefined,
() => {
// Be advised, these snapshots are huge! However, the only expected failure cases here are:
// 1. you intentionally changed the contents of the fixture in spotlight-api
// 2. you intentionally changed the record format for this Metric type
// Be especially careful inspecting snapshots for Metrics that filter their sources,
// e.g. Parole/Probation metrics. Verify that they use the right rows!
expect(metric.records).toMatchSnapshot();
// @ts-expect-error typedefs for `test.each` are wrong, `done` will be a function
done();
}
);
});
});

test("file loading state", async () => {
test("file loading state", (done) => {
testMetricMapping = getTestMapping();
// not really necessary to test this once per type; we just pick one arbitrarily
const metric = getTestMetric("ParoleSuccessHistorical");

expect(metric.isLoading).toBeUndefined();
let dataPromise: ReturnType<typeof fromPromise>;

const dataPromise = metric.fetch();
expect(metric.isLoading).toBe(true);
expect(metric.records).toBeUndefined();
// this should be the initial state of the metric instance
when(
() => metric.isLoading === undefined,
() => {
expect(metric.records).toBeUndefined();
// the fetch is initiated here; this will trigger the reactions below
dataPromise = fromPromise(metric.fetch());
}
);

when(
() => dataPromise.state === "pending",
() => {
expect(metric.isLoading).toBe(true);
expect(metric.records).toBeUndefined();
}
);

when(
() => dataPromise.state === "fulfilled",
() => {
expect(metric.isLoading).toBe(false);
expect(metric.records).toBeDefined();
done();
}
);

await dataPromise;
expect(metric.isLoading).toBe(false);
expect(metric.records).toBeDefined();
expect.assertions(5);
});

test("fetch error state", async () => {
Expand Down
4 changes: 4 additions & 0 deletions spotlight-client/src/contentModels/Metric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ export default class Metric<RecordFormat extends AnyRecord> {
methodology: false,
name: false,
tenantId: false,
// in practice, collections should not change once we are done
// boostrapping them (which is done right after instantiation);
// no need to make them observable
collections: false,
});

// initialize metadata
Expand Down
11 changes: 11 additions & 0 deletions spotlight-client/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,21 @@

import "react-app-polyfill/ie11";
import "react-app-polyfill/stable";
import { configure } from "mobx";

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

configure({
// make proxies optional for IE 11 support
useProxies: "ifavailable",
// activate runtime linting
computedRequiresReaction: true,
reactionRequiresObservable: true,
observableRequiresReaction: true,
});

ReactDOM.render(
<React.StrictMode>
<App />
Expand Down
11 changes: 10 additions & 1 deletion spotlight-client/src/setupTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,20 @@
// learn more: https://github.com/testing-library/jest-dom

import "@testing-library/jest-dom/extend-expect";

import fetchMock from "jest-fetch-mock";
import { configure } from "mobx";

// we want this mock to be available but disabled by default;
// tests should default to doing real fetches against a /spotlight-api test server
// but can mock it per test to simulate errors, etc
fetchMock.enableMocks();
fetchMock.dontMock();

configure({
// activate runtime linting
computedRequiresReaction: true,
reactionRequiresObservable: true,
observableRequiresReaction: true,
// debug setting to avoid silent failures in reactive code
disableErrorBoundaries: true,
});
3 changes: 2 additions & 1 deletion spotlight-client/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"isolatedModules": true,
"lib": ["dom", "dom.iterable", "esnext"],
"moduleResolution": "node",
"noEmit": true
"noEmit": true,
"useDefineForClassFields": true
},
"include": ["src"]
}
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8977,6 +8977,11 @@ mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
dependencies:
minimist "^1.2.5"

mobx-utils@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/mobx-utils/-/mobx-utils-6.0.1.tgz#bc16c2c62794a48498685f3663e6162d42fb6106"
integrity sha512-0p5xqHDt/arz3cswVcEHYKY/BwL+fifsNroT3Gjkz9lNC7ZDsJm3IuGsmYlvFcVRJRr1rwlFZGuZXrt/MhHEJQ==

mobx@^6.0.4:
version "6.0.4"
resolved "https://registry.yarnpkg.com/mobx/-/mobx-6.0.4.tgz#8fc3e3629a3346f8afddf5bd954411974744dad1"
Expand Down

0 comments on commit 06551f4

Please sign in to comment.