fix(ui): move environment create/edit into a dialog (#8391)

## Thinking Path

> - Paperclip is the open source control plane people use to manage AI
agents and the work they do.
> - This change lives in the board UI, specifically the instance
environments settings page where operators define reusable execution
targets.
> - That page currently rendered the create/edit environment form inline
near the bottom of the screen.
> - Because the form appeared away from the action that triggered it, it
was easy to miss and felt inconsistent with the rest of the UI.
> - This pull request moves environment creation and editing into a
proper centered dialog with the existing form behavior preserved.
> - The benefit is a more obvious, consistent, and easier-to-complete
settings flow for environment management.

## Linked Issues or Issue Description

No public GitHub issue matched this exact UI problem when I searched
related issues and PRs.

### Pre-submission checklist

- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on the latest released version of Paperclip (or can reproduce
on `master`).
- [x] I have confirmed the issue originates in Paperclip itself, not
adapter or local configuration.

### What happened?

On `Settings -> Instance settings -> Environments`, clicking create or
edit revealed the environment form inline near the bottom of the page.
Because the form appeared away from the action that triggered it, it was
easy to miss.

### Expected behavior

Create and edit should open a centered modal dialog with a dimmed
backdrop and the same form controls.

### Steps to reproduce

1. Open `Settings -> Instance settings -> Environments`.
2. Click the create or edit action for an environment.
3. Observe that the form expands inline on the page instead of opening
in a modal.

### Paperclip version or commit

Observed on current `master` before this PR, for example
[`e93d78b46`](https://github.com/paperclipai/paperclip/commit/e93d78b46).

### Deployment mode

Local dev (`pnpm dev`).

### Installation method

Built from source (`pnpm dev` / `pnpm build`).

### Agent adapter(s) involved

- [x] Not adapter-specific (core bug)

### Database mode

Embedded PGlite (default, `DATABASE_URL` unset).

### Access context

Board (human operator).

### Node.js version

Node.js 25.6.1 in the local contributor environment.

### Operating system

macOS local development environment.

### Relevant logs or output

None. This was a visible UI behavior issue rather than a logged
server/runtime error.

### Relevant config (if applicable)

Not config-related.

### Additional context

Related PR/search context checked before opening this fix. UI preview
screenshots are attached below.

### Privacy checklist

- [x] I have reviewed all pasted output for PII and redacted where
necessary.

## What Changed

- Moved the environment create/edit form in
`ui/src/pages/CompanyEnvironments.tsx` into a shared dialog.
- Added an explicit `Add environment` action near the saved environments
list and wired row edit actions to open the same dialog in prefilled
mode.
- Preserved existing save/test behavior while resetting dialog-local
mutation state when the modal opens or closes.
- Extended `ui/src/pages/CompanyEnvironments.test.tsx` to cover
add-open/cancel and edit-open/save dialog flows.
- Updated `ui/src/pages/CompanySettings.test.tsx` so the existing
environments coverage opens and inspects the dialog through the Radix
portal.

## Verification

- `node node_modules/vitest/vitest.mjs run
ui/src/pages/CompanyEnvironments.test.tsx`
- `node node_modules/vitest/vitest.mjs run
ui/src/pages/CompanySettings.test.tsx`
- Manual review:
  - Open `Settings -> Instance settings -> Environments`
- Click `Add environment` and confirm the form opens in a centered
dialog
- Click `Edit` on an existing environment and confirm the dialog opens
with existing values
  - Confirm `Cancel`, `Test`, and save actions still behave as expected
- Before/after screenshots:
- [Cancel environment
creation/edit](https://artifacts.cutter.sh/8391/run-1175b5c-2026-06-20T18-47-11/preview/clip-01.mp4)
- [Add a new
environment](https://artifacts.cutter.sh/8391/run-1175b5c-2026-06-20T18-47-11/preview/change-02.png)
- [Save an
environment](https://artifacts.cutter.sh/8391/run-1175b5c-2026-06-20T18-47-11/preview/change-03.png)

## Risks

- Low risk overall because the change is contained to the environments
page and keeps the existing form fields and mutations.
- The main behavior change is modal layout, so the biggest risk is
regressions in tall-form scrolling or smaller-screen dialog ergonomics.

> I checked [`ROADMAP.md`](ROADMAP.md). This is a tightly scoped UI
polish fix, not duplicate roadmap-level core feature work.

## Model Used

- OpenAI GPT-5.4 via the `codex_local` Paperclip adapter
- High reasoning mode with tool use, shell execution, git operations,
and targeted test execution
- Model-assisted authoring and verification of the UI and test changes

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Devin Foley
2026-06-20 12:26:53 -07:00
committed by GitHub
parent e93d78b46c
commit e9931f116a
3 changed files with 413 additions and 235 deletions
+99
View File
@@ -17,6 +17,7 @@ const mockEnvironmentsApi = vi.hoisted(() => ({
setDefault: vi.fn(), setDefault: vi.fn(),
})); }));
const mockInstanceSettingsApi = vi.hoisted(() => ({ const mockInstanceSettingsApi = vi.hoisted(() => ({
get: vi.fn(),
getExperimental: vi.fn(), getExperimental: vi.fn(),
})); }));
const mockSecretsApi = vi.hoisted(() => ({ const mockSecretsApi = vi.hoisted(() => ({
@@ -52,6 +53,13 @@ vi.mock("@/api/secrets", () => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
// Minimal Radix dialog dependency for jsdom.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).ResizeObserver = class {
observe() {}
unobserve() {}
disconnect() {}
};
async function act(callback: () => void | Promise<void>) { async function act(callback: () => void | Promise<void>) {
await callback(); await callback();
@@ -73,6 +81,14 @@ function testProviderButtons(container: HTMLElement): HTMLButtonElement[] {
}); });
} }
function findButton(root: ParentNode, label: string): HTMLButtonElement | undefined {
return Array.from(root.querySelectorAll("button")).find((button) => button.textContent?.trim() === label);
}
function getOpenDialog(): HTMLElement | null {
return document.body.querySelector("[role='dialog']");
}
describe("CompanyEnvironments — test provider button", () => { describe("CompanyEnvironments — test provider button", () => {
let container: HTMLDivElement; let container: HTMLDivElement;
let probeResolvers: Map<string, () => void>; let probeResolvers: Map<string, () => void>;
@@ -81,6 +97,7 @@ describe("CompanyEnvironments — test provider button", () => {
container = document.createElement("div"); container = document.createElement("div");
document.body.appendChild(container); document.body.appendChild(container);
probeResolvers = new Map(); probeResolvers = new Map();
mockInstanceSettingsApi.get.mockResolvedValue({ defaultEnvironmentId: null });
mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableEnvironments: true }); mockInstanceSettingsApi.getExperimental.mockResolvedValue({ enableEnvironments: true });
mockEnvironmentsApi.capabilities.mockResolvedValue({ adapters: [], sandboxProviders: {} }); mockEnvironmentsApi.capabilities.mockResolvedValue({ adapters: [], sandboxProviders: {} });
mockSecretsApi.list.mockResolvedValue([]); mockSecretsApi.list.mockResolvedValue([]);
@@ -88,6 +105,20 @@ describe("CompanyEnvironments — test provider button", () => {
{ id: "env-1", name: "Alpha", driver: "sandbox", description: null, config: { provider: "e2b" } }, { id: "env-1", name: "Alpha", driver: "sandbox", description: null, config: { provider: "e2b" } },
{ id: "env-2", name: "Beta", driver: "sandbox", description: null, config: { provider: "e2b" } }, { id: "env-2", name: "Beta", driver: "sandbox", description: null, config: { provider: "e2b" } },
]); ]);
mockEnvironmentsApi.create.mockImplementation(async (_companyId: string, body: { name: string }) => ({
id: "env-new",
name: body.name,
driver: "ssh",
description: null,
config: {},
}));
mockEnvironmentsApi.update.mockImplementation(async (environmentId: string, body: { name: string }) => ({
id: environmentId,
name: body.name,
driver: "sandbox",
description: null,
config: { provider: "e2b" },
}));
// Each probe stays pending until its resolver is called, so the testing // Each probe stays pending until its resolver is called, so the testing
// state remains observable and can be settled per environment. // state remains observable and can be settled per environment.
mockEnvironmentsApi.probe.mockImplementation( mockEnvironmentsApi.probe.mockImplementation(
@@ -175,4 +206,72 @@ describe("CompanyEnvironments — test provider button", () => {
expect(buttons[1].textContent?.trim()).toBe("Testing..."); expect(buttons[1].textContent?.trim()).toBe("Testing...");
expect(buttons[1].disabled).toBe(true); expect(buttons[1].disabled).toBe(true);
}); });
it("opens the add-environment form in a dialog and closes it on cancel", async () => {
const root = createRoot(container);
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
await act(async () => {
root.render(
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<CompanyEnvironments />
</TooltipProvider>
</QueryClientProvider>,
);
});
await flushReact();
await act(async () => {
findButton(container, "Add environment")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
await flushReact();
expect(getOpenDialog()?.textContent).toContain("Add environment");
await act(async () => {
findButton(document.body, "Cancel")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
await flushReact();
expect(getOpenDialog()).toBeNull();
});
it("opens the edit form in a dialog with existing values and closes after save", async () => {
const root = createRoot(container);
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
await act(async () => {
root.render(
<QueryClientProvider client={queryClient}>
<TooltipProvider>
<CompanyEnvironments />
</TooltipProvider>
</QueryClientProvider>,
);
});
await flushReact();
await act(async () => {
findButton(container, "Edit")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
await flushReact();
const dialog = getOpenDialog();
expect(dialog?.textContent).toContain("Edit environment");
expect(
Array.from(dialog?.querySelectorAll("input") ?? []).some((input) => (input as HTMLInputElement).value === "Alpha"),
).toBe(true);
await act(async () => {
findButton(document.body, "Save environment")?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
await flushReact();
expect(mockEnvironmentsApi.update).toHaveBeenCalledExactlyOnceWith(
"env-1",
expect.objectContaining({ name: "Alpha", driver: "sandbox" }),
);
expect(getOpenDialog()).toBeNull();
});
}); });
+93 -45
View File
@@ -13,6 +13,14 @@ import { environmentsApi } from "@/api/environments";
import { instanceSettingsApi } from "@/api/instanceSettings"; import { instanceSettingsApi } from "@/api/instanceSettings";
import { secretsApi } from "@/api/secrets"; import { secretsApi } from "@/api/secrets";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { EnvVarEditor } from "@/components/EnvVarEditor"; import { EnvVarEditor } from "@/components/EnvVarEditor";
import { JsonSchemaForm, getDefaultValues, validateJsonSchemaForm } from "@/components/JsonSchemaForm"; import { JsonSchemaForm, getDefaultValues, validateJsonSchemaForm } from "@/components/JsonSchemaForm";
import { useBreadcrumbs } from "@/context/BreadcrumbContext"; import { useBreadcrumbs } from "@/context/BreadcrumbContext";
@@ -181,6 +189,7 @@ export function CompanyEnvironments() {
const { setBreadcrumbs } = useBreadcrumbs(); const { setBreadcrumbs } = useBreadcrumbs();
const { pushToast } = useToast(); const { pushToast } = useToast();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [environmentDialogOpen, setEnvironmentDialogOpen] = useState(false);
const [editingEnvironmentId, setEditingEnvironmentId] = useState<string | null>(null); const [editingEnvironmentId, setEditingEnvironmentId] = useState<string | null>(null);
const [environmentForm, setEnvironmentForm] = useState<EnvironmentFormState>(createEmptyEnvironmentForm); const [environmentForm, setEnvironmentForm] = useState<EnvironmentFormState>(createEmptyEnvironmentForm);
const [probeResults, setProbeResults] = useState<Record<string, EnvironmentProbeResult | null>>({}); const [probeResults, setProbeResults] = useState<Record<string, EnvironmentProbeResult | null>>({});
@@ -245,13 +254,17 @@ export function CompanyEnvironments() {
return await environmentsApi.create(selectedCompanyId!, body); return await environmentsApi.create(selectedCompanyId!, body);
}, },
onSuccess: async (environment) => { onSuccess: async (environment) => {
const wasEditing = editingEnvironmentId !== null;
await queryClient.invalidateQueries({ await queryClient.invalidateQueries({
queryKey: queryKeys.environments.list(selectedCompanyId!), queryKey: queryKeys.environments.list(selectedCompanyId!),
}); });
setEnvironmentDialogOpen(false);
setEditingEnvironmentId(null); setEditingEnvironmentId(null);
setEnvironmentForm(createEmptyEnvironmentForm()); setEnvironmentForm(createEmptyEnvironmentForm());
environmentMutation.reset();
draftEnvironmentProbeMutation.reset();
pushToast({ pushToast({
title: editingEnvironmentId ? "Environment updated" : "Environment created", title: wasEditing ? "Environment updated" : "Environment created",
body: `${environment.name} is ready.`, body: `${environment.name} is ready.`,
tone: "success", tone: "success",
}); });
@@ -345,14 +358,26 @@ export function CompanyEnvironments() {
}); });
useEffect(() => { useEffect(() => {
setEnvironmentDialogOpen(false);
setEditingEnvironmentId(null); setEditingEnvironmentId(null);
setEnvironmentForm(createEmptyEnvironmentForm()); setEnvironmentForm(createEmptyEnvironmentForm());
setProbeResults({}); setProbeResults({});
setTestingEnvironmentId(null); setTestingEnvironmentId(null);
}, [selectedCompanyId]); }, [selectedCompanyId]);
function handleStartCreateEnvironment() {
setEditingEnvironmentId(null);
setEnvironmentForm(createEmptyEnvironmentForm());
environmentMutation.reset();
draftEnvironmentProbeMutation.reset();
setEnvironmentDialogOpen(true);
}
function handleEditEnvironment(environment: Environment) { function handleEditEnvironment(environment: Environment) {
environmentMutation.reset();
draftEnvironmentProbeMutation.reset();
setEditingEnvironmentId(environment.id); setEditingEnvironmentId(environment.id);
setEnvironmentDialogOpen(true);
if (environment.driver === "ssh") { if (environment.driver === "ssh") {
const ssh = readSshConfig(environment); const ssh = readSshConfig(environment);
setEnvironmentForm({ setEnvironmentForm({
@@ -396,9 +421,13 @@ export function CompanyEnvironments() {
}); });
} }
function handleCancelEnvironmentEdit() { function closeEnvironmentDialog() {
if (environmentMutation.isPending) return;
setEnvironmentDialogOpen(false);
setEditingEnvironmentId(null); setEditingEnvironmentId(null);
setEnvironmentForm(createEmptyEnvironmentForm()); setEnvironmentForm(createEmptyEnvironmentForm());
environmentMutation.reset();
draftEnvironmentProbeMutation.reset();
} }
const discoveredPluginSandboxProviders = Object.entries(environmentCapabilities?.sandboxProviders ?? {}) const discoveredPluginSandboxProviders = Object.entries(environmentCapabilities?.sandboxProviders ?? {})
@@ -590,6 +619,12 @@ export function CompanyEnvironments() {
</div> </div>
<div className="space-y-3"> <div className="space-y-3">
<div className="flex flex-wrap items-center justify-between gap-3">
<div className="text-sm font-medium">Saved environments</div>
<Button size="sm" onClick={handleStartCreateEnvironment}>
Add environment
</Button>
</div>
{savedEnvironments.length === 0 ? ( {savedEnvironments.length === 0 ? (
<div className="text-sm text-muted-foreground">No saved environments yet. Local remains the default until you add another target.</div> <div className="text-sm text-muted-foreground">No saved environments yet. Local remains the default until you add another target.</div>
) : ( ) : (
@@ -672,12 +707,28 @@ export function CompanyEnvironments() {
}) })
)} )}
</div> </div>
<div className="border-t border-border/60 pt-4">
<div className="mb-3 text-sm font-medium">
{editingEnvironmentId ? "Edit environment" : "Add environment"}
</div> </div>
<div className="space-y-3">
<Dialog
open={environmentDialogOpen}
onOpenChange={(open) => {
if (open) {
setEnvironmentDialogOpen(true);
return;
}
closeEnvironmentDialog();
}}
>
<DialogContent className="flex max-h-[calc(100dvh-2rem)] flex-col gap-0 overflow-hidden p-0 sm:max-w-4xl">
<DialogHeader className="border-b border-border/60 px-6 pb-4 pr-12 pt-6">
<DialogTitle>{editingEnvironmentId ? "Edit environment" : "Add environment"}</DialogTitle>
<DialogDescription>
Configure a reusable execution target for your agents.
</DialogDescription>
</DialogHeader>
<div className="min-h-0 flex-1 overflow-y-auto px-6 py-4">
<div className="space-y-4">
<Field label="Name" hint="Operator-facing name for this execution target."> <Field label="Name" hint="Operator-facing name for this execution target.">
<input <input
className="w-full rounded-md border border-border bg-transparent px-2.5 py-1.5 text-sm outline-none" className="w-full rounded-md border border-border bg-transparent px-2.5 py-1.5 text-sm outline-none"
@@ -872,9 +923,39 @@ export function CompanyEnvironments() {
/> />
</Field> </Field>
<div className="flex flex-wrap items-center gap-2"> {environmentMutation.isError ? (
<div className="text-xs text-destructive">
{environmentMutation.error instanceof Error
? environmentMutation.error.message
: "Failed to save environment"}
</div>
) : null}
{draftEnvironmentProbeMutation.data ? (
<div className={draftEnvironmentProbeMutation.data.ok ? "text-xs text-green-600" : "text-xs text-destructive"}>
{draftEnvironmentProbeMutation.data.summary}
</div>
) : null}
</div>
</div>
<DialogFooter className="border-t border-border/60 bg-background px-6 py-4">
<Button
variant="outline"
onClick={closeEnvironmentDialog}
disabled={environmentMutation.isPending}
>
Cancel
</Button>
{environmentForm.driver !== "local" ? (
<Button
variant="outline"
onClick={() => draftEnvironmentProbeMutation.mutate(environmentForm)}
disabled={draftEnvironmentProbeMutation.isPending || !environmentFormValid}
>
{draftEnvironmentProbeMutation.isPending ? "Testing..." : "Test"}
</Button>
) : null}
<Button <Button
size="sm"
onClick={() => environmentMutation.mutate(environmentForm)} onClick={() => environmentMutation.mutate(environmentForm)}
disabled={environmentMutation.isPending || !environmentFormValid} disabled={environmentMutation.isPending || !environmentFormValid}
> >
@@ -886,42 +967,9 @@ export function CompanyEnvironments() {
? "Save environment" ? "Save environment"
: "Create environment"} : "Create environment"}
</Button> </Button>
{editingEnvironmentId ? ( </DialogFooter>
<Button </DialogContent>
size="sm" </Dialog>
variant="ghost"
onClick={handleCancelEnvironmentEdit}
disabled={environmentMutation.isPending}
>
Cancel
</Button>
) : null}
{environmentForm.driver !== "local" ? (
<Button
size="sm"
variant="outline"
onClick={() => draftEnvironmentProbeMutation.mutate(environmentForm)}
disabled={draftEnvironmentProbeMutation.isPending || !environmentFormValid}
>
{draftEnvironmentProbeMutation.isPending ? "Testing..." : "Test"}
</Button>
) : null}
{environmentMutation.isError ? (
<span className="text-xs text-destructive">
{environmentMutation.error instanceof Error
? environmentMutation.error.message
: "Failed to save environment"}
</span>
) : null}
{draftEnvironmentProbeMutation.data ? (
<span className={draftEnvironmentProbeMutation.data.ok ? "text-xs text-green-600" : "text-xs text-destructive"}>
{draftEnvironmentProbeMutation.data.summary}
</span>
) : null}
</div>
</div>
</div>
</div>
</div> </div>
); );
} }
+36 -5
View File
@@ -97,6 +97,13 @@ vi.mock("../context/CompanyContext", () => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; (globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
class ResizeObserverStub {
observe() {}
unobserve() {}
disconnect() {}
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).ResizeObserver = (globalThis as any).ResizeObserver ?? ResizeObserverStub;
async function flushReact() { async function flushReact() {
await act(async () => { await act(async () => {
@@ -105,6 +112,10 @@ async function flushReact() {
}); });
} }
function getOpenDialog(): HTMLElement | null {
return document.body.querySelector("[role='dialog']");
}
describe("CompanyEnvironments", () => { describe("CompanyEnvironments", () => {
let container: HTMLDivElement; let container: HTMLDivElement;
@@ -198,7 +209,20 @@ describe("CompanyEnvironments", () => {
await flushReact(); await flushReact();
await flushReact(); await flushReact();
const driverSelect = Array.from(container.querySelectorAll("select")) const addEnvironmentButton = Array.from(container.querySelectorAll("button")).find(
(button) => button.textContent?.trim() === "Add environment",
);
expect(addEnvironmentButton).toBeTruthy();
await act(async () => {
addEnvironmentButton?.dispatchEvent(new MouseEvent("click", { bubbles: true }));
});
await flushReact();
const dialog = getOpenDialog();
expect(dialog).toBeTruthy();
const driverSelect = Array.from(dialog?.querySelectorAll("select") ?? [])
.find((select) => Array.from(select.options).some((option) => option.value === "ssh")) as .find((select) => Array.from(select.options).some((option) => option.value === "ssh")) as
| HTMLSelectElement | HTMLSelectElement
| undefined; | undefined;
@@ -254,7 +278,10 @@ describe("CompanyEnvironments", () => {
}); });
await flushReact(); await flushReact();
const driverSelect = Array.from(container.querySelectorAll("select")) const dialog = getOpenDialog();
expect(dialog).toBeTruthy();
const driverSelect = Array.from(dialog?.querySelectorAll("select") ?? [])
.find((select) => Array.from(select.options).some((option) => option.value === "ssh")) as .find((select) => Array.from(select.options).some((option) => option.value === "ssh")) as
| HTMLSelectElement | HTMLSelectElement
| undefined; | undefined;
@@ -337,8 +364,12 @@ describe("CompanyEnvironments", () => {
}); });
await flushReact(); await flushReact();
const providerSelect = Array.from(container.querySelectorAll("select")) const dialog = getOpenDialog();
.find((select) => Array.from(select.options).some((option) => option.value === "secure-plugin")) as HTMLSelectElement | undefined; expect(dialog).toBeTruthy();
const providerSelect = Array.from(dialog?.querySelectorAll("select") ?? []).find((select) =>
Array.from(select.options).some((option) => option.value === "secure-plugin"),
) as HTMLSelectElement | undefined;
expect(providerSelect).toBeTruthy(); expect(providerSelect).toBeTruthy();
await act(async () => { await act(async () => {
@@ -347,7 +378,7 @@ describe("CompanyEnvironments", () => {
}); });
await flushReact(); await flushReact();
const templateInput = Array.from(container.querySelectorAll("input")) const templateInput = Array.from(dialog?.querySelectorAll("input") ?? [])
.find((input) => (input as HTMLInputElement).value === "saved-template") as HTMLInputElement | undefined; .find((input) => (input as HTMLInputElement).value === "saved-template") as HTMLInputElement | undefined;
expect(templateInput?.value).toBe("saved-template"); expect(templateInput?.value).toBe("saved-template");