mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-21 16:27:18 +02:00
Address Greptile bootstrap claim feedback
This commit is contained in:
1 parent
d93680be79
commit
983ee24ee5
4 files changed
+49
-84
No files matched your search
+34
-78
@@ -42,9 +42,6 @@ vi.mock("@/lib/router", () => ({
|
||||
useParams: () => ({}),
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
async function flushReact() {
|
||||
await Promise.resolve();
|
||||
await new Promise((resolve) => window.setTimeout(resolve, 0));
|
||||
@@ -58,6 +55,29 @@ async function waitForText(container: HTMLElement, text: string) {
|
||||
expect(container.textContent).toContain(text);
|
||||
}
|
||||
|
||||
function renderGate(container: HTMLElement) {
|
||||
const root = createRoot(container);
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CloudAccessGate />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
function unmountRoot(root: ReturnType<typeof createRoot>) {
|
||||
flushSync(() => {
|
||||
root.unmount();
|
||||
});
|
||||
}
|
||||
|
||||
describe("CloudAccessGate", () => {
|
||||
let container: HTMLDivElement;
|
||||
|
||||
@@ -92,26 +112,13 @@ describe("CloudAccessGate", () => {
|
||||
keyId: null,
|
||||
});
|
||||
|
||||
const root = createRoot(container);
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CloudAccessGate />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
const root = renderGate(container);
|
||||
await waitForText(container, "No company access");
|
||||
|
||||
expect(container.textContent).toContain("No company access");
|
||||
expect(container.textContent).not.toContain("Outlet content");
|
||||
|
||||
flushSync(() => {
|
||||
root.unmount();
|
||||
});
|
||||
unmountRoot(root);
|
||||
});
|
||||
|
||||
it("allows authenticated users with company access through to the board", async () => {
|
||||
@@ -128,26 +135,13 @@ describe("CloudAccessGate", () => {
|
||||
keyId: null,
|
||||
});
|
||||
|
||||
const root = createRoot(container);
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CloudAccessGate />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
const root = renderGate(container);
|
||||
await waitForText(container, "Outlet content");
|
||||
|
||||
expect(container.textContent).toContain("Outlet content");
|
||||
expect(container.textContent).not.toContain("No company access");
|
||||
|
||||
flushSync(() => {
|
||||
root.unmount();
|
||||
});
|
||||
unmountRoot(root);
|
||||
});
|
||||
|
||||
it("shows browser sign-in setup for signed-out private bootstrap-pending instances", async () => {
|
||||
@@ -160,18 +154,7 @@ describe("CloudAccessGate", () => {
|
||||
});
|
||||
mockAuthApi.getSession.mockResolvedValue(null);
|
||||
|
||||
const root = createRoot(container);
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CloudAccessGate />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
const root = renderGate(container);
|
||||
await waitForText(container, "Finish setting up this Paperclip");
|
||||
|
||||
expect(container.textContent).toContain("Finish setting up this Paperclip");
|
||||
@@ -179,9 +162,7 @@ describe("CloudAccessGate", () => {
|
||||
expect(container.textContent).toContain("pnpm paperclipai auth bootstrap-ceo");
|
||||
expect(mockAccessApi.getCurrentBoardAccess).not.toHaveBeenCalled();
|
||||
|
||||
flushSync(() => {
|
||||
root.unmount();
|
||||
});
|
||||
unmountRoot(root);
|
||||
});
|
||||
|
||||
it("shows the claim action for signed-in private bootstrap-pending instances", async () => {
|
||||
@@ -198,18 +179,7 @@ describe("CloudAccessGate", () => {
|
||||
});
|
||||
mockAccessApi.claimBootstrapAdmin.mockResolvedValue({ claimed: true, userId: "user-1" });
|
||||
|
||||
const root = createRoot(container);
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CloudAccessGate />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
const root = renderGate(container);
|
||||
await waitForText(container, "Claim this instance");
|
||||
|
||||
expect(container.textContent).toContain("Claim this instance");
|
||||
@@ -227,10 +197,9 @@ describe("CloudAccessGate", () => {
|
||||
|
||||
expect(mockAccessApi.claimBootstrapAdmin).toHaveBeenCalledTimes(1);
|
||||
expect(container.textContent).toContain("You're the instance admin");
|
||||
expect(container.textContent).toContain("Continue to dashboard");
|
||||
|
||||
flushSync(() => {
|
||||
root.unmount();
|
||||
});
|
||||
unmountRoot(root);
|
||||
});
|
||||
|
||||
it("keeps public bootstrap-pending instances invite-only", async () => {
|
||||
@@ -246,18 +215,7 @@ describe("CloudAccessGate", () => {
|
||||
user: { id: "user-1", email: "user@example.com", name: "User", image: null },
|
||||
});
|
||||
|
||||
const root = createRoot(container);
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: { queries: { retry: false } },
|
||||
});
|
||||
|
||||
flushSync(() => {
|
||||
root.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CloudAccessGate />
|
||||
</QueryClientProvider>,
|
||||
);
|
||||
});
|
||||
const root = renderGate(container);
|
||||
await waitForText(container, "This Paperclip is waiting on its first admin");
|
||||
|
||||
expect(container.textContent).toContain("This Paperclip is waiting on its first admin");
|
||||
@@ -266,8 +224,6 @@ describe("CloudAccessGate", () => {
|
||||
expect(container.textContent).not.toContain("Sign in / Create account");
|
||||
expect(mockAccessApi.claimBootstrapAdmin).not.toHaveBeenCalled();
|
||||
|
||||
flushSync(() => {
|
||||
root.unmount();
|
||||
});
|
||||
unmountRoot(root);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export const BOOTSTRAP_FALLBACK_COMMAND = "pnpm paperclipai auth bootstrap-ceo";
|
||||
@@ -2,10 +2,9 @@ import type { ReactNode } from "react";
|
||||
import { Loader2, ShieldCheck, Terminal, TriangleAlert } from "lucide-react";
|
||||
import { Link } from "@/lib/router";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { BOOTSTRAP_FALLBACK_COMMAND } from "@/bootstrapSetup";
|
||||
import type { AuthSession } from "@paperclipai/shared";
|
||||
|
||||
const FALLBACK_COMMAND = "pnpm paperclipai auth bootstrap-ceo";
|
||||
|
||||
type BootstrapPendingPageProps = {
|
||||
claimAvailable: boolean;
|
||||
hasActiveInvite?: boolean;
|
||||
@@ -28,7 +27,7 @@ function CliFallback({ hasActiveInvite = false }: { hasActiveInvite?: boolean })
|
||||
: "Run this command on the host that runs Paperclip to print a one-time first-admin invite URL:"}
|
||||
</p>
|
||||
<pre className="mt-3 overflow-x-auto rounded-md border border-border bg-muted/30 p-3 font-mono text-xs">
|
||||
{FALLBACK_COMMAND}
|
||||
{BOOTSTRAP_FALLBACK_COMMAND}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
@@ -108,6 +107,11 @@ export function BootstrapPendingPage({
|
||||
<Loader2 className="size-4 animate-spin text-muted-foreground" aria-hidden />
|
||||
<span className="text-sm text-muted-foreground">Redirecting...</span>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<Button asChild variant="outline">
|
||||
<a href="/">Continue to dashboard</a>
|
||||
</Button>
|
||||
</div>
|
||||
</StateChrome>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ReactElement, ReactNode } from "react";
|
||||
import { Loader2, ShieldCheck, Terminal, TriangleAlert } from "lucide-react";
|
||||
import { BOOTSTRAP_FALLBACK_COMMAND } from "@/bootstrapSetup";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
type LabFixtureKey =
|
||||
@@ -28,8 +29,6 @@ const FIXTURE_ORDER: LabFixtureKey[] = [
|
||||
"public-invite-only",
|
||||
];
|
||||
|
||||
const FALLBACK_COMMAND = "pnpm paperclipai auth bootstrap-ceo";
|
||||
|
||||
function CliFallback({ hasActiveInvite }: { hasActiveInvite: boolean }) {
|
||||
return (
|
||||
<div className="mt-6 border-t border-border pt-5">
|
||||
@@ -43,7 +42,7 @@ function CliFallback({ hasActiveInvite }: { hasActiveInvite: boolean }) {
|
||||
: "Run this command on the host that runs Paperclip to print a one‑time first‑admin invite URL:"}
|
||||
</p>
|
||||
<pre className="mt-3 overflow-x-auto rounded-md border border-border bg-muted/30 p-3 font-mono text-xs">
|
||||
{FALLBACK_COMMAND}
|
||||
{BOOTSTRAP_FALLBACK_COMMAND}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
@@ -170,6 +169,11 @@ function ClaimSuccess() {
|
||||
<Loader2 className="size-4 animate-spin text-muted-foreground" aria-hidden />
|
||||
<span className="text-sm text-muted-foreground">Redirecting…</span>
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
<Button asChild variant="outline">
|
||||
<a href="/">Continue to dashboard</a>
|
||||
</Button>
|
||||
</div>
|
||||
</StateChrome>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user