mirror of
https://github.com/prymitive/karma
synced 2026-05-07 03:26:52 +00:00
Merge pull request #61 from prymitive/ui-tests
feat(test): more test coverage for labels
This commit is contained in:
6
ui/package-lock.json
generated
6
ui/package-lock.json
generated
@@ -6355,6 +6355,12 @@
|
||||
"p-map": "1.2.0"
|
||||
}
|
||||
},
|
||||
"jest-localstorage-mock": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-localstorage-mock/-/jest-localstorage-mock-2.2.0.tgz",
|
||||
"integrity": "sha512-x+P0vcwr4540bCAYzTEpiD9rs+zh/QZzyiABV+MU6yM2OPwPlrrLyUx/6gValMyt6tg5lX6Z53o2rHWfUht5Xw==",
|
||||
"dev": true
|
||||
},
|
||||
"jest-matcher-utils": {
|
||||
"version": "20.0.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-20.0.3.tgz",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint-plugin-react": "7.11.1",
|
||||
"jest-localstorage-mock": "2.2.0",
|
||||
"markdownlint-cli": "0.13.0",
|
||||
"node-sass-chokidar": "1.3.3",
|
||||
"onchange": "4.1.0",
|
||||
|
||||
@@ -28,7 +28,7 @@ const Fetcher = observer(
|
||||
|
||||
this.interval = newInterval;
|
||||
this.timer = setInterval(
|
||||
() => alertStore.fetch(),
|
||||
() => alertStore.fetchWithThrottle(),
|
||||
this.interval * 1000
|
||||
);
|
||||
}
|
||||
@@ -37,7 +37,7 @@ const Fetcher = observer(
|
||||
componentDidUpdate() {
|
||||
const { alertStore } = this.props;
|
||||
|
||||
alertStore.fetch();
|
||||
alertStore.fetchWithThrottle();
|
||||
|
||||
this.setTimer();
|
||||
}
|
||||
@@ -45,7 +45,7 @@ const Fetcher = observer(
|
||||
componentDidMount() {
|
||||
const { alertStore } = this.props;
|
||||
|
||||
alertStore.fetch();
|
||||
alertStore.fetchWithThrottle();
|
||||
|
||||
this.setTimer();
|
||||
}
|
||||
|
||||
124
ui/src/Components/Fetcher/index.test.js
Normal file
124
ui/src/Components/Fetcher/index.test.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import React from "react";
|
||||
import renderer from "react-test-renderer";
|
||||
|
||||
import { FetchMock, EmptyAPIResponse } from "__mocks__/Fetch";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
|
||||
import { Fetcher } from ".";
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
});
|
||||
|
||||
let alertStore;
|
||||
let settingsStore;
|
||||
|
||||
beforeEach(() => {
|
||||
const response = EmptyAPIResponse();
|
||||
global.fetch = FetchMock(response);
|
||||
|
||||
alertStore = new AlertStore(["label=value"]);
|
||||
settingsStore = new Settings();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllTimers();
|
||||
|
||||
global.fetch.mockRestore();
|
||||
});
|
||||
|
||||
describe("<Fetcher />", () => {
|
||||
it("renders correctly with 'label=value' filter", () => {
|
||||
const tree = renderer
|
||||
.create(<Fetcher alertStore={alertStore} settingsStore={settingsStore} />)
|
||||
.toJSON();
|
||||
|
||||
expect(tree.props["data-filters"]).toBe("label=value");
|
||||
expect(tree.props["data-interval"]).toBe(30);
|
||||
});
|
||||
|
||||
it("re-renders on fetch interval change", () => {
|
||||
const fetcher = renderer.create(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
);
|
||||
|
||||
expect(fetcher.toJSON().props["data-interval"]).toBe(30);
|
||||
settingsStore.fetchConfig.config.interval = 60;
|
||||
expect(fetcher.toJSON().props["data-interval"]).toBe(60);
|
||||
});
|
||||
|
||||
it("re-renders on filters change", () => {
|
||||
const fetcher = renderer.create(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
);
|
||||
|
||||
expect(fetcher.toJSON().props["data-filters"]).toBe("label=value");
|
||||
alertStore.filters.values = [];
|
||||
expect(fetcher.toJSON().props["data-filters"]).toBe("");
|
||||
});
|
||||
|
||||
it("calls alertStore.fetchWithThrottle on mount", () => {
|
||||
const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
|
||||
|
||||
renderer.create(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
);
|
||||
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("calls alertStore.fetchWithThrottle again after interval change", () => {
|
||||
const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
|
||||
|
||||
renderer.create(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
);
|
||||
settingsStore.fetchConfig.config.interval = 60;
|
||||
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("calls alertStore.fetchWithThrottle again after filter change", () => {
|
||||
const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
|
||||
|
||||
renderer.create(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
);
|
||||
alertStore.filters.values = [];
|
||||
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("keeps calling alertStore.fetchWithThrottle after running pending timers", () => {
|
||||
const fetchSpy = jest.spyOn(alertStore, "fetchWithThrottle");
|
||||
|
||||
renderer.create(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(2);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(3);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(4);
|
||||
});
|
||||
|
||||
it("internal timer is armed after render", () => {
|
||||
const fetcher = renderer.create(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
);
|
||||
const instance = fetcher.getInstance();
|
||||
expect(instance.timer).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
it("internal timer is null after unmount", () => {
|
||||
const fetcher = renderer.create(
|
||||
<Fetcher alertStore={alertStore} settingsStore={settingsStore} />
|
||||
);
|
||||
const instance = fetcher.getInstance();
|
||||
instance.componentWillUnmount();
|
||||
expect(instance.timer).toBeNull();
|
||||
});
|
||||
});
|
||||
112
ui/src/Components/Labels/FilteringCounterBadge/index.test.js
Normal file
112
ui/src/Components/Labels/FilteringCounterBadge/index.test.js
Normal file
@@ -0,0 +1,112 @@
|
||||
import React from "react";
|
||||
import renderer from "react-test-renderer";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
|
||||
import { FilteringCounterBadge } from ".";
|
||||
|
||||
let alertStore;
|
||||
|
||||
beforeEach(() => {
|
||||
alertStore = new AlertStore([]);
|
||||
});
|
||||
|
||||
const validateClassName = (value, className) => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<FilteringCounterBadge
|
||||
alertStore={alertStore}
|
||||
name="@state"
|
||||
value={value}
|
||||
counter={1}
|
||||
/>
|
||||
)
|
||||
.toJSON();
|
||||
expect(tree.props.className.split(" ")).toContain(className);
|
||||
};
|
||||
|
||||
const validateStyle = value => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<FilteringCounterBadge
|
||||
alertStore={alertStore}
|
||||
name="@state"
|
||||
value={value}
|
||||
counter={1}
|
||||
/>
|
||||
)
|
||||
.toJSON();
|
||||
expect(tree.props.style).toMatchObject({});
|
||||
};
|
||||
|
||||
const validateOnClick = value => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<FilteringCounterBadge
|
||||
alertStore={alertStore}
|
||||
name="@state"
|
||||
value={value}
|
||||
counter={1}
|
||||
/>
|
||||
)
|
||||
.toJSON();
|
||||
|
||||
tree.props.onClick({ preventDefault: () => {} });
|
||||
|
||||
expect(alertStore.filters.values).toHaveLength(1);
|
||||
expect(alertStore.filters.values).toContainEqual({
|
||||
applied: false,
|
||||
isValid: true,
|
||||
raw: `@state=${value}`,
|
||||
hits: 0,
|
||||
name: "",
|
||||
matcher: "",
|
||||
value: ""
|
||||
});
|
||||
};
|
||||
|
||||
describe("<FilteringCounterBadge />", () => {
|
||||
it("@state=unprocessed counter badge should have className 'badge-secondary'", () => {
|
||||
validateClassName("unprocessed", "badge-secondary");
|
||||
});
|
||||
it("@state=active counter badge should have className 'badge-secondary'", () => {
|
||||
validateClassName("active", "badge-danger");
|
||||
});
|
||||
it("@state=suppressed counter badge should have className 'badge-secondary'", () => {
|
||||
validateClassName("suppressed", "badge-success");
|
||||
});
|
||||
|
||||
it("@state=unprocessed counter badge should have empty style", () => {
|
||||
validateStyle("unprocessed");
|
||||
});
|
||||
it("@state=active counter badge should have empty style", () => {
|
||||
validateStyle("active");
|
||||
});
|
||||
it("@state=suppressed counter badge should have empty style", () => {
|
||||
validateStyle("suppressed");
|
||||
});
|
||||
|
||||
it("counter badge should have correct children based on the counter prop value", () => {
|
||||
const tree = renderer
|
||||
.create(
|
||||
<FilteringCounterBadge
|
||||
alertStore={alertStore}
|
||||
name="@state"
|
||||
value="active"
|
||||
counter={123}
|
||||
/>
|
||||
)
|
||||
.toJSON();
|
||||
expect(tree.children).toEqual(["123"]);
|
||||
});
|
||||
|
||||
it("onClick method on @state=unprocessed counter badge should add a new filter", () => {
|
||||
validateOnClick("unprocessed");
|
||||
});
|
||||
it("onClick method on @state=active counter badge should add a new filter", () => {
|
||||
validateOnClick("active");
|
||||
});
|
||||
it("onClick method on @state=suppressed counter badge should add a new filter", () => {
|
||||
validateOnClick("suppressed");
|
||||
});
|
||||
});
|
||||
@@ -212,7 +212,7 @@ class AlertStore {
|
||||
});
|
||||
});
|
||||
|
||||
fetchWithThrottle = throttle(this.fetch, 500);
|
||||
fetchWithThrottle = throttle(this.fetch, 300);
|
||||
|
||||
parseAPIResponse = action(result => {
|
||||
if (result.error) {
|
||||
@@ -220,18 +220,20 @@ class AlertStore {
|
||||
return;
|
||||
}
|
||||
|
||||
const queryFilters = new Set(
|
||||
this.filters.values
|
||||
.map(f => f.raw)
|
||||
.slice()
|
||||
.sort()
|
||||
);
|
||||
const responseFilters = new Set(result.filters.map(m => m.text).sort());
|
||||
if (
|
||||
JSON.stringify([...queryFilters]) !== JSON.stringify([...responseFilters])
|
||||
) {
|
||||
const queryFilters = [
|
||||
...new Set(
|
||||
this.filters.values
|
||||
.map(f => f.raw)
|
||||
.slice()
|
||||
.sort()
|
||||
)
|
||||
];
|
||||
const responseFilters = [
|
||||
...new Set(result.filters.map(m => m.text).sort())
|
||||
];
|
||||
if (JSON.stringify(queryFilters) !== JSON.stringify(responseFilters)) {
|
||||
console.info(
|
||||
`Got response with filters=${responseFilters} while expecting results for ${queryFilters}, ignoring`
|
||||
`Got response with filters '${responseFilters}' while expecting results for '${queryFilters}', ignoring`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
1
ui/src/setupTests.js
Normal file
1
ui/src/setupTests.js
Normal file
@@ -0,0 +1 @@
|
||||
require("jest-localstorage-mock");
|
||||
Reference in New Issue
Block a user