@oneuptime/common 9.2.20 → 9.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Server/Services/AIService.ts +1 -1
- package/Tests/Server/API/BaseAPI.test.ts +9 -4
- package/Tests/Server/Middleware/ProjectAuthorization.test.ts +133 -162
- package/Tests/Server/Services/ProbeService.test.ts +91 -784
- package/Tests/Server/Services/ScheduledMaintenanceService.test.ts +131 -112
- package/Tests/Server/Services/TeamMemberService.test.ts +87 -1343
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +18 -9
- package/Tests/Server/Utils/Cookie.test.ts +10 -2
- package/Tests/Types/HashedString.test.ts +52 -8
- package/Tests/UI/Components/404.test.tsx +10 -15
- package/Tests/UI/Components/Breadcrumbs.test.tsx +6 -2
- package/Tests/UI/Components/Button.test.tsx +12 -12
- package/Tests/UI/Components/Card.test.tsx +4 -2
- package/Tests/UI/Components/ConfirmModal.test.tsx +1 -1
- package/Tests/UI/Components/Dropdown.test.tsx +37 -4
- package/Tests/UI/Components/DuplicateModel.test.tsx +49 -45
- package/Tests/UI/Components/FilePicker.test.tsx +258 -178
- package/Tests/UI/Components/List.test.tsx +3 -1
- package/Tests/UI/Components/MarkdownEditor.test.tsx +6 -5
- package/Tests/UI/Components/MasterPage.test.tsx +1 -1
- package/Tests/UI/Components/Modal.test.tsx +5 -5
- package/Tests/UI/Components/NavBar.test.tsx +14 -1
- package/Tests/UI/Components/OrderedStatesList.test.tsx +1 -1
- package/Tests/UI/Components/Pagination.test.tsx +6 -2
- package/Tests/Utils/API.test.ts +133 -11
- package/Tests/__mocks__/azure.js +2 -0
- package/Tests/__mocks__/botbuilder-stdlib.js +2 -0
- package/Tests/__mocks__/botbuilder.js +10 -0
- package/Tests/__mocks__/locter.js +5 -0
- package/Tests/__mocks__/otpauth.js +30 -0
- package/Tests/__mocks__/simplewebauthn.js +34 -0
- package/Tests/__mocks__/styleMock.js +1 -0
- package/Tests/__mocks__/uuid.js +31 -0
- package/Tests/__mocks__/yaml.js +11 -0
- package/Tests/jest.setup.ts +14 -0
- package/UI/Components/AI/AITemplates.ts +226 -0
- package/UI/Components/AI/GenerateFromAIModal.tsx +21 -270
- package/build/dist/Server/Services/AIService.js +1 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Tests/Server/API/BaseAPI.test.js +7 -2
- package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
- package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js +89 -101
- package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/ProbeService.test.js +95 -687
- package/build/dist/Tests/Server/Services/ProbeService.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js +108 -89
- package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/TeamMemberService.test.js +85 -924
- package/build/dist/Tests/Server/Services/TeamMemberService.test.js.map +1 -1
- package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js +14 -9
- package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js.map +1 -1
- package/build/dist/Tests/Server/Utils/Cookie.test.js +10 -4
- package/build/dist/Tests/Server/Utils/Cookie.test.js.map +1 -1
- package/build/dist/Tests/Types/HashedString.test.js +39 -6
- package/build/dist/Tests/Types/HashedString.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/404.test.js +10 -10
- package/build/dist/Tests/UI/Components/404.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Breadcrumbs.test.js +6 -2
- package/build/dist/Tests/UI/Components/Breadcrumbs.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Button.test.js +12 -12
- package/build/dist/Tests/UI/Components/Card.test.js +4 -2
- package/build/dist/Tests/UI/Components/Card.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/ConfirmModal.test.js +1 -1
- package/build/dist/Tests/UI/Components/ConfirmModal.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Dropdown.test.js +19 -3
- package/build/dist/Tests/UI/Components/Dropdown.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/DuplicateModel.test.js +46 -41
- package/build/dist/Tests/UI/Components/DuplicateModel.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/FilePicker.test.js +210 -117
- package/build/dist/Tests/UI/Components/FilePicker.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/List.test.js +3 -1
- package/build/dist/Tests/UI/Components/List.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/MarkdownEditor.test.js +6 -5
- package/build/dist/Tests/UI/Components/MarkdownEditor.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/MasterPage.test.js +1 -1
- package/build/dist/Tests/UI/Components/MasterPage.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Modal.test.js +5 -5
- package/build/dist/Tests/UI/Components/Modal.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/NavBar.test.js +13 -1
- package/build/dist/Tests/UI/Components/NavBar.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/OrderedStatesList.test.js +1 -1
- package/build/dist/Tests/UI/Components/OrderedStatesList.test.js.map +1 -1
- package/build/dist/Tests/UI/Components/Pagination.test.js +6 -2
- package/build/dist/Tests/UI/Components/Pagination.test.js.map +1 -1
- package/build/dist/Tests/Utils/API.test.js +100 -9
- package/build/dist/Tests/Utils/API.test.js.map +1 -1
- package/build/dist/Tests/jest.setup.js +13 -0
- package/build/dist/Tests/jest.setup.js.map +1 -0
- package/build/dist/UI/Components/AI/AITemplates.js +218 -0
- package/build/dist/UI/Components/AI/AITemplates.js.map +1 -0
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js +5 -238
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
- package/jest.config.json +18 -1
- package/package.json +1 -1
|
@@ -72,7 +72,7 @@ describe("Modal", () => {
|
|
|
72
72
|
</Modal>,
|
|
73
73
|
);
|
|
74
74
|
|
|
75
|
-
expect(getByTestId("modal")).toHaveClass("
|
|
75
|
+
expect(getByTestId("modal")).toHaveClass("md:max-w-lg");
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
it("displays the modal with the correct width when modalWidth is set", () => {
|
|
@@ -88,7 +88,7 @@ describe("Modal", () => {
|
|
|
88
88
|
</Modal>,
|
|
89
89
|
);
|
|
90
90
|
|
|
91
|
-
expect(getByTestId("modal")).toHaveClass("
|
|
91
|
+
expect(getByTestId("modal")).toHaveClass("md:max-w-3xl");
|
|
92
92
|
});
|
|
93
93
|
|
|
94
94
|
it("displays the children passed to the modal", () => {
|
|
@@ -112,7 +112,7 @@ describe("Modal", () => {
|
|
|
112
112
|
</Modal>,
|
|
113
113
|
);
|
|
114
114
|
|
|
115
|
-
expect(getByTestId("loader")).toBeInTheDocument();
|
|
115
|
+
expect(getByTestId("bar-loader")).toBeInTheDocument();
|
|
116
116
|
});
|
|
117
117
|
|
|
118
118
|
it("does not display the loader when isBodyLoading is false", () => {
|
|
@@ -124,7 +124,7 @@ describe("Modal", () => {
|
|
|
124
124
|
</Modal>,
|
|
125
125
|
);
|
|
126
126
|
|
|
127
|
-
expect(queryByTestId("loader")).not.toBeInTheDocument();
|
|
127
|
+
expect(queryByTestId("bar-loader")).not.toBeInTheDocument();
|
|
128
128
|
});
|
|
129
129
|
|
|
130
130
|
it("does not display the loader when isBodyLoading is undefined", () => {
|
|
@@ -136,7 +136,7 @@ describe("Modal", () => {
|
|
|
136
136
|
</Modal>,
|
|
137
137
|
);
|
|
138
138
|
|
|
139
|
-
expect(queryByTestId("loader")).not.toBeInTheDocument();
|
|
139
|
+
expect(queryByTestId("bar-loader")).not.toBeInTheDocument();
|
|
140
140
|
});
|
|
141
141
|
|
|
142
142
|
it("disables the submit button when isLoading is true", () => {
|
|
@@ -2,14 +2,27 @@ import Navbar, {
|
|
|
2
2
|
ComponentProps,
|
|
3
3
|
NavItem,
|
|
4
4
|
} from "../../../UI/Components/Navbar/NavBar";
|
|
5
|
-
import { describe, expect, it } from "@jest/globals";
|
|
5
|
+
import { describe, expect, it, beforeEach } from "@jest/globals";
|
|
6
6
|
import "@testing-library/jest-dom/extend-expect";
|
|
7
7
|
import { render, screen } from "@testing-library/react";
|
|
8
8
|
import React from "react";
|
|
9
9
|
import Route from "../../../Types/API/Route";
|
|
10
10
|
import IconProp from "../../../Types/Icon/IconProp";
|
|
11
|
+
import Navigation from "../../../UI/Utils/Navigation";
|
|
12
|
+
import { Location } from "react-router-dom";
|
|
11
13
|
|
|
12
14
|
describe("Navbar", () => {
|
|
15
|
+
// Mock Navigation location for Navigation utility
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
const mockLocation: Location = {
|
|
18
|
+
pathname: "/",
|
|
19
|
+
search: "",
|
|
20
|
+
hash: "",
|
|
21
|
+
state: null,
|
|
22
|
+
key: "default",
|
|
23
|
+
};
|
|
24
|
+
Navigation.setLocation(mockLocation);
|
|
25
|
+
});
|
|
13
26
|
const defaultProps: ComponentProps = {
|
|
14
27
|
children: <div>Test</div>,
|
|
15
28
|
};
|
|
@@ -46,7 +46,7 @@ describe("OrderedSateList", () => {
|
|
|
46
46
|
isLoading: true,
|
|
47
47
|
};
|
|
48
48
|
render(<OrderedStatesList {...props} />);
|
|
49
|
-
expect(screen.
|
|
49
|
+
expect(screen.getByTestId("bar-loader")).toBeInTheDocument();
|
|
50
50
|
});
|
|
51
51
|
it("should render an error message if error prop is present", () => {
|
|
52
52
|
const props: ComponentProps<ItemData> = {
|
|
@@ -59,7 +59,9 @@ describe("Pagination", () => {
|
|
|
59
59
|
|
|
60
60
|
render(<Pagination {...props} />);
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
// There are multiple "Next" elements (mobile and desktop), get the first one
|
|
63
|
+
const nextButtons: HTMLElement[] = screen.getAllByText("Next");
|
|
64
|
+
fireEvent.click(nextButtons[0]!);
|
|
63
65
|
|
|
64
66
|
await waitFor(() => {
|
|
65
67
|
expect(mockOnNavigateToPage).toHaveBeenCalledWith(2, 10);
|
|
@@ -81,7 +83,9 @@ describe("Pagination", () => {
|
|
|
81
83
|
|
|
82
84
|
render(<Pagination {...props} />);
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
// There are multiple "Previous" elements (mobile and desktop), get the first one
|
|
87
|
+
const prevButtons: HTMLElement[] = screen.getAllByText("Previous");
|
|
88
|
+
fireEvent.click(prevButtons[0]!);
|
|
85
89
|
|
|
86
90
|
await waitFor(() => {
|
|
87
91
|
expect(mockOnNavigateToPage).toHaveBeenCalledWith(1, 10);
|
package/Tests/Utils/API.test.ts
CHANGED
|
@@ -396,33 +396,155 @@ describe.each(httpMethodTests)("$name", ({ name, method }: HTTPMethodType) => {
|
|
|
396
396
|
});
|
|
397
397
|
});
|
|
398
398
|
|
|
399
|
-
|
|
400
|
-
test(`should make a ${method} request`, async () => {
|
|
401
|
-
const route: string = "fact";
|
|
402
|
-
const hostname: string = "catfact.ninja";
|
|
403
|
-
const api: API = new API(Protocol.HTTPS, new Hostname(hostname));
|
|
399
|
+
// New tests replacing the skipped instance method tests
|
|
404
400
|
|
|
401
|
+
describe("API.patch", () => {
|
|
402
|
+
test("should make a PATCH request", async () => {
|
|
405
403
|
mockedAxios.mockResolvedValueOnce(createAxiosResponse());
|
|
406
404
|
|
|
407
405
|
const url: URL = new URL(
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
406
|
+
Protocol.HTTPS,
|
|
407
|
+
"catfact.ninja",
|
|
408
|
+
new Route("fact"),
|
|
411
409
|
);
|
|
412
|
-
const got: HTTPResponse<JSONObject> = await (
|
|
410
|
+
const got: HTTPResponse<JSONObject> = await API.patch({
|
|
413
411
|
url,
|
|
414
412
|
data: requestData,
|
|
415
413
|
headers: requestHeaders,
|
|
416
414
|
});
|
|
417
415
|
|
|
418
|
-
// Check method, url (protocol, hostname, route), headers, request data
|
|
419
416
|
expect(axios).toBeCalledWith(
|
|
420
417
|
createAxiosParameters({
|
|
418
|
+
method: "PATCH",
|
|
421
419
|
url: "https://catfact.ninja/fact",
|
|
422
|
-
method,
|
|
423
420
|
data: requestData,
|
|
421
|
+
headers: mergedHeaders,
|
|
424
422
|
}),
|
|
425
423
|
);
|
|
426
424
|
expect(got).toBeInstanceOf(HTTPResponse);
|
|
427
425
|
});
|
|
426
|
+
|
|
427
|
+
test("should make a PATCH request without data", async () => {
|
|
428
|
+
mockedAxios.mockResolvedValueOnce(createAxiosResponse());
|
|
429
|
+
|
|
430
|
+
const url: URL = new URL(
|
|
431
|
+
Protocol.HTTPS,
|
|
432
|
+
"catfact.ninja",
|
|
433
|
+
new Route("update"),
|
|
434
|
+
);
|
|
435
|
+
const got: HTTPResponse<JSONObject> = await API.patch({
|
|
436
|
+
url,
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
expect(axios).toBeCalledWith({
|
|
440
|
+
method: "PATCH",
|
|
441
|
+
url: "https://catfact.ninja/update",
|
|
442
|
+
headers: DEFAULT_HEADERS,
|
|
443
|
+
data: undefined,
|
|
444
|
+
});
|
|
445
|
+
expect(got).toBeInstanceOf(HTTPResponse);
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
describe("API.getFriendlyErrorMessage", () => {
|
|
450
|
+
test("should return error message from AxiosError", () => {
|
|
451
|
+
const errorMessage: string = "Request failed";
|
|
452
|
+
const axiosError: AxiosError = createAxiosError({
|
|
453
|
+
message: errorMessage,
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
const message: string = API.getFriendlyErrorMessage(axiosError);
|
|
457
|
+
expect(message).toBe(errorMessage);
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
test("should return error message from regular Error", () => {
|
|
461
|
+
const error: Error = new Error("Something went wrong");
|
|
462
|
+
const message: string = API.getFriendlyErrorMessage(error);
|
|
463
|
+
expect(message).toBe("Something went wrong");
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test("should handle error and return non-empty string", () => {
|
|
467
|
+
const customError: Error = new Error("Network timeout occurred");
|
|
468
|
+
const message: string = API.getFriendlyErrorMessage(customError);
|
|
469
|
+
expect(message.length).toBeGreaterThan(0);
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
test("should return string type result", () => {
|
|
473
|
+
const error: Error = new Error("Test error");
|
|
474
|
+
const message: string = API.getFriendlyErrorMessage(error);
|
|
475
|
+
expect(typeof message).toBe("string");
|
|
476
|
+
expect(message.length).toBeGreaterThan(0);
|
|
477
|
+
});
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
describe("API instance properties", () => {
|
|
481
|
+
test("should return protocol with trailing slashes", () => {
|
|
482
|
+
const api: API = new API(Protocol.HTTPS, new Hostname("example.com"));
|
|
483
|
+
expect(api.protocol).toBe("https://");
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
test("should return protocol for HTTP", () => {
|
|
487
|
+
const api: API = new API(Protocol.HTTP, new Hostname("localhost"));
|
|
488
|
+
expect(api.protocol).toBe("http://");
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
test("should return hostname as string", () => {
|
|
492
|
+
const hostname: string = "api.example.com";
|
|
493
|
+
const api: API = new API(Protocol.HTTPS, new Hostname(hostname));
|
|
494
|
+
expect(api.hostname.toString()).toBe(hostname);
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
test("should handle nested route in base route", () => {
|
|
498
|
+
const api: API = new API(
|
|
499
|
+
Protocol.HTTPS,
|
|
500
|
+
new Hostname("api.example.com"),
|
|
501
|
+
new Route("/v1/api"),
|
|
502
|
+
);
|
|
503
|
+
expect(api.baseRoute.toString()).toBe("/v1/api");
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
describe("API.fetch with options", () => {
|
|
508
|
+
test("should make request with query parameters", async () => {
|
|
509
|
+
mockedAxios.mockResolvedValueOnce(
|
|
510
|
+
createAxiosResponse({ data: responseData }),
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
const params: Dictionary<string> = {
|
|
514
|
+
page: "1",
|
|
515
|
+
limit: "10",
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
await API.fetch({
|
|
519
|
+
method: HTTPMethod.GET,
|
|
520
|
+
url: new URL(Protocol.HTTPS, "api.example.com", new Route("items")),
|
|
521
|
+
params,
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
expect(axios).toBeCalledWith({
|
|
525
|
+
method: "GET",
|
|
526
|
+
url: "https://api.example.com/items?page=1&limit=10",
|
|
527
|
+
headers: DEFAULT_HEADERS,
|
|
528
|
+
data: undefined,
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
test("should handle empty params object", async () => {
|
|
533
|
+
mockedAxios.mockResolvedValueOnce(
|
|
534
|
+
createAxiosResponse({ data: responseData }),
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
await API.fetch({
|
|
538
|
+
method: HTTPMethod.GET,
|
|
539
|
+
url: new URL(Protocol.HTTPS, "api.example.com", new Route("items")),
|
|
540
|
+
params: {},
|
|
541
|
+
});
|
|
542
|
+
|
|
543
|
+
expect(axios).toBeCalledWith({
|
|
544
|
+
method: "GET",
|
|
545
|
+
url: "https://api.example.com/items",
|
|
546
|
+
headers: DEFAULT_HEADERS,
|
|
547
|
+
data: undefined,
|
|
548
|
+
});
|
|
549
|
+
});
|
|
428
550
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// Mock for botbuilder, botbuilder-core, and botframework-connector packages
|
|
2
|
+
module.exports = {
|
|
3
|
+
CloudAdapter: class CloudAdapter {},
|
|
4
|
+
ConfigurationBotFrameworkAuthentication: class ConfigurationBotFrameworkAuthentication {},
|
|
5
|
+
TeamsActivityHandler: class TeamsActivityHandler {},
|
|
6
|
+
TurnContext: class TurnContext {},
|
|
7
|
+
ActivityHandler: class ActivityHandler {},
|
|
8
|
+
MessageFactory: { text: jest.fn() },
|
|
9
|
+
CardFactory: { heroCard: jest.fn() },
|
|
10
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Mock for otpauth package
|
|
2
|
+
module.exports = {
|
|
3
|
+
HOTP: class HOTP {
|
|
4
|
+
generate() {
|
|
5
|
+
return "123456";
|
|
6
|
+
}
|
|
7
|
+
validate() {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
TOTP: class TOTP {
|
|
12
|
+
generate() {
|
|
13
|
+
return "123456";
|
|
14
|
+
}
|
|
15
|
+
validate() {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
Secret: class Secret {
|
|
20
|
+
static fromBase32() {
|
|
21
|
+
return new Secret();
|
|
22
|
+
}
|
|
23
|
+
base32 = "JBSWY3DPEHPK3PXP";
|
|
24
|
+
},
|
|
25
|
+
URI: {
|
|
26
|
+
parse: jest.fn(),
|
|
27
|
+
stringify: jest.fn(),
|
|
28
|
+
},
|
|
29
|
+
version: "9.0.0",
|
|
30
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Mock for @simplewebauthn/server package
|
|
2
|
+
module.exports = {
|
|
3
|
+
generateRegistrationOptions: jest.fn().mockResolvedValue({
|
|
4
|
+
challenge: "mock-challenge",
|
|
5
|
+
rp: { name: "Mock RP", id: "localhost" },
|
|
6
|
+
user: { id: "user-id", name: "user@example.com", displayName: "User" },
|
|
7
|
+
pubKeyCredParams: [],
|
|
8
|
+
timeout: 60000,
|
|
9
|
+
attestation: "none",
|
|
10
|
+
excludeCredentials: [],
|
|
11
|
+
authenticatorSelection: {},
|
|
12
|
+
}),
|
|
13
|
+
verifyRegistrationResponse: jest.fn().mockResolvedValue({
|
|
14
|
+
verified: true,
|
|
15
|
+
registrationInfo: {
|
|
16
|
+
credentialID: Buffer.from("mock-credential-id"),
|
|
17
|
+
credentialPublicKey: Buffer.from("mock-public-key"),
|
|
18
|
+
counter: 0,
|
|
19
|
+
},
|
|
20
|
+
}),
|
|
21
|
+
generateAuthenticationOptions: jest.fn().mockResolvedValue({
|
|
22
|
+
challenge: "mock-challenge",
|
|
23
|
+
timeout: 60000,
|
|
24
|
+
rpId: "localhost",
|
|
25
|
+
allowCredentials: [],
|
|
26
|
+
userVerification: "preferred",
|
|
27
|
+
}),
|
|
28
|
+
verifyAuthenticationResponse: jest.fn().mockResolvedValue({
|
|
29
|
+
verified: true,
|
|
30
|
+
authenticationInfo: {
|
|
31
|
+
newCounter: 1,
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Mock for uuid package with unique and valid UUID values
|
|
2
|
+
let v1Counter = 0;
|
|
3
|
+
let v4Counter = 0;
|
|
4
|
+
|
|
5
|
+
// Generate a valid UUID by padding counter to create proper hex segments
|
|
6
|
+
function padHex(num, length) {
|
|
7
|
+
return num.toString(16).padStart(length, "0").slice(-length);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module.exports = {
|
|
11
|
+
v1: jest.fn(() => {
|
|
12
|
+
v1Counter++;
|
|
13
|
+
// Generate valid v1 UUID format: xxxxxxxx-xxxx-1xxx-xxxx-xxxxxxxxxxxx
|
|
14
|
+
const p1 = padHex(v1Counter, 8);
|
|
15
|
+
const p2 = padHex(v1Counter * 2, 4);
|
|
16
|
+
const p3 = "1" + padHex(v1Counter * 3, 3);
|
|
17
|
+
const p4 = padHex(0x8000 + (v1Counter % 0x3fff), 4);
|
|
18
|
+
const p5 = padHex(v1Counter, 12);
|
|
19
|
+
return `${p1}-${p2}-${p3}-${p4}-${p5}`;
|
|
20
|
+
}),
|
|
21
|
+
v4: jest.fn(() => {
|
|
22
|
+
v4Counter++;
|
|
23
|
+
// Generate valid v4 UUID format: xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx
|
|
24
|
+
const p1 = padHex(v4Counter + 0x10000000, 8);
|
|
25
|
+
const p2 = padHex(v4Counter * 2, 4);
|
|
26
|
+
const p3 = "4" + padHex(v4Counter * 3, 3);
|
|
27
|
+
const p4 = padHex(0x8000 + (v4Counter % 0x3fff), 4);
|
|
28
|
+
const p5 = padHex(v4Counter + 0x100000000000, 12);
|
|
29
|
+
return `${p1}-${p2}-${p3}-${p4}-${p5}`;
|
|
30
|
+
}),
|
|
31
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import { TextEncoder, TextDecoder } from "util";
|
|
3
|
+
|
|
4
|
+
// Polyfill TextEncoder/TextDecoder for jsdom
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
6
|
+
(global as any).TextEncoder = TextEncoder;
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
(global as any).TextDecoder = TextDecoder;
|
|
9
|
+
|
|
10
|
+
// Mock window.scrollTo for jsdom
|
|
11
|
+
Object.defineProperty(window, "scrollTo", {
|
|
12
|
+
value: jest.fn(),
|
|
13
|
+
writable: true,
|
|
14
|
+
});
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* AI Template definitions for different note types
|
|
3
|
+
* These templates guide AI generation for various use cases
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface AITemplate {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
content: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Default hardcoded templates for incident postmortem
|
|
13
|
+
export const POSTMORTEM_TEMPLATES: Array<AITemplate> = [
|
|
14
|
+
{
|
|
15
|
+
id: "default-standard",
|
|
16
|
+
name: "Standard Postmortem",
|
|
17
|
+
content: `## Executive Summary
|
|
18
|
+
[Brief overview of the incident, its impact, and resolution]
|
|
19
|
+
|
|
20
|
+
## Incident Timeline
|
|
21
|
+
| Time | Event |
|
|
22
|
+
|------|-------|
|
|
23
|
+
| [Time] | [Event description] |
|
|
24
|
+
|
|
25
|
+
## Root Cause Analysis
|
|
26
|
+
[Detailed analysis of what caused the incident]
|
|
27
|
+
|
|
28
|
+
## Impact Assessment
|
|
29
|
+
- **Duration**: [How long the incident lasted]
|
|
30
|
+
- **Users Affected**: [Number or percentage of affected users]
|
|
31
|
+
- **Services Affected**: [List of affected services]
|
|
32
|
+
|
|
33
|
+
## Resolution
|
|
34
|
+
[Steps taken to resolve the incident]
|
|
35
|
+
|
|
36
|
+
## Action Items
|
|
37
|
+
- [ ] [Action item 1]
|
|
38
|
+
- [ ] [Action item 2]
|
|
39
|
+
- [ ] [Action item 3]
|
|
40
|
+
|
|
41
|
+
## Lessons Learned
|
|
42
|
+
[Key takeaways and improvements identified]`,
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "default-detailed",
|
|
46
|
+
name: "Detailed Technical Postmortem",
|
|
47
|
+
content: `## Incident Overview
|
|
48
|
+
**Incident Title**: [Title]
|
|
49
|
+
**Severity**: [P1/P2/P3/P4]
|
|
50
|
+
**Duration**: [Start time] - [End time]
|
|
51
|
+
**Authors**: [Names]
|
|
52
|
+
|
|
53
|
+
## Summary
|
|
54
|
+
[2-3 sentence summary of the incident]
|
|
55
|
+
|
|
56
|
+
## Detection
|
|
57
|
+
- **How was the incident detected?** [Monitoring alert / Customer report / etc.]
|
|
58
|
+
- **Time to detection**: [Duration from start to detection]
|
|
59
|
+
|
|
60
|
+
## Timeline
|
|
61
|
+
| Timestamp | Action | Owner |
|
|
62
|
+
|-----------|--------|-------|
|
|
63
|
+
| [Time] | [What happened] | [Who did it] |
|
|
64
|
+
|
|
65
|
+
## Root Cause
|
|
66
|
+
### Primary Cause
|
|
67
|
+
[Detailed explanation of the root cause]
|
|
68
|
+
|
|
69
|
+
### Contributing Factors
|
|
70
|
+
1. [Factor 1]
|
|
71
|
+
2. [Factor 2]
|
|
72
|
+
|
|
73
|
+
## Impact
|
|
74
|
+
### Customer Impact
|
|
75
|
+
[Description of how customers were affected]
|
|
76
|
+
|
|
77
|
+
### Business Impact
|
|
78
|
+
[Description of business consequences]
|
|
79
|
+
|
|
80
|
+
### Technical Impact
|
|
81
|
+
[Systems and services affected]
|
|
82
|
+
|
|
83
|
+
## Mitigation & Resolution
|
|
84
|
+
### Immediate Actions
|
|
85
|
+
[Steps taken to stop the bleeding]
|
|
86
|
+
|
|
87
|
+
### Permanent Fix
|
|
88
|
+
[Long-term solution implemented]
|
|
89
|
+
|
|
90
|
+
## Prevention
|
|
91
|
+
### What Went Well
|
|
92
|
+
- [Item 1]
|
|
93
|
+
- [Item 2]
|
|
94
|
+
|
|
95
|
+
### What Went Wrong
|
|
96
|
+
- [Item 1]
|
|
97
|
+
- [Item 2]
|
|
98
|
+
|
|
99
|
+
### Where We Got Lucky
|
|
100
|
+
- [Item 1]
|
|
101
|
+
|
|
102
|
+
## Action Items
|
|
103
|
+
| Action | Owner | Priority | Due Date |
|
|
104
|
+
|--------|-------|----------|----------|
|
|
105
|
+
| [Action] | [Name] | [High/Medium/Low] | [Date] |
|
|
106
|
+
|
|
107
|
+
## Appendix
|
|
108
|
+
[Any additional technical details, logs, or graphs]`,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: "default-brief",
|
|
112
|
+
name: "Brief Postmortem",
|
|
113
|
+
content: `## What Happened
|
|
114
|
+
[Concise description of the incident]
|
|
115
|
+
|
|
116
|
+
## Why It Happened
|
|
117
|
+
[Root cause explanation]
|
|
118
|
+
|
|
119
|
+
## How We Fixed It
|
|
120
|
+
[Resolution steps]
|
|
121
|
+
|
|
122
|
+
## How We Prevent It
|
|
123
|
+
- [ ] [Prevention action 1]
|
|
124
|
+
- [ ] [Prevention action 2]`,
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
|
|
128
|
+
// Default templates for public notes (customer-facing)
|
|
129
|
+
export const PUBLIC_NOTE_TEMPLATES: Array<AITemplate> = [
|
|
130
|
+
{
|
|
131
|
+
id: "public-status-update",
|
|
132
|
+
name: "Status Update",
|
|
133
|
+
content: `## Current Status
|
|
134
|
+
[Brief description of the current situation]
|
|
135
|
+
|
|
136
|
+
## What We're Doing
|
|
137
|
+
[Actions being taken to resolve the issue]
|
|
138
|
+
|
|
139
|
+
## Next Update
|
|
140
|
+
[Expected time for next update or resolution]`,
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: "public-resolution",
|
|
144
|
+
name: "Resolution Notice",
|
|
145
|
+
content: `## Issue Resolved
|
|
146
|
+
[Brief description of what was resolved]
|
|
147
|
+
|
|
148
|
+
## Summary
|
|
149
|
+
[What happened and how it was fixed]
|
|
150
|
+
|
|
151
|
+
## Prevention
|
|
152
|
+
[Steps taken to prevent recurrence]
|
|
153
|
+
|
|
154
|
+
Thank you for your patience.`,
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
id: "public-maintenance",
|
|
158
|
+
name: "Maintenance Update",
|
|
159
|
+
content: `## Maintenance Status
|
|
160
|
+
[Current phase of the maintenance]
|
|
161
|
+
|
|
162
|
+
## Progress
|
|
163
|
+
[What has been completed]
|
|
164
|
+
|
|
165
|
+
## Remaining Work
|
|
166
|
+
[What still needs to be done]
|
|
167
|
+
|
|
168
|
+
## Expected Completion
|
|
169
|
+
[Estimated completion time]`,
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
|
|
173
|
+
// Default templates for internal notes (team-facing)
|
|
174
|
+
export const INTERNAL_NOTE_TEMPLATES: Array<AITemplate> = [
|
|
175
|
+
{
|
|
176
|
+
id: "internal-investigation",
|
|
177
|
+
name: "Investigation Update",
|
|
178
|
+
content: `## Current Investigation Status
|
|
179
|
+
[What we're looking at]
|
|
180
|
+
|
|
181
|
+
## Findings So Far
|
|
182
|
+
- [Finding 1]
|
|
183
|
+
- [Finding 2]
|
|
184
|
+
|
|
185
|
+
## Hypothesis
|
|
186
|
+
[Current theory about the root cause]
|
|
187
|
+
|
|
188
|
+
## Next Steps
|
|
189
|
+
- [ ] [Action 1]
|
|
190
|
+
- [ ] [Action 2]`,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: "internal-technical",
|
|
194
|
+
name: "Technical Analysis",
|
|
195
|
+
content: `## Technical Details
|
|
196
|
+
[Detailed technical observations]
|
|
197
|
+
|
|
198
|
+
## Metrics/Logs
|
|
199
|
+
[Relevant metrics or log entries]
|
|
200
|
+
|
|
201
|
+
## Impact Assessment
|
|
202
|
+
[Technical impact analysis]
|
|
203
|
+
|
|
204
|
+
## Recommendations
|
|
205
|
+
[Technical recommendations for resolution]`,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: "internal-handoff",
|
|
209
|
+
name: "Shift Handoff",
|
|
210
|
+
content: `## Current State
|
|
211
|
+
[Where things stand now]
|
|
212
|
+
|
|
213
|
+
## Actions Taken
|
|
214
|
+
[What has been done so far]
|
|
215
|
+
|
|
216
|
+
## Open Questions
|
|
217
|
+
[Things that still need investigation]
|
|
218
|
+
|
|
219
|
+
## Immediate Priorities
|
|
220
|
+
- [ ] [Priority 1]
|
|
221
|
+
- [ ] [Priority 2]
|
|
222
|
+
|
|
223
|
+
## Contacts
|
|
224
|
+
[Key people involved or to contact]`,
|
|
225
|
+
},
|
|
226
|
+
];
|