Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/Frontend/test/preconditions/failedMessageCounts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FailedMessageStatus } from "@/resources/FailedMessage";
import type { SetupFactoryOptions } from "../driver";

export const hasFailedMessageCounts =
({ failed = 0, deleted = 0 }: { failed?: number; deleted?: number }) =>
({ driver }: SetupFactoryOptions) => {
const serviceControlUrl = window.defaultConfig.service_control_url;
const counts: Record<string, number> = {
[FailedMessageStatus.Unresolved]: failed,
[FailedMessageStatus.Archived]: deleted,
};

driver.mockEndpointDynamic(`${serviceControlUrl}errors`, "get", (url) => {
const count = counts[url.searchParams.get("status") ?? ""] ?? 0;
return Promise.resolve({ body: [], headers: { "Total-Count": String(count) } });
});
};
101 changes: 101 additions & 0 deletions src/Frontend/test/preconditions/failedMessageGroups.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import dayjs from "@/utils/dayjs";
import type GroupOperation from "@/resources/GroupOperation";
import type { SetupFactoryOptions } from "../driver";

export interface FailedMessageGroupOptions {
title?: string;
type?: string;
count?: number;
comment?: string;
firstFailedDaysAgo?: number;
lastFailedDaysAgo?: number;
lastRetriedDaysAgo?: number;
}

export const createFailedMessageGroup = (
id: string,
{ title = `Failed group ${id}`, type = "Exception Type and Stack Trace", count = 1, comment = "", firstFailedDaysAgo = 5, lastFailedDaysAgo = 1, lastRetriedDaysAgo }: FailedMessageGroupOptions = {}
): GroupOperation => {
const group: GroupOperation = {
id,
title,
type,
count,
comment,
first: dayjs().subtract(firstFailedDaysAgo, "day").toISOString(),
last: dayjs().subtract(lastFailedDaysAgo, "day").toISOString(),
operation_status: "None",
operation_progress: 0,
need_user_acknowledgement: false,
};

if (lastRetriedDaysAgo !== undefined) {
group.operation_completion_time = dayjs().subtract(lastRetriedDaysAgo, "day").toISOString();
group.last_operation_completion_time = group.operation_completion_time;
}

return group;
};

export interface FailedMessageGroupsTestBed {
groups: GroupOperation[];
requestedClassifiers: string[];
retriedGroupIds: string[];
deletedGroupIds: string[];
removedNoteGroupIds: string[];
}

export const hasFailedMessageGroups =
({ groups = [] }: { groups?: GroupOperation[] } = {}) =>
({ driver }: SetupFactoryOptions): FailedMessageGroupsTestBed => {
const serviceControlUrl = window.defaultConfig.service_control_url;
const table = [...groups];
const bed: FailedMessageGroupsTestBed = { groups: table, requestedClassifiers: [], retriedGroupIds: [], deletedGroupIds: [], removedNoteGroupIds: [] };

driver.mockEndpointDynamic(`${serviceControlUrl}recoverability/groups{/:classifier}?`, "get", (_url, params) => {
bed.requestedClassifiers.push(params.classifier ? decodeURIComponent(String(params.classifier)) : "");
return Promise.resolve({ body: table.map((group) => ({ ...group })) });
});

driver.mockEndpointDynamic(`${serviceControlUrl}recoverability/groups/id/:groupId`, "get", (_url, params) => {
const group = table.find((candidate) => candidate.id === String(params.groupId));
return Promise.resolve({ body: group ?? {}, status: group ? 200 : 404 });
});

driver.mockEndpointDynamic(`${serviceControlUrl}recoverability/groups/:groupId/errors`, "get", () => Promise.resolve({ body: [], headers: { "Total-Count": "0" } }));

driver.mockEndpointDynamic(`${serviceControlUrl}recoverability/groups/:groupId/comment`, "delete", (_url, params) => {
const groupId = String(params.groupId);
const group = table.find((candidate) => candidate.id === groupId);
if (group) {
group.comment = "";
}
bed.removedNoteGroupIds.push(groupId);
return Promise.resolve({ body: {} });
});

driver.mockEndpointDynamic(`${serviceControlUrl}recoverability/groups/:groupId/errors/retry`, "post", (_url, params) => {
const groupId = String(params.groupId);
const index = table.findIndex((group) => group.id === groupId);
if (index >= 0) {
table.splice(index, 1);
}
bed.retriedGroupIds.push(groupId);
return Promise.resolve({ body: {} });
});

driver.mockEndpointDynamic(`${serviceControlUrl}recoverability/groups/:groupId/errors/archive`, "post", (_url, params) => {
const groupId = String(params.groupId);
const group = table.find((candidate) => candidate.id === groupId);
if (group) {
group.operation_status = "ArchiveCompleted";
group.operation_progress = 1;
group.need_user_acknowledgement = true;
group.operation_completion_time = dayjs().toISOString();
}
bed.deletedGroupIds.push(groupId);
return Promise.resolve({ body: {} });
});

return bed;
};
2 changes: 1 addition & 1 deletion src/Frontend/test/preconditions/hasNoErrors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { SetupFactoryOptions } from "../driver";

const content = JSON.stringify([]);
const content: never[] = [];

export const errorsDefaultHandler = ({ driver }: SetupFactoryOptions) => {
const serviceControlInstanceUrl = window.defaultConfig.service_control_url;
Expand Down
3 changes: 3 additions & 0 deletions src/Frontend/test/preconditions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ export { serviceControlWithMonitoring } from "./serviceControlWithMonitoring";
export * from "./recoverability";
export * from "./deletedMessages";
export * from "./deletedMessageGroups";
export * from "./failedMessageGroups";
export * from "./recoverabilityHistory";
export * from "./failedMessageCounts";
export * from "./deletableFailedMessage";
export * from "./licensing";
export { hasLicensingReportAvailable } from "../preconditions/hasLicensingReportAvailable";
Expand Down
49 changes: 49 additions & 0 deletions src/Frontend/test/preconditions/recoverabilityHistory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import dayjs from "@/utils/dayjs";
import type HistoricRetryOperation from "@/resources/HistoricRetryOperation";
import type RecoverabilityHistoryResponse from "@/resources/RecoverabilityHistoryResponse";
import { RetryType } from "@/resources/RetryType";
import type { SetupFactoryOptions } from "../driver";

export interface HistoricRetryOperationOptions {
originator?: string;
retryType?: RetryType;
messagesSent?: number;
startedMinutesAgo?: number;
completedMinutesAgo?: number;
}

export const createHistoricRetryOperation = (
id: string,
{ originator = `Retried group ${id}`, retryType = RetryType.FailureGroup, messagesSent = 1, startedMinutesAgo = 30, completedMinutesAgo = 20 }: HistoricRetryOperationOptions = {}
): HistoricRetryOperation => ({
request_id: id,
retry_type: retryType,
start_time: dayjs().subtract(startedMinutesAgo, "minute").toISOString(),
completion_time: dayjs().subtract(completedMinutesAgo, "minute").toISOString(),
originator,
failed: false,
number_of_messages_processed: messagesSent,
});

export interface CompletedRetryRequestsTestBed {
historyRequestCount: number;
}

export const hasCompletedRetryRequests =
({ operations = [] }: { operations?: HistoricRetryOperation[] } = {}) =>
({ driver }: SetupFactoryOptions): CompletedRetryRequestsTestBed => {
const serviceControlUrl = window.defaultConfig.service_control_url;
const bed: CompletedRetryRequestsTestBed = { historyRequestCount: 0 };

driver.mockEndpointDynamic(`${serviceControlUrl}recoverability/history`, "get", () => {
bed.historyRequestCount++;
return Promise.resolve({
body: <RecoverabilityHistoryResponse>{
historic_operations: operations,
unacknowledged_operations: [],
},
});
});

return bed;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import userEvent from "@testing-library/user-event";
import { screen, within } from "@testing-library/vue";
import { getFailedMessageGroupRow } from "../questions/failedMessageGroupRows";
import { getConfirmationDialog } from "../questions/failedMessageGroupsView";

export async function clickGroupAction(groupTitle: string, action: RegExp): Promise<void> {
const row = getFailedMessageGroupRow(groupTitle);
if (!row) {
throw new Error(`Cannot click ${action}: no failed message group row titled "${groupTitle}" is listed`);
}
await userEvent.click(within(row.element).getByRole("button", { name: action }));
}

export async function confirmDialog(heading: RegExp): Promise<void> {
await userEvent.click(within(getConfirmationDialog(heading)).getByRole("button", { name: "Yes" }));
}

async function openMenu(label: string): Promise<HTMLElement> {
const menu = screen.getByText(label).closest<HTMLElement>(".msg-group-menu");
const toggle = menu?.querySelector<HTMLButtonElement>("button.dropdown-toggle");
if (!menu || !toggle) {
throw new Error(`Cannot open menu: the '${label}' menu is not shown`);
}
await userEvent.click(toggle);
return menu;
}

export async function selectGrouping(classifier: string): Promise<void> {
const menu = await openMenu("Group by:");
await userEvent.click(within(menu).getByText(classifier, { selector: "a" }));
}

export async function selectSort(sortOption: RegExp): Promise<void> {
const menu = await openMenu("Sort by:");
await userEvent.click(within(menu).getByRole("button", { name: sortOption }));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import userEvent from "@testing-library/user-event";
import { getFailedMessageGroupRow } from "../questions/failedMessageGroupRows";

export async function hoverFailedMessageGroup(groupTitle: string): Promise<void> {
const row = getFailedMessageGroupRow(groupTitle);
if (!row) {
throw new Error(`Failed message group "${groupTitle}" is not displayed`);
}
await userEvent.hover(row.element);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import userEvent from "@testing-library/user-event";
import { completedRetryRequestsHeading } from "../questions/completedRetryRequests";

export async function toggleCompletedRetryRequests(): Promise<void> {
await userEvent.click(completedRetryRequestsHeading());
}
Loading
Loading