feat(test): more test coverage for AlertStore module

This commit is contained in:
Łukasz Mierzwa
2018-08-21 18:37:25 +01:00
parent 9fe6494830
commit faedbf96e3
2 changed files with 120 additions and 24 deletions

View File

@@ -192,31 +192,32 @@ class AlertStore {
this.filters.setFilters(initialFilters);
}
// fetch is throttled to once per 500ms
fetch = action(
throttle(() => {
this.status.setInProgress();
fetch = action(() => {
this.status.setInProgress();
const alertsURI =
FormatUnseeBackendURI("alerts.json?") +
FormatAPIFilterQuery(this.filters.values.map(f => f.raw));
const alertsURI =
FormatUnseeBackendURI("alerts.json?") +
FormatAPIFilterQuery(this.filters.values.map(f => f.raw));
fetch(alertsURI)
.then(result => result.json())
.then(result => {
this.parseAPIResponse(result);
})
.catch(err =>
this.handleFetchError(
`Request for ${alertsURI} failed with "${err.message}"`
)
return fetch(alertsURI)
.then(result => result.json())
.then(result => {
return this.parseAPIResponse(result);
})
.catch(err => {
console.trace(err);
return this.handleFetchError(
`Request for ${alertsURI} failed with "${err.message}"`
);
}, 500)
);
});
});
fetchWithThrottle = throttle(this.fetch, 500);
parseAPIResponse = action(result => {
if (result.error) {
this.handleFetchError(result.error);
return;
}
const queryFilters = new Set(
@@ -226,7 +227,9 @@ class AlertStore {
.sort()
);
const responseFilters = new Set(result.filters.map(m => m.text).sort());
if (JSON.stringify(queryFilters) !== JSON.stringify(responseFilters)) {
if (
JSON.stringify([...queryFilters]) !== JSON.stringify([...responseFilters])
) {
console.info(
`Got response with filters=${responseFilters} while expecting results for ${queryFilters}, ignoring`
);

View File

@@ -1,3 +1,6 @@
import { ConsoleMock } from "__mocks__/Console";
import { FetchMock, EmptyAPIResponse } from "__mocks__/Fetch";
import {
AlertStore,
AlertStoreStatuses,
@@ -5,6 +8,16 @@ import {
DecodeLocationSearch
} from "Stores/AlertStore";
beforeEach(() => {
// wipe REACT_APP_BACKEND_URI env on each run as it's used by some tests
delete process.env.REACT_APP_BACKEND_URI;
});
afterEach(() => {
// same after each
delete process.env.REACT_APP_BACKEND_URI;
});
describe("AlertStore.status", () => {
it("status is initially idle with no error", () => {
const store = new AlertStore([]);
@@ -116,11 +129,6 @@ describe("AlertStore.filters", () => {
});
describe("FormatUnseeBackendURI", () => {
beforeEach(() => {
// wipe REACT_APP_BACKEND_URI env on each run as it's used by some tests
delete process.env.REACT_APP_BACKEND_URI;
});
it("FormatUnseeBackendURI without REACT_APP_BACKEND_URI env returns ./ prefixed URIs", () => {
const uri = FormatUnseeBackendURI("foo/bar");
expect(uri).toEqual("./foo/bar");
@@ -182,3 +190,88 @@ describe("DecodeLocationSearch", () => {
});
});
});
describe("AlertStore.fetch", () => {
it("parseAPIResponse() rejects a response with mismatched filters", () => {
const consoleSpy = ConsoleMock("info");
const response = EmptyAPIResponse();
const store = new AlertStore([]);
store.parseAPIResponse(response);
expect(store.status.value).toEqual(AlertStoreStatuses.Idle);
// there should be no filters set on AlertStore instance since we started
// with 0 and rejected response with 1 filter
expect(store.filters.values).toHaveLength(0);
// console.info should have been called since we emited a warning
expect(consoleSpy).toHaveBeenCalledTimes(1);
consoleSpy.mockRestore();
});
it("parseAPIResponse() works for a single filter 'label=value'", () => {
const response = EmptyAPIResponse();
const store = new AlertStore(["label=value"]);
store.parseAPIResponse(response);
expect(store.status.value).toEqual(AlertStoreStatuses.Idle);
expect(store.info.version).toBe("fakeVersion");
});
it("fetch() works with valid response", async () => {
const response = EmptyAPIResponse();
global.fetch = FetchMock(response);
const store = new AlertStore(["label=value"]);
await expect(store.fetch()).resolves.toBeUndefined();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(store.status.value).toEqual(AlertStoreStatuses.Idle);
expect(store.info.version).toBe("fakeVersion");
global.fetch.mockRestore();
});
it("fetch() handles response with error correctly", async () => {
global.fetch = jest.fn().mockImplementation(() =>
Promise.resolve({
json: () => ({
error: "Fetch error"
})
})
);
const store = new AlertStore([]);
await expect(store.fetch()).resolves.toBeUndefined();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(store.status.value).toEqual(AlertStoreStatuses.Failure);
expect(store.info.version).toBe("unknown");
global.fetch.mockRestore();
});
it("fetch() handles response that throws an error correctly", async () => {
const consoleSpy = ConsoleMock("trace");
global.fetch = jest.fn().mockImplementation(() =>
Promise.resolve({
json: () => {
throw new Error("Failed fetch");
}
})
);
const store = new AlertStore([]);
await expect(store.fetch()).resolves.toHaveProperty("error");
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(store.status.value).toEqual(AlertStoreStatuses.Failure);
expect(store.info.version).toBe("unknown");
// there should be a trace of the error
expect(consoleSpy).toHaveBeenCalledTimes(1);
consoleSpy.mockRestore();
global.fetch.mockRestore();
});
});