mirror of
https://github.com/mgeeky/decode-spam-headers.git
synced 2026-02-22 13:33:30 +01:00
MAESTRO: wire header analyzer controls and captcha
This commit is contained in:
@@ -18,8 +18,10 @@ const { submitSpy, cancelSpy, useAnalysisState } = vi.hoisted(() => {
|
||||
progress: null,
|
||||
result: null,
|
||||
error: null,
|
||||
captchaChallenge: null,
|
||||
submit: submitSpy,
|
||||
cancel: cancelSpy,
|
||||
clearCaptchaChallenge: vi.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -86,6 +88,7 @@ const resetUseAnalysisState = (): void => {
|
||||
useAnalysisState.progress = null;
|
||||
useAnalysisState.result = null;
|
||||
useAnalysisState.error = null;
|
||||
useAnalysisState.captchaChallenge = null;
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
@@ -152,4 +155,15 @@ describe("Home page", () => {
|
||||
const results = container.querySelector('[data-testid="analysis-results"]');
|
||||
expect(results).not.toBeNull();
|
||||
});
|
||||
|
||||
it("renders the captcha modal when rate limited", () => {
|
||||
useAnalysisState.captchaChallenge = {
|
||||
challengeToken: "challenge-123",
|
||||
imageBase64: "image-data",
|
||||
};
|
||||
|
||||
const { container } = render(<Home />);
|
||||
const modal = container.querySelector('[data-testid="captcha-challenge"]');
|
||||
expect(modal).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,8 +18,10 @@ const { submitSpy, cancelSpy, useAnalysisState } = vi.hoisted(() => {
|
||||
progress: null,
|
||||
result: null,
|
||||
error: null,
|
||||
captchaChallenge: null,
|
||||
submit: submitSpy,
|
||||
cancel: cancelSpy,
|
||||
clearCaptchaChallenge: vi.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -118,6 +120,7 @@ const resetUseAnalysisState = (): void => {
|
||||
useAnalysisState.progress = null;
|
||||
useAnalysisState.result = null;
|
||||
useAnalysisState.error = null;
|
||||
useAnalysisState.captchaChallenge = null;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { act } from "react-dom/test-utils";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { apiClient } from "../lib/api-client";
|
||||
import { ApiError, apiClient } from "../lib/api-client";
|
||||
import type { AnalysisConfig, AnalysisProgress, AnalysisReport } from "../types/analysis";
|
||||
import useAnalysis from "../hooks/useAnalysis";
|
||||
|
||||
@@ -105,7 +105,8 @@ const timeoutReport: AnalysisReport = {
|
||||
};
|
||||
|
||||
const AnalysisHarness = ({ request, onStatusChange }: HarnessProps) => {
|
||||
const { status, progress, result, error, submit, cancel } = useAnalysis();
|
||||
const { status, progress, result, error, submit, cancel, captchaChallenge, clearCaptchaChallenge } =
|
||||
useAnalysis();
|
||||
|
||||
useEffect(() => {
|
||||
onStatusChange?.(status);
|
||||
@@ -118,12 +119,16 @@ const AnalysisHarness = ({ request, onStatusChange }: HarnessProps) => {
|
||||
<span data-testid="percentage">{progress?.percentage ?? ""}</span>
|
||||
<span data-testid="result-total">{result?.metadata.totalTests ?? ""}</span>
|
||||
<span data-testid="error">{error ?? ""}</span>
|
||||
<span data-testid="captcha-token">{captchaChallenge?.challengeToken ?? ""}</span>
|
||||
<button data-testid="submit" onClick={() => submit(request)}>
|
||||
Submit
|
||||
</button>
|
||||
<button data-testid="cancel" onClick={() => cancel()}>
|
||||
Cancel
|
||||
</button>
|
||||
<button data-testid="clear-captcha" onClick={() => clearCaptchaChallenge()}>
|
||||
Clear Captcha
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -260,4 +265,24 @@ describe("useAnalysis", () => {
|
||||
expect(abortSignal?.aborted).toBe(true);
|
||||
expect(statuses).toContain("idle");
|
||||
});
|
||||
|
||||
it("captures captcha challenges on rate limit errors", async () => {
|
||||
vi.spyOn(apiClient, "stream").mockRejectedValue(
|
||||
new ApiError("Too many requests", 429, {
|
||||
captchaChallenge: { challengeToken: "abc123", imageBase64: "image-data" },
|
||||
}),
|
||||
);
|
||||
|
||||
const { container } = render(<AnalysisHarness request={baseRequest} />);
|
||||
|
||||
act(() => {
|
||||
getByTestId(container, "submit").dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await flushPromises();
|
||||
});
|
||||
|
||||
expect(getByTestId(container, "captcha-token").textContent).toBe("abc123");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user