probe-terminal.test.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /**
  2. * @vitest-environment happy-dom
  3. */
  4. import { describe, expect, test } from "vitest";
  5. // Test the pure functions extracted from ProbeTerminal component
  6. // These handle log formatting, filtering, and status determination
  7. describe("ProbeTerminal - formatTime", () => {
  8. function formatTime(date: Date | string): string {
  9. const d = typeof date === "string" ? new Date(date) : date;
  10. return d.toLocaleTimeString(undefined, {
  11. hour: "2-digit",
  12. minute: "2-digit",
  13. second: "2-digit",
  14. });
  15. }
  16. test("should format Date object correctly", () => {
  17. const date = new Date("2024-01-15T10:30:45Z");
  18. const result = formatTime(date);
  19. // Result format depends on locale, but should contain time components
  20. expect(result).toMatch(/\d{1,2}:\d{2}:\d{2}/);
  21. });
  22. test("should format ISO string correctly", () => {
  23. const result = formatTime("2024-01-15T10:30:45Z");
  24. expect(result).toMatch(/\d{1,2}:\d{2}:\d{2}/);
  25. });
  26. test("should handle different date formats", () => {
  27. const result1 = formatTime("2024-01-15T00:00:00Z");
  28. const result2 = formatTime("2024-12-31T23:59:59Z");
  29. expect(result1).toMatch(/\d{1,2}:\d{2}:\d{2}/);
  30. expect(result2).toMatch(/\d{1,2}:\d{2}:\d{2}/);
  31. });
  32. });
  33. describe("ProbeTerminal - formatLatency", () => {
  34. function formatLatency(ms: number | null): string {
  35. if (ms === null) return "-";
  36. if (ms < 1000) return `${Math.round(ms)}ms`;
  37. return `${(ms / 1000).toFixed(2)}s`;
  38. }
  39. test("should return dash for null values", () => {
  40. expect(formatLatency(null)).toBe("-");
  41. });
  42. test("should format milliseconds for values < 1000", () => {
  43. expect(formatLatency(0)).toBe("0ms");
  44. expect(formatLatency(100)).toBe("100ms");
  45. expect(formatLatency(500)).toBe("500ms");
  46. expect(formatLatency(999)).toBe("999ms");
  47. });
  48. test("should format seconds for values >= 1000", () => {
  49. expect(formatLatency(1000)).toBe("1.00s");
  50. expect(formatLatency(1500)).toBe("1.50s");
  51. expect(formatLatency(2345)).toBe("2.35s");
  52. expect(formatLatency(10000)).toBe("10.00s");
  53. });
  54. test("should round milliseconds to nearest integer", () => {
  55. expect(formatLatency(100.4)).toBe("100ms");
  56. expect(formatLatency(100.5)).toBe("101ms");
  57. expect(formatLatency(100.9)).toBe("101ms");
  58. });
  59. });
  60. describe("ProbeTerminal - getLogLevel", () => {
  61. type LogLevel = "success" | "error" | "warn";
  62. interface ProbeLog {
  63. ok: boolean;
  64. errorType: string | null;
  65. }
  66. function getLogLevel(log: ProbeLog): LogLevel {
  67. if (log.ok) return "success";
  68. if (log.errorType === "timeout") return "warn";
  69. return "error";
  70. }
  71. test("should return success for ok logs", () => {
  72. expect(getLogLevel({ ok: true, errorType: null })).toBe("success");
  73. expect(getLogLevel({ ok: true, errorType: "timeout" })).toBe("success");
  74. });
  75. test("should return warn for timeout errors", () => {
  76. expect(getLogLevel({ ok: false, errorType: "timeout" })).toBe("warn");
  77. });
  78. test("should return error for other failures", () => {
  79. expect(getLogLevel({ ok: false, errorType: null })).toBe("error");
  80. expect(getLogLevel({ ok: false, errorType: "connection_refused" })).toBe("error");
  81. expect(getLogLevel({ ok: false, errorType: "ssl_error" })).toBe("error");
  82. expect(getLogLevel({ ok: false, errorType: "dns_error" })).toBe("error");
  83. });
  84. });
  85. describe("ProbeTerminal - log filtering", () => {
  86. interface MockLog {
  87. id: number;
  88. errorMessage: string | null;
  89. errorType: string | null;
  90. statusCode: number | null;
  91. }
  92. function filterLogs(logs: MockLog[], filter: string): MockLog[] {
  93. if (!filter) return logs;
  94. const searchLower = filter.toLowerCase();
  95. return logs.filter((log) => {
  96. return (
  97. log.errorMessage?.toLowerCase().includes(searchLower) ||
  98. log.errorType?.toLowerCase().includes(searchLower) ||
  99. log.statusCode?.toString().includes(searchLower)
  100. );
  101. });
  102. }
  103. const mockLogs: MockLog[] = [
  104. { id: 1, errorMessage: "Connection refused", errorType: "connection_error", statusCode: null },
  105. { id: 2, errorMessage: null, errorType: "timeout", statusCode: null },
  106. { id: 3, errorMessage: "SSL certificate error", errorType: "ssl_error", statusCode: null },
  107. { id: 4, errorMessage: null, errorType: null, statusCode: 200 },
  108. { id: 5, errorMessage: null, errorType: null, statusCode: 500 },
  109. { id: 6, errorMessage: "Bad Gateway", errorType: "http_error", statusCode: 502 },
  110. ];
  111. test("should return all logs when filter is empty", () => {
  112. expect(filterLogs(mockLogs, "")).toHaveLength(6);
  113. expect(filterLogs(mockLogs, "")).toEqual(mockLogs);
  114. });
  115. test("should filter by error message", () => {
  116. const result = filterLogs(mockLogs, "connection");
  117. expect(result).toHaveLength(1);
  118. expect(result[0].id).toBe(1);
  119. });
  120. test("should filter by error type", () => {
  121. const result = filterLogs(mockLogs, "timeout");
  122. expect(result).toHaveLength(1);
  123. expect(result[0].id).toBe(2);
  124. });
  125. test("should filter by status code", () => {
  126. const result = filterLogs(mockLogs, "500");
  127. expect(result).toHaveLength(1);
  128. expect(result[0].id).toBe(5);
  129. });
  130. test("should be case insensitive", () => {
  131. const result1 = filterLogs(mockLogs, "SSL");
  132. const result2 = filterLogs(mockLogs, "ssl");
  133. expect(result1).toHaveLength(1);
  134. expect(result2).toHaveLength(1);
  135. expect(result1[0].id).toBe(result2[0].id);
  136. });
  137. test("should match partial strings", () => {
  138. const result = filterLogs(mockLogs, "error");
  139. // Should match: connection_error, ssl_error, http_error, and "SSL certificate error"
  140. expect(result.length).toBeGreaterThan(0);
  141. });
  142. test("should return empty array when no matches", () => {
  143. const result = filterLogs(mockLogs, "nonexistent");
  144. expect(result).toHaveLength(0);
  145. });
  146. });
  147. describe("ProbeTerminal - levelConfig", () => {
  148. const levelConfig = {
  149. success: {
  150. label: "OK",
  151. color: "text-emerald-500",
  152. bgColor: "bg-emerald-500/5",
  153. borderColor: "border-l-emerald-500",
  154. },
  155. error: {
  156. label: "FAIL",
  157. color: "text-rose-500",
  158. bgColor: "bg-rose-500/5",
  159. borderColor: "border-l-rose-500",
  160. },
  161. warn: {
  162. label: "WARN",
  163. color: "text-amber-500",
  164. bgColor: "bg-amber-500/5",
  165. borderColor: "border-l-amber-500",
  166. },
  167. };
  168. test("success level should have green colors", () => {
  169. expect(levelConfig.success.color).toContain("emerald");
  170. expect(levelConfig.success.bgColor).toContain("emerald");
  171. expect(levelConfig.success.borderColor).toContain("emerald");
  172. });
  173. test("error level should have red colors", () => {
  174. expect(levelConfig.error.color).toContain("rose");
  175. expect(levelConfig.error.bgColor).toContain("rose");
  176. expect(levelConfig.error.borderColor).toContain("rose");
  177. });
  178. test("warn level should have amber colors", () => {
  179. expect(levelConfig.warn.color).toContain("amber");
  180. expect(levelConfig.warn.bgColor).toContain("amber");
  181. expect(levelConfig.warn.borderColor).toContain("amber");
  182. });
  183. test("each level should have distinct labels", () => {
  184. expect(levelConfig.success.label).toBe("OK");
  185. expect(levelConfig.error.label).toBe("FAIL");
  186. expect(levelConfig.warn.label).toBe("WARN");
  187. });
  188. });
  189. describe("ProbeTerminal - maxLines slicing", () => {
  190. function sliceLogs<T>(logs: T[], maxLines: number): T[] {
  191. return logs.slice(-maxLines);
  192. }
  193. test("should return all logs when count <= maxLines", () => {
  194. const logs = [1, 2, 3, 4, 5];
  195. expect(sliceLogs(logs, 10)).toEqual([1, 2, 3, 4, 5]);
  196. expect(sliceLogs(logs, 5)).toEqual([1, 2, 3, 4, 5]);
  197. });
  198. test("should return last N logs when count > maxLines", () => {
  199. const logs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  200. expect(sliceLogs(logs, 5)).toEqual([6, 7, 8, 9, 10]);
  201. expect(sliceLogs(logs, 3)).toEqual([8, 9, 10]);
  202. });
  203. test("should handle empty array", () => {
  204. expect(sliceLogs([], 10)).toEqual([]);
  205. });
  206. test("should handle maxLines of 1", () => {
  207. const logs = [1, 2, 3];
  208. expect(sliceLogs(logs, 1)).toEqual([3]);
  209. });
  210. });