feat(FN-2407): add blocking auth token recovery flow
- Emit an auth-token recovery event when dashboard daemon calls fail with bearer-token 401 responses - Render a non-dismissable recovery dialog with set-token and clear-token reload actions - Keep the recovery overlay topmost while aligning modal markup with shared modal conventions - Add focused regression coverage for dialog behavior and include a patch changeset for @runfusion/fusion
This commit is contained in:
5
.changeset/auth-token-recovery-dialog.md
Normal file
5
.changeset/auth-token-recovery-dialog.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
Add a blocking dashboard token-recovery dialog that appears only for daemon bearer-token 401 responses, with set-token or clear-token recovery actions that reload the app.
|
||||
@@ -2,13 +2,8 @@
|
||||
z-index: 210;
|
||||
}
|
||||
|
||||
.auth-token-recovery-modal {
|
||||
width: min(560px, calc(100vw - (var(--space-xl) * 2)));
|
||||
}
|
||||
|
||||
.auth-token-recovery-header h2 {
|
||||
.auth-token-recovery-header h3 {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.auth-token-recovery-content {
|
||||
|
||||
@@ -44,14 +44,14 @@ export function AuthTokenRecoveryDialog({ open }: AuthTokenRecoveryDialogProps)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="modal auth-token-recovery-modal"
|
||||
className="modal modal-md"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="auth-token-recovery-title"
|
||||
aria-describedby="auth-token-recovery-description"
|
||||
>
|
||||
<div className="modal-header auth-token-recovery-header">
|
||||
<h2 id="auth-token-recovery-title">Authentication token required</h2>
|
||||
<h3 id="auth-token-recovery-title">Authentication token required</h3>
|
||||
</div>
|
||||
|
||||
<div className="auth-token-recovery-content">
|
||||
|
||||
@@ -29,9 +29,19 @@ describe("AuthTokenRecoveryDialog", () => {
|
||||
it("renders a blocking dialog with disabled set button until token is entered", () => {
|
||||
render(<AuthTokenRecoveryDialog open={true} />);
|
||||
|
||||
expect(screen.getByRole("dialog", { name: "Authentication token required" })).toBeInTheDocument();
|
||||
const dialog = screen.getByRole("dialog", { name: "Authentication token required" });
|
||||
expect(dialog).toBeInTheDocument();
|
||||
expect(screen.queryByRole("button", { name: /close/i })).toBeNull();
|
||||
|
||||
const overlay = dialog.closest(".auth-token-recovery-overlay");
|
||||
expect(overlay).toBeTruthy();
|
||||
|
||||
if (!overlay) {
|
||||
throw new Error("Expected auth token recovery overlay");
|
||||
}
|
||||
|
||||
expect(dialog.className).toContain("modal-md");
|
||||
|
||||
const setTokenButton = screen.getByRole("button", { name: "Set token and reload" });
|
||||
expect(setTokenButton).toBeDisabled();
|
||||
|
||||
|
||||
@@ -2361,7 +2361,7 @@ describe("ModelOnboardingModal", () => {
|
||||
expect(mockFetchGlobalSettings).toHaveBeenCalled();
|
||||
|
||||
// The model dropdown should be pre-populated with the saved default
|
||||
const dropdown = screen.getByTestId("mock-model-dropdown") as HTMLSelectElement;
|
||||
const dropdown = await screen.findByTestId("mock-model-dropdown") as HTMLSelectElement;
|
||||
expect(dropdown.value).toBe("anthropic/claude-sonnet-4-5");
|
||||
});
|
||||
|
||||
@@ -2407,7 +2407,7 @@ describe("ModelOnboardingModal", () => {
|
||||
});
|
||||
|
||||
// The model dropdown should be pre-populated with the saved default
|
||||
const dropdown = screen.getByTestId("mock-model-dropdown") as HTMLSelectElement;
|
||||
const dropdown = await screen.findByTestId("mock-model-dropdown") as HTMLSelectElement;
|
||||
expect(dropdown.value).toBe("openai/gpt-4o");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user