@oneuptime/common 9.2.18 → 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.
Files changed (100) hide show
  1. package/Server/Services/AIService.ts +1 -1
  2. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +78 -1
  3. package/Server/Utils/Workspace/Slack/Slack.ts +80 -1
  4. package/Tests/Server/API/BaseAPI.test.ts +9 -4
  5. package/Tests/Server/Middleware/ProjectAuthorization.test.ts +133 -162
  6. package/Tests/Server/Services/ProbeService.test.ts +91 -784
  7. package/Tests/Server/Services/ScheduledMaintenanceService.test.ts +131 -112
  8. package/Tests/Server/Services/TeamMemberService.test.ts +87 -1343
  9. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +18 -9
  10. package/Tests/Server/Utils/Cookie.test.ts +10 -2
  11. package/Tests/Types/HashedString.test.ts +52 -8
  12. package/Tests/UI/Components/404.test.tsx +10 -15
  13. package/Tests/UI/Components/Breadcrumbs.test.tsx +6 -2
  14. package/Tests/UI/Components/Button.test.tsx +12 -12
  15. package/Tests/UI/Components/Card.test.tsx +4 -2
  16. package/Tests/UI/Components/ConfirmModal.test.tsx +1 -1
  17. package/Tests/UI/Components/Dropdown.test.tsx +37 -4
  18. package/Tests/UI/Components/DuplicateModel.test.tsx +49 -45
  19. package/Tests/UI/Components/FilePicker.test.tsx +258 -178
  20. package/Tests/UI/Components/List.test.tsx +3 -1
  21. package/Tests/UI/Components/MarkdownEditor.test.tsx +6 -5
  22. package/Tests/UI/Components/MasterPage.test.tsx +1 -1
  23. package/Tests/UI/Components/Modal.test.tsx +5 -5
  24. package/Tests/UI/Components/NavBar.test.tsx +14 -1
  25. package/Tests/UI/Components/OrderedStatesList.test.tsx +1 -1
  26. package/Tests/UI/Components/Pagination.test.tsx +6 -2
  27. package/Tests/Utils/API.test.ts +133 -11
  28. package/Tests/__mocks__/azure.js +2 -0
  29. package/Tests/__mocks__/botbuilder-stdlib.js +2 -0
  30. package/Tests/__mocks__/botbuilder.js +10 -0
  31. package/Tests/__mocks__/locter.js +5 -0
  32. package/Tests/__mocks__/otpauth.js +30 -0
  33. package/Tests/__mocks__/simplewebauthn.js +34 -0
  34. package/Tests/__mocks__/styleMock.js +1 -0
  35. package/Tests/__mocks__/uuid.js +31 -0
  36. package/Tests/__mocks__/yaml.js +11 -0
  37. package/Tests/jest.setup.ts +14 -0
  38. package/UI/Components/AI/AITemplates.ts +226 -0
  39. package/UI/Components/AI/GenerateFromAIModal.tsx +21 -270
  40. package/build/dist/Server/Services/AIService.js +1 -1
  41. package/build/dist/Server/Services/AIService.js.map +1 -1
  42. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +59 -2
  43. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  44. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +61 -1
  45. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  46. package/build/dist/Tests/Server/API/BaseAPI.test.js +7 -2
  47. package/build/dist/Tests/Server/API/BaseAPI.test.js.map +1 -1
  48. package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js +89 -101
  49. package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js.map +1 -1
  50. package/build/dist/Tests/Server/Services/ProbeService.test.js +95 -687
  51. package/build/dist/Tests/Server/Services/ProbeService.test.js.map +1 -1
  52. package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js +108 -89
  53. package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js.map +1 -1
  54. package/build/dist/Tests/Server/Services/TeamMemberService.test.js +85 -924
  55. package/build/dist/Tests/Server/Services/TeamMemberService.test.js.map +1 -1
  56. package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js +14 -9
  57. package/build/dist/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.js.map +1 -1
  58. package/build/dist/Tests/Server/Utils/Cookie.test.js +10 -4
  59. package/build/dist/Tests/Server/Utils/Cookie.test.js.map +1 -1
  60. package/build/dist/Tests/Types/HashedString.test.js +39 -6
  61. package/build/dist/Tests/Types/HashedString.test.js.map +1 -1
  62. package/build/dist/Tests/UI/Components/404.test.js +10 -10
  63. package/build/dist/Tests/UI/Components/404.test.js.map +1 -1
  64. package/build/dist/Tests/UI/Components/Breadcrumbs.test.js +6 -2
  65. package/build/dist/Tests/UI/Components/Breadcrumbs.test.js.map +1 -1
  66. package/build/dist/Tests/UI/Components/Button.test.js +12 -12
  67. package/build/dist/Tests/UI/Components/Card.test.js +4 -2
  68. package/build/dist/Tests/UI/Components/Card.test.js.map +1 -1
  69. package/build/dist/Tests/UI/Components/ConfirmModal.test.js +1 -1
  70. package/build/dist/Tests/UI/Components/ConfirmModal.test.js.map +1 -1
  71. package/build/dist/Tests/UI/Components/Dropdown.test.js +19 -3
  72. package/build/dist/Tests/UI/Components/Dropdown.test.js.map +1 -1
  73. package/build/dist/Tests/UI/Components/DuplicateModel.test.js +46 -41
  74. package/build/dist/Tests/UI/Components/DuplicateModel.test.js.map +1 -1
  75. package/build/dist/Tests/UI/Components/FilePicker.test.js +210 -117
  76. package/build/dist/Tests/UI/Components/FilePicker.test.js.map +1 -1
  77. package/build/dist/Tests/UI/Components/List.test.js +3 -1
  78. package/build/dist/Tests/UI/Components/List.test.js.map +1 -1
  79. package/build/dist/Tests/UI/Components/MarkdownEditor.test.js +6 -5
  80. package/build/dist/Tests/UI/Components/MarkdownEditor.test.js.map +1 -1
  81. package/build/dist/Tests/UI/Components/MasterPage.test.js +1 -1
  82. package/build/dist/Tests/UI/Components/MasterPage.test.js.map +1 -1
  83. package/build/dist/Tests/UI/Components/Modal.test.js +5 -5
  84. package/build/dist/Tests/UI/Components/Modal.test.js.map +1 -1
  85. package/build/dist/Tests/UI/Components/NavBar.test.js +13 -1
  86. package/build/dist/Tests/UI/Components/NavBar.test.js.map +1 -1
  87. package/build/dist/Tests/UI/Components/OrderedStatesList.test.js +1 -1
  88. package/build/dist/Tests/UI/Components/OrderedStatesList.test.js.map +1 -1
  89. package/build/dist/Tests/UI/Components/Pagination.test.js +6 -2
  90. package/build/dist/Tests/UI/Components/Pagination.test.js.map +1 -1
  91. package/build/dist/Tests/Utils/API.test.js +100 -9
  92. package/build/dist/Tests/Utils/API.test.js.map +1 -1
  93. package/build/dist/Tests/jest.setup.js +13 -0
  94. package/build/dist/Tests/jest.setup.js.map +1 -0
  95. package/build/dist/UI/Components/AI/AITemplates.js +218 -0
  96. package/build/dist/UI/Components/AI/AITemplates.js.map +1 -0
  97. package/build/dist/UI/Components/AI/GenerateFromAIModal.js +5 -238
  98. package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
  99. package/jest.config.json +18 -1
  100. package/package.json +1 -1
@@ -72,7 +72,7 @@ describe("Modal", () => {
72
72
  </Modal>,
73
73
  );
74
74
 
75
- expect(getByTestId("modal")).toHaveClass("sm:max-w-lg");
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("sm:max-w-3xl");
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.getByRole("bar-loader")).toBeInTheDocument();
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
- fireEvent.click(screen.getByText("Next"));
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
- fireEvent.click(screen.getByText("Previous"));
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);
@@ -396,33 +396,155 @@ describe.each(httpMethodTests)("$name", ({ name, method }: HTTPMethodType) => {
396
396
  });
397
397
  });
398
398
 
399
- describe.each(httpMethodTests)(".$name", ({ name, method }: HTTPMethodType) => {
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
- api.protocol,
409
- api.hostname,
410
- api.baseRoute.addRoute(route),
406
+ Protocol.HTTPS,
407
+ "catfact.ninja",
408
+ new Route("fact"),
411
409
  );
412
- const got: HTTPResponse<JSONObject> = await (api as any)[name]({
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,2 @@
1
+ // Mock for @azure packages
2
+ module.exports = {};
@@ -0,0 +1,2 @@
1
+ // Mock for botbuilder-stdlib package
2
+ module.exports = {};
@@ -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,5 @@
1
+ // Mock for locter package
2
+ module.exports = {
3
+ locate: jest.fn(),
4
+ locateSync: jest.fn(),
5
+ };
@@ -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,11 @@
1
+ // Mock for yaml package
2
+ module.exports = {
3
+ parse: jest.fn((str) => {
4
+ return {};
5
+ }),
6
+ stringify: jest.fn((obj) => {
7
+ return "";
8
+ }),
9
+ parseDocument: jest.fn(),
10
+ parseAllDocuments: jest.fn(),
11
+ };
@@ -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
+ ];