Terminal.test.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { act, screen } from "@testing-library/react";
  2. import { renderWithProviders } from "test-utils";
  3. import { vi, describe, afterEach, it, expect } from "vitest";
  4. import { Command, appendInput, appendOutput } from "#/state/commandSlice";
  5. import Terminal from "#/components/terminal/Terminal";
  6. global.ResizeObserver = vi.fn().mockImplementation(() => ({
  7. observe: vi.fn(),
  8. disconnect: vi.fn(),
  9. }));
  10. const mockTerminal = {
  11. open: vi.fn(),
  12. write: vi.fn(),
  13. writeln: vi.fn(),
  14. dispose: vi.fn(),
  15. onKey: vi.fn(),
  16. attachCustomKeyEventHandler: vi.fn(),
  17. loadAddon: vi.fn(),
  18. };
  19. vi.mock("@xterm/xterm", async (importOriginal) => ({
  20. ...(await importOriginal<typeof import("@xterm/xterm")>()),
  21. Terminal: vi.fn().mockImplementation(() => mockTerminal),
  22. }));
  23. const renderTerminal = (commands: Command[] = []) =>
  24. renderWithProviders(<Terminal secrets={[]} />, {
  25. preloadedState: {
  26. cmd: {
  27. commands,
  28. },
  29. },
  30. });
  31. describe.skip("Terminal", () => {
  32. afterEach(() => {
  33. vi.clearAllMocks();
  34. });
  35. it("should render a terminal", () => {
  36. renderTerminal();
  37. expect(screen.getByText("Terminal")).toBeInTheDocument();
  38. expect(mockTerminal.open).toHaveBeenCalledTimes(1);
  39. expect(mockTerminal.write).toHaveBeenCalledWith("$ ");
  40. });
  41. it("should load commands to the terminal", () => {
  42. renderTerminal([
  43. { type: "input", content: "INPUT" },
  44. { type: "output", content: "OUTPUT" },
  45. ]);
  46. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(1, "INPUT");
  47. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(2, "OUTPUT");
  48. });
  49. it("should write commands to the terminal", () => {
  50. const { store } = renderTerminal();
  51. act(() => {
  52. store.dispatch(appendInput("echo Hello"));
  53. store.dispatch(appendOutput("Hello"));
  54. });
  55. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(1, "echo Hello");
  56. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(2, "Hello");
  57. act(() => {
  58. store.dispatch(appendInput("echo World"));
  59. });
  60. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(3, "echo World");
  61. });
  62. it("should load and write commands to the terminal", () => {
  63. const { store } = renderTerminal([
  64. { type: "input", content: "echo Hello" },
  65. { type: "output", content: "Hello" },
  66. ]);
  67. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(1, "echo Hello");
  68. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(2, "Hello");
  69. act(() => {
  70. store.dispatch(appendInput("echo Hello"));
  71. });
  72. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(3, "echo Hello");
  73. });
  74. it("should end the line with a dollar sign after writing a command", () => {
  75. const { store } = renderTerminal();
  76. act(() => {
  77. store.dispatch(appendInput("echo Hello"));
  78. });
  79. expect(mockTerminal.writeln).toHaveBeenCalledWith("echo Hello");
  80. expect(mockTerminal.write).toHaveBeenCalledWith("$ ");
  81. });
  82. it("should display a custom symbol if output contains a custom symbol", () => {
  83. renderTerminal([
  84. { type: "input", content: "echo Hello" },
  85. {
  86. type: "output",
  87. content:
  88. "Hello\r\n\r\n[Python Interpreter: /openhands/poetry/openhands-5O4_aCHf-py3.12/bin/python]\nopenhands@659478cb008c:/workspace $ ",
  89. },
  90. ]);
  91. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(1, "echo Hello");
  92. expect(mockTerminal.writeln).toHaveBeenNthCalledWith(2, "Hello");
  93. expect(mockTerminal.write).toHaveBeenCalledWith(
  94. "\nopenhands@659478cb008c:/workspace $ ",
  95. );
  96. });
  97. // This test fails because it expects `disposeMock` to have been called before the component is unmounted.
  98. it.skip("should dispose the terminal on unmount", () => {
  99. const { unmount } = renderWithProviders(<Terminal secrets={[]} />);
  100. expect(mockTerminal.dispose).not.toHaveBeenCalled();
  101. unmount();
  102. expect(mockTerminal.dispose).toHaveBeenCalledTimes(1);
  103. });
  104. });