Skip to content

Commit b7ba138

Browse files
soujaycquirosj
andauthored
Add custom checks acceptance tests (#2216)
* Testing * raw testing of mock data Frontend custom checks * dynamic precondition function hasCustomChecks * No data custom checks tests * wip Failing custom checks tests * All custom checks are in a failed state TODOs * precondition.hasCustomChecks params rename * WIP All custom checks are in a failed state asserts * Revert "WIP All custom checks are in a failed state asserts" This reverts commit dca2eb7. * Revert "precondition.hasCustomChecks params rename" This reverts commit a337eb2. * Revert "All custom checks are in a failed state TODOs" This reverts commit 44edfad. * Revert "wip Failing custom checks tests" This reverts commit 300bfd1. * Revert "No data custom checks tests" This reverts commit fcfd091. * Revert "dynamic precondition function hasCustomChecks" This reverts commit 07ade85. * wip : add mocks for custom checks data, add test to check for "no failed custom checks message" - * add driver set up for servicecontrolwithmonitoring to fix the tests * refactor files and code * add a template for custom check items and use it for tests * use mockEndpointDynamic to add customchecks url by querystring * remove extra space * added some console.log for testing * Update src/Frontend/test/specs/customchecks/viewing-no-data.spec.ts Co-authored-by: Christian <[email protected]> * Update src/Frontend/test/specs/customchecks/viewing-no-data.spec.ts Co-authored-by: Christian <[email protected]> * add test for no custom check data scenario * set up mock data for custom check and write tests for empty and failed custom check listing * add no data and failing cutom checks * fix custom check pagination test * generate custom check items * add test for custom checks to be displayed in descending order of last checked on the custom checks tab * Added the following tests- When a custom check fails, the custom checks tab is auto-refreshed with the new failed custom check, A failing custom check that begins passing is auto-refreshed and removed from the list on the custom checks tab * test for dismissing a custom check * add test for dismissbutton * refactor code --------- Co-authored-by: Christian <[email protected]> Co-authored-by: Christian <[email protected]>
1 parent 91fdf30 commit b7ba138

File tree

13 files changed

+438
-128
lines changed

13 files changed

+438
-128
lines changed

src/Frontend/src/components/customchecks/CustomCheckView.vue

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,27 @@ const store = useCustomChecksStore();
1111
<template>
1212
<div class="row box box-warning box-no-click">
1313
<div class="col-sm-12 no-side-padding">
14-
<div class="custom-check-row">
14+
<div class="custom-check-row" role="row" aria-label="custom-check-failed-row">
1515
<div class="custom-check-row-detail">
1616
<div class="row box-header">
1717
<div class="col-sm-12 no-side-padding">
18-
<p class="lead">{{ customCheck.failure_reason }}</p>
18+
<p class="lead" role="note" aria-label="custom-check-failed-reason">{{ customCheck.failure_reason }}</p>
1919
<div class="row">
2020
<div class="col-sm-12 no-side-padding">
2121
<p class="metadata">
2222
<span class="metadata"><i aria-hidden="true" class="fa fa-check"></i> Check: {{ customCheck.custom_check_id }}</span>
2323
<span class="metadata"><i aria-hidden="true" class="fa fa-list"></i> Category: {{ customCheck.category }}</span>
2424
<span class="metadata"><i aria-hidden="true" class="fa pa-endpoint"></i> Endpoint: {{ customCheck.originating_endpoint.name }}</span>
2525
<span class="metadata"><i aria-hidden="true" class="fa fa-server"></i> Host: {{ customCheck.originating_endpoint.host }}</span>
26-
<span class="metadata"><i aria-hidden="true" class="fa fa-clock-o"></i> Last checked: <TimeSince :date-utc="customCheck.reported_at"></TimeSince></span>
26+
<span class="metadata"><i aria-hidden="true" class="fa fa-clock-o"></i> Last checked: <TimeSince :date-utc="customCheck.reported_at" role="note" aria-label="custom-check-reported-date"></TimeSince></span>
2727
</p>
2828
</div>
2929
</div>
3030
</div>
3131
</div>
3232
</div>
3333
<div>
34-
<button type="button" class="btn btn-default" title="Dismiss this custom check so it doesn't show up as an alert" @click="store.dismissCustomCheck(customCheck.id)">Dismiss</button>
34+
<button type="button" class="btn btn-default" title="Dismiss this custom check so it doesn't show up as an alert" role="button" aria-label="custom-check-dismiss" @click="store.dismissCustomCheck(customCheck.id)">Dismiss</button>
3535
</div>
3636
</div>
3737
</div>

src/Frontend/src/components/heartbeats/serviceControlWithHeartbeats.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const serviceControlWithHeartbeats = async ({ driver }: SetupFactoryOptio
66
await driver.setUp(precondition.hasUpToDateServicePulse);
77
await driver.setUp(precondition.hasUpToDateServiceControl);
88
await driver.setUp(precondition.errorsDefaultHandler);
9-
await driver.setUp(precondition.hasNoFailingCustomChecks);
9+
await driver.setUp(precondition.hasCustomChecksEmpty);
1010
await driver.setUp(precondition.hasEventLogItems);
1111
await driver.setUp(precondition.hasServiceControlMainInstance(minimumSCVersionForEndpointSettings));
1212
await driver.setUp(precondition.hasNoDisconnectedEndpoints);

src/Frontend/src/views/CustomChecksView.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ const { pageNumber, failingCount, failedChecks } = storeToRefs(store);
1919
</div>
2020

2121
<section name="custom_checks">
22-
<NoData v-if="failingCount === 0" message="No failed custom checks" />
23-
<div v-else class="row">
22+
<NoData v-if="failingCount === 0" message="No failed custom checks" role="note" aria-label="customcheck-message" />
23+
<div v-else class="row" role="table" aria-label="custom-check-list">
2424
<div class="col-sm-12">
2525
<CustomCheckView v-for="item of failedChecks" :key="item.id" :custom-check="item" />
2626
<div class="row">
27-
<PaginationStrip :items-per-page="10" :total-count="failingCount" v-model="pageNumber" />
27+
<PaginationStrip :items-per-page="10" :total-count="failingCount" v-model="pageNumber" role="row" aria-label="custom-check-pagination" />
2828
</div>
2929
</div>
3030
</div>

src/Frontend/src/views/throughputreport/serviceControlWithThroughput.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ export const serviceControlWithThroughput = async ({ driver }: SetupFactoryOptio
66
await driver.setUp(precondition.hasUpToDateServicePulse);
77
await driver.setUp(precondition.hasUpToDateServiceControl);
88
await driver.setUp(precondition.errorsDefaultHandler);
9-
await driver.setUp(precondition.hasNoFailingCustomChecks);
9+
await driver.setUp(precondition.hasCustomChecksEmpty);
1010
await driver.setUp(precondition.hasEventLogItems);
1111
await driver.setUp(precondition.hasNoHeartbeatsEndpoints);
1212
await driver.setUp(precondition.hasServiceControlMainInstance(minimumSCVersionForThroughput));

src/Frontend/test/mocks/browser.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ const driver = makeDriver();
2525
(async () => {
2626
await driver.setUp(precondition.serviceControlWithMonitoring);
2727
//override the default mocked endpoints with a custom list
28+
await driver.setUp(precondition.hasCustomChecks(3, 2));
29+
2830
await driver.setUp(
2931
precondition.monitoredEndpointsNamed([
3032
"Universe.Solarsystem.Mercury.Endpoint1",
@@ -35,7 +37,6 @@ const driver = makeDriver();
3537
"Universe.Solarsystem.Earth.Endpoint6",
3638
])
3739
);
38-
3940
await driver.setUp(
4041
precondition.hasFailedMessage({
4142
withGroupId: "81dca64e-76fc-e1c3-11a2-3069f51c58c8",
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
import CustomCheck, { Status } from "@/resources/CustomCheck";
2+
import { SetupFactoryOptions } from "../driver";
3+
const emptyContent = JSON.stringify([]);
4+
5+
export const hasCustomChecksEmpty = ({ driver }: SetupFactoryOptions) => {
6+
const serviceControlInstanceUrl = window.defaultConfig.service_control_url;
7+
driver.mockEndpoint(`${serviceControlInstanceUrl}customchecks`, {
8+
body: emptyContent,
9+
headers: {
10+
"Total-Count": "0", //count of failing custom checks
11+
},
12+
});
13+
};
14+
15+
const generateGuid = () => {
16+
return crypto.randomUUID();
17+
};
18+
const customCheckTemplate = <CustomCheck>{
19+
id: "customchecks/6131fa95-9414-1898-9c83-c5b18587945b",
20+
custom_check_id: "SampleCustomeCheck",
21+
category: "SomeCategory",
22+
status: "Pass",
23+
failure_reason: "I don't know the reason",
24+
reported_at: "2025-01-10T05:06:30.4074087Z",
25+
originating_endpoint: {
26+
name: "EndpointX",
27+
host_id: "ff605b55-6fbb-af56-5753-73c1ff73e601",
28+
host: "ABC",
29+
},
30+
};
31+
32+
export const hasCustomChecks =
33+
(failingCount: number, passingCount: number) =>
34+
({ driver }: SetupFactoryOptions) => {
35+
const customChecksData = generateCustomChecksData(failingCount, passingCount)();
36+
37+
// Call getCustomChecks to mock the endpoints with the generated data
38+
getCustomChecks(customChecksData)({ driver });
39+
};
40+
41+
export const generateCustomChecksData = (failingCount: number, passingCount: number) => () => {
42+
// Calculate total count
43+
const totalCount = failingCount + passingCount;
44+
45+
// Create checks (both failing and passing)
46+
const customChecks = Array.from({ length: totalCount }).map((_, index) => {
47+
// Generate the date based on the index
48+
const date = new Date();
49+
date.setDate(date.getDate() - index); // Subtract `index` days from the current date
50+
const reportedAt = date.toISOString(); // Convert to ISO string format
51+
52+
// Determine status and failure reason
53+
const status = index < failingCount ? Status.Fail : Status.Pass;
54+
const failureReason = status === Status.Fail ? `configured to fail on endpoint ${index}` : "";
55+
56+
// Generate a new GUID for the ID and host_id
57+
const newGuid = generateGuid();
58+
const originatingEndpointName = `endpoint ${index}`;
59+
const originatingHost = `ABC ${index}`;
60+
const customCategory = `Some Category ${index}`;
61+
const customeCheckId = `SampleCustomeCheck ${index}`;
62+
63+
return {
64+
...customCheckTemplate,
65+
id: `customchecks/${newGuid}`, // New GUID for ID
66+
category: customCategory,
67+
custom_check_id: customeCheckId,
68+
status, // Fail or Pass based on index
69+
failure_reason: failureReason, // Failure reason or empty for passing
70+
reported_at: reportedAt, // Autogenerated reported_at based on index
71+
originating_endpoint: {
72+
name: originatingEndpointName, // Endpoint name based on index
73+
host_id: newGuid, // New GUID for host_id
74+
host: originatingHost, // Host name based on index
75+
},
76+
};
77+
});
78+
return customChecks;
79+
};
80+
81+
export const getCustomChecks =
82+
(customChecks: CustomCheck[]) =>
83+
({ driver }: SetupFactoryOptions) => {
84+
const serviceControlInstanceUrl = window.defaultConfig.service_control_url;
85+
86+
const failedCustomChecks = customChecks.filter((check) => check.status === "Fail");
87+
88+
driver.mockEndpointDynamic(`${serviceControlInstanceUrl}customchecks`, "get", (url) => {
89+
const status = url.searchParams.get("status");
90+
if (status === "fail") {
91+
return Promise.resolve({
92+
body: failedCustomChecks,
93+
headers: { "Total-Count": failedCustomChecks.length.toString() },
94+
});
95+
}
96+
97+
return Promise.resolve({
98+
body: customChecks,
99+
headers: { "Total-Count": customChecks.length.toString() },
100+
});
101+
});
102+
driver.mockEndpointDynamic(`${serviceControlInstanceUrl}customchecks/:id`, "delete", (url, params) => {
103+
const status = url.searchParams.get("status");
104+
console.log(status + ", " + params.id);
105+
106+
return Promise.resolve({
107+
body: { message: "Successfully deleted" },
108+
status: 200,
109+
headers: { "Content-Type": "application/json" },
110+
});
111+
});
112+
};
113+
114+
export const updateCustomCheckItemByStatus = (data: CustomCheck[], statusToUpdate: string) => {
115+
const itemToUpdate = data.find((item) => item.status === statusToUpdate);
116+
117+
if (itemToUpdate) {
118+
if (statusToUpdate === "Pass") {
119+
itemToUpdate.status = Status.Fail;
120+
itemToUpdate.failure_reason = "Some reason I dont know";
121+
} else {
122+
itemToUpdate.status = Status.Pass;
123+
itemToUpdate.failure_reason = "";
124+
}
125+
}
126+
};
127+
export const updateCustomCheckItemByItem = (data: CustomCheck[], itemToUpdate: CustomCheck, statusToUpdate: string) => {
128+
const itemFound = data.find((item) => item.id === itemToUpdate.id);
129+
if (itemFound != null) {
130+
if (statusToUpdate === "Fail") {
131+
itemToUpdate.status = Status.Fail;
132+
itemToUpdate.failure_reason = "Some reason I dont know";
133+
} else {
134+
itemToUpdate.status = Status.Pass;
135+
itemToUpdate.failure_reason = "";
136+
}
137+
}
138+
};

src/Frontend/test/preconditions/hasNoFailingCustomChecks.ts

Lines changed: 0 additions & 11 deletions
This file was deleted.

src/Frontend/test/preconditions/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export { hasServiceControlMonitoringInstanceUrl } from "../preconditions/hasServ
55
export { hasUpToDateServiceControl } from "../preconditions/hasUpToDateServiceControl";
66
export { hasUpToDateServicePulse } from "../preconditions/hasUpToDateServicePulse";
77
export { errorsDefaultHandler } from "../preconditions/hasNoErrors";
8-
export { hasNoFailingCustomChecks } from "../preconditions/hasNoFailingCustomChecks";
8+
export { hasCustomChecksEmpty, hasCustomChecks, generateCustomChecksData, getCustomChecks, updateCustomCheckItemByStatus, updateCustomCheckItemByItem } from "./customChecks";
99
export { hasNoDisconnectedEndpoints } from "../preconditions/hasNoDisconnectedEndpoints";
1010
export { hasNoMonitoredEndpoints, hasMonitoredEndpointsList, monitoredEndpointsNamed } from "../preconditions/hasMonitoredEndpoints";
1111
export { hasEventLogItems } from "../preconditions/hasEventLogItems";

src/Frontend/test/preconditions/serviceControlWithMonitoring.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const serviceControlWithMonitoring = async ({ driver }: SetupFactoryOptio
2323
await driver.setUp(precondition.errorsDefaultHandler);
2424

2525
//http://localhost:33333/api/customchecks
26-
await driver.setUp(precondition.hasNoFailingCustomChecks);
26+
await driver.setUp(precondition.hasCustomChecksEmpty);
2727

2828
//http://localhost:33633/monitored-endpoints/disconnected
2929
await driver.setUp(precondition.hasNoDisconnectedEndpoints);
Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,85 @@
11
import { test, describe } from "../../drivers/vitest/driver";
2+
import { expect } from "vitest";
3+
import * as precondition from "../../preconditions";
4+
import { customChecksListElement, customChecksDismissButtonList, customChecksFailedRowsList } from "./questions/failedCustomChecks";
5+
import { waitFor } from "@testing-library/vue";
6+
import userEvent from "@testing-library/user-event";
27

38
describe("FEATURE: Dismiss custom checks", () => {
49
describe("RULE: Dismiss button should be visible", () => {
5-
test.todo("EXAMPLE: Dismiss button should be visible on each failing custom check");
10+
test("EXAMPLE: Dismiss button is visible on each failing custom check", async ({ driver }) => {
11+
const failingCustomCheckCount = 4;
12+
const passingCustomCheckCount = 2;
613

7-
/* SCENARIO
8-
Given 2 failing custom checks
9-
And the custom checks page is open
10-
Then each should render a dismiss button
11-
*/
14+
await driver.setUp(precondition.serviceControlWithMonitoring);
15+
await driver.setUp(precondition.hasCustomChecks(failingCustomCheckCount, passingCustomCheckCount));
16+
17+
await driver.goTo("/custom-checks");
18+
19+
await waitFor(async () => {
20+
expect(await customChecksListElement()).toBeInTheDocument();
21+
});
22+
23+
await waitFor(async () => {
24+
expect(await customChecksDismissButtonList()).toHaveLength(failingCustomCheckCount); //count of dismiss button
25+
});
26+
});
1227
});
28+
1329
describe("RULE: Dismissing a custom check should remove from the list", () => {
14-
test.todo("EXAMPLE: The dismiss button should remove the custom check from the list when clicked");
30+
test("EXAMPLE: The dismiss button removes the custom check from the list when clicked", async ({ driver }) => {
31+
const failingCustomCheckCount = 4;
32+
const passingCustomCheckCount = 2;
33+
34+
await driver.setUp(precondition.serviceControlWithMonitoring);
35+
await driver.setUp(precondition.hasCustomChecks(failingCustomCheckCount, passingCustomCheckCount));
36+
37+
await driver.goTo("/custom-checks");
38+
39+
await waitFor(async () => {
40+
expect(await customChecksListElement()).toBeInTheDocument(); //failed list is visisble
41+
expect(await customChecksFailedRowsList()).toHaveLength(failingCustomCheckCount); //count of failed checks matches failing count set
42+
});
43+
44+
let dismissButtonList = await customChecksDismissButtonList();
45+
expect(dismissButtonList).toHaveLength(failingCustomCheckCount); //count of dismiss button matches the failed custom check count
1546

16-
/* SCENARIO
17-
Given 2 failing custom checks
18-
When the dismiss button is clicked
19-
Then the dismissed custom check should be removed from the list
20-
*/
47+
//click the dismiss button
48+
await userEvent.click(dismissButtonList[0]);
49+
50+
//get the new dismiss button list
51+
dismissButtonList = await customChecksDismissButtonList();
52+
expect(dismissButtonList).toHaveLength(failingCustomCheckCount - 1); //count of dismss button is decreased by 1
53+
});
2154
});
2255
describe("RULE: Failing after a dismiss should cause the failed check to reappear", () => {
23-
test.todo("EXAMPLE: Dismissed custom check should reappear in the list when it fails");
24-
25-
/* SCENARIO
26-
Given 2 failed custom checks
27-
And one of them is dismissed
28-
When the dismissed custom check fails
29-
Then the custom check should appear in the list
30-
*/
56+
test("EXAMPLE: Dismissed custom check should reappear in the list when it fails", async ({ driver }) => {
57+
const failingCustomCheckCount = 4;
58+
const passingCustomCheckCount = 0;
59+
60+
await driver.setUp(precondition.serviceControlWithMonitoring);
61+
62+
const customCheckItems = precondition.generateCustomChecksData(failingCustomCheckCount, passingCustomCheckCount)();
63+
await driver.setUp(precondition.getCustomChecks(customCheckItems));
64+
await driver.goTo("/custom-checks");
65+
66+
await waitFor(async () => {
67+
expect(await customChecksListElement()).toBeInTheDocument(); //failed list is visisble
68+
expect(await customChecksFailedRowsList()).toHaveLength(failingCustomCheckCount); //count of failed checks matches failing count set
69+
});
70+
71+
const dismissButtonList = await customChecksDismissButtonList();
72+
expect(dismissButtonList).toHaveLength(failingCustomCheckCount); //count of dismiss button
73+
74+
//click dismiss button
75+
await userEvent.click(dismissButtonList[0]);
76+
expect(await customChecksDismissButtonList()).toHaveLength(failingCustomCheckCount - 1); //count of dismiss button
77+
expect(await customChecksFailedRowsList()).toHaveLength(failingCustomCheckCount - 1); //count of failed checks matches failing count set
78+
79+
//re-add the dismissed custom check Item
80+
await driver.setUp(precondition.getCustomChecks(customCheckItems));
81+
await driver.goTo("/custom-checks");
82+
expect(await customChecksFailedRowsList()).toHaveLength(failingCustomCheckCount); //count of failed checks matches failing count set
83+
});
3184
});
3285
});

0 commit comments

Comments
 (0)