use-terminal.test.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. import { beforeAll, describe, expect, it, vi } from "vitest";
  2. import { render } from "@testing-library/react";
  3. import { afterEach } from "node:test";
  4. import { useTerminal } from "#/hooks/useTerminal";
  5. import { Command } from "#/state/commandSlice";
  6. import { WsClientProvider } from "#/context/ws-client-provider";
  7. import { ReactNode } from "react";
  8. interface TestTerminalComponentProps {
  9. commands: Command[];
  10. secrets: string[];
  11. }
  12. function TestTerminalComponent({
  13. commands,
  14. secrets,
  15. }: TestTerminalComponentProps) {
  16. const ref = useTerminal(commands, secrets);
  17. return <div ref={ref} />;
  18. }
  19. interface WrapperProps {
  20. children: ReactNode;
  21. }
  22. function Wrapper({children}: WrapperProps) {
  23. return (
  24. <WsClientProvider enabled={true} token="NO_JWT" ghToken="NO_GITHUB" settings={null}>{children}</WsClientProvider>
  25. )
  26. }
  27. describe("useTerminal", () => {
  28. const mockTerminal = vi.hoisted(() => ({
  29. loadAddon: vi.fn(),
  30. open: vi.fn(),
  31. write: vi.fn(),
  32. writeln: vi.fn(),
  33. onKey: vi.fn(),
  34. attachCustomKeyEventHandler: vi.fn(),
  35. dispose: vi.fn(),
  36. }));
  37. beforeAll(() => {
  38. // mock ResizeObserver
  39. window.ResizeObserver = vi.fn().mockImplementation(() => ({
  40. observe: vi.fn(),
  41. unobserve: vi.fn(),
  42. disconnect: vi.fn(),
  43. }));
  44. // mock Terminal
  45. vi.mock("@xterm/xterm", async (importOriginal) => ({
  46. ...(await importOriginal<typeof import("@xterm/xterm")>()),
  47. Terminal: vi.fn().mockImplementation(() => mockTerminal),
  48. }));
  49. });
  50. afterEach(() => {
  51. vi.clearAllMocks();
  52. });
  53. it("should render", () => {
  54. render(<TestTerminalComponent commands={[]} secrets={[]} />, {
  55. wrapper: Wrapper,
  56. });
  57. });
  58. it("should render the commands in the terminal", () => {
  59. const commands: Command[] = [
  60. { content: "echo hello", type: "input" },
  61. { content: "hello", type: "output" },
  62. ];
  63. render(<TestTerminalComponent commands={commands} secrets={[]} />, {
  64. wrapper: Wrapper,
  65. });
  66. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(1, "echo hello");
  67. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(2, "hello");
  68. });
  69. it("should hide secrets in the terminal", () => {
  70. const secret = "super_secret_github_token";
  71. const anotherSecret = "super_secret_another_token";
  72. const commands: Command[] = [
  73. {
  74. content: `export GITHUB_TOKEN=${secret},${anotherSecret},${secret}`,
  75. type: "input",
  76. },
  77. { content: secret, type: "output" },
  78. ];
  79. render(
  80. <TestTerminalComponent
  81. commands={commands}
  82. secrets={[secret, anotherSecret]}
  83. />,
  84. {
  85. wrapper: Wrapper,
  86. },
  87. );
  88. // BUG: `vi.clearAllMocks()` does not clear the number of calls
  89. // therefore, we need to assume the order of the calls based
  90. // on the test order
  91. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(
  92. 3,
  93. `export GITHUB_TOKEN=${"*".repeat(10)},${"*".repeat(10)},${"*".repeat(10)}`,
  94. );
  95. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(4, "*".repeat(10));
  96. });
  97. });