Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import { expect } from '@playwright/test';
import { expect, type Page } from '@playwright/test';

import { loadFixture } from '../../playwright/paths';
import { test } from '../../playwright/test';

const closeManageEnvironmentsDialog = async (page: Page) => {
const dialog = page.getByTestId('WorkspaceEnvironmentsDialog');
const closeButton = page.getByRole('button', { name: 'Close', exact: true });

await closeButton.click();

if (await dialog.isVisible()) {
await expect.soft(dialog).toHaveAttribute('data-save-state', 'idle');
await closeButton.click();
}

await expect.soft(page.getByRole('heading', { name: 'Manage Environments' })).toBeHidden();
};

test.describe('Environment Editor', () => {
test('manage environment', async ({ page, app, insomnia }) => {
const text = await loadFixture('environments.yaml');
Expand All @@ -18,7 +32,7 @@
await page.getByTestId('CreateEnvironmentDropdown').click();
await page.getByRole('menuitemradio', { name: 'Shared Environment' }).press('Enter');
await page.getByRole('row', { name: 'New Environment' }).click();
await page.getByRole('dialog').getByRole('button', { name: 'Close' }).click();
await closeManageEnvironmentsDialog(page);

await page.getByRole('option', { name: 'New Environment' }).press('Enter');
await page.getByRole('option', { name: 'New Environment' }).press('Escape');
Expand All @@ -43,8 +57,7 @@
await page.getByRole('row', { name: 'ExampleB' }).locator('input').fill('Gandalf');
await page.getByRole('row', { name: 'ExampleB' }).locator('input').press('Enter');

await page.getByRole('button', { name: 'Close', exact: true }).click();

await closeManageEnvironmentsDialog(page);
await page.getByRole('option', { name: 'Gandalf' }).press('Enter');
await page.getByRole('option', { name: 'Gandalf' }).press('Escape');

Expand All @@ -68,10 +81,7 @@
await dialog.getByTestId('CodeEditor').getByRole('textbox').press('Enter');
await dialog.getByTestId('CodeEditor').getByRole('textbox').fill('"testString":"Gandalf",');

// Blur the editor before closing so the debounce flush is triggered by the button's mousedown
await dialog.getByRole('button', { name: 'Close' }).click();
// Wait for the dialog to be gone before navigating away
await expect.soft(page.getByRole('heading', { name: 'Manage Environments' })).toBeHidden();
await closeManageEnvironmentsDialog(page);
await page.getByLabel('Manage collection environments').press('Escape');
await insomnia.navigationSidebar.clickRequestOrFolder('New Request');

Expand Down Expand Up @@ -110,17 +120,14 @@
// wait for modal to show
await expect.soft(page.getByRole('dialog').getByTestId('CodeEditor')).toBeVisible();
const bodyEditor = page.getByRole('dialog').getByTestId('CodeEditor').getByRole('textbox');
// move cursor right and input json string
await bodyEditor.focus();
await page.keyboard.press('ControlOrMeta+a');
await page.keyboard.type('{"anotherString":"kvAnotherStr","anotherNumber": 12345}');
// Submit and wait for the JSON modal to close before proceeding
await page.getByRole('button', { name: 'Modal Submit' }).click();
await expect.soft(page.getByRole('dialog', { name: 'Modal' })).toBeHidden();

// Close the environment editor and wait for the dialog to disappear before navigating
await page.getByRole('button', { name: 'Close', exact: true }).click();
await expect.soft(page.getByRole('heading', { name: 'Manage Environments' })).toBeHidden();
await closeManageEnvironmentsDialog(page);
await page.getByLabel('Manage collection environments').press('Escape');
await insomnia.navigationSidebar.clickRequestOrFolder('New Request');
await page.getByRole('button', { name: 'Send' }).click();
Expand All @@ -129,7 +136,7 @@
// check new environment value
await expect.soft(page.getByText('kvstring')).toBeVisible();
await page.getByText('kvstring').click();
await page.getByText('kvAnotherStr').click();

Check failure on line 139 in packages/insomnia-smoke-test/tests/smoke/environment-editor-interactions.test.ts

View workflow job for this annotation

GitHub Actions / test (2, 6)

[Smoke] › tests/smoke/environment-editor-interactions.test.ts:21:7 › Environment Editor › manage environment

1) [Smoke] › tests/smoke/environment-editor-interactions.test.ts:21:7 › Environment Editor › manage environment TimeoutError: locator.click: Timeout 30000ms exceeded. Call log: - waiting for getByText('kvAnotherStr') 137 | await expect.soft(page.getByText('kvstring')).toBeVisible(); 138 | await page.getByText('kvstring').click(); > 139 | await page.getByText('kvAnotherStr').click(); | ^ 140 | await page.getByText('12345').click(); 141 | }); 142 | at /home/runner/work/insomnia/insomnia/packages/insomnia-smoke-test/tests/smoke/environment-editor-interactions.test.ts:139:42
await page.getByText('12345').click();
});

Expand Down Expand Up @@ -164,8 +171,7 @@
await expect.soft(exampleStringRow).toHaveCSS('opacity', '0.4');

// Close the editor and wait for it to disappear
await page.getByRole('button', { name: 'Close', exact: true }).click();
await expect.soft(page.getByRole('heading', { name: 'Manage Environments' })).toBeHidden();
await closeManageEnvironmentsDialog(page);
await page.getByLabel('Manage collection environments').press('Escape');

// Send request — disabled sub-env variable should fall back to base environment
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { IconName, IconProp } from '@fortawesome/fontawesome-svg-core';
import React, { Fragment, useMemo, useRef, useState } from 'react';
import React, { Fragment, useEffect, useMemo, useRef, useState } from 'react';
import {
Button,
Dialog,
Expand Down Expand Up @@ -62,9 +62,11 @@ export const WorkspaceEnvironmentsEditModal = ({ onClose }: { onClose: () => voi
const updateEnvironmentFetcher = useEnvironmentUpdateActionFetcher();
const duplicateEnvironmentFetcher = useEnvironmentDuplicateActionFetcher();
const { toggleEnvironmentType } = useToggleEnvironmentType();
const pendingUpdateRef = useRef(false);

const { baseEnvironment, activeEnvironment, subEnvironments, activeProject, activeWorkspaceMeta } = routeData;
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<string>(activeEnvironment._id);
const [hasPendingUpdate, setHasPendingUpdate] = useState(false);
const isUsingInsomniaCloudSync = Boolean(
models.project.isRemoteProject(activeProject) && !activeWorkspaceMeta?.gitRepositoryId,
);
Expand Down Expand Up @@ -169,6 +171,8 @@ export const WorkspaceEnvironmentsEditModal = ({ onClose }: { onClose: () => voi

const handleEnvironmentChange = (value: EnvironmentInfo) => {
if (environmentEditorRef.current?.isValid() && selectedEnvironment) {
pendingUpdateRef.current = true;
setHasPendingUpdate(true);
const { object, propertyOrder } = value;

updateEnvironmentFetcher.submit({
Expand All @@ -186,6 +190,8 @@ export const WorkspaceEnvironmentsEditModal = ({ onClose }: { onClose: () => voi

const handleKVPairChange = (kvPairData: EnvironmentKvPairData[]) => {
if (selectedEnvironment) {
pendingUpdateRef.current = true;
setHasPendingUpdate(true);
const environmentData = getDataFromKVPair(kvPairData);
updateEnvironmentFetcher.submit({
organizationId,
Expand All @@ -200,6 +206,24 @@ export const WorkspaceEnvironmentsEditModal = ({ onClose }: { onClose: () => voi
});
}
};
useEffect(() => {
if (pendingUpdateRef.current && updateEnvironmentFetcher.state === 'idle') {
pendingUpdateRef.current = false;
setHasPendingUpdate(false);
}
}, [updateEnvironmentFetcher.state]);
const isEnvironmentMutationPending =
hasPendingUpdate ||
createEnvironmentFetcher.state !== 'idle' ||
deleteEnvironmentFetcher.state !== 'idle' ||
duplicateEnvironmentFetcher.state !== 'idle' ||
updateEnvironmentFetcher.state !== 'idle';
const requestClose = (close: () => void) => {
if (pendingUpdateRef.current || isEnvironmentMutationPending) {
return;
}
close();
};
const environmentsDragAndDrop = useDragAndDrop({
getItems: keys => [...keys].map(key => ({ 'text/plain': key.toString() })),
onReorder(e) {
Expand Down Expand Up @@ -249,26 +273,36 @@ export const WorkspaceEnvironmentsEditModal = ({ onClose }: { onClose: () => voi
<ModalOverlay
isOpen
onOpenChange={isOpen => {
!isOpen && onClose();
if (!isOpen && !pendingUpdateRef.current && !isEnvironmentMutationPending) {
onClose();
}
}}
className="fixed top-0 left-0 z-10 flex h-(--visual-viewport-height) w-full items-center justify-center bg-black/30"
>
<Modal
onOpenChange={isOpen => {
!isOpen && onClose();
if (!isOpen && !pendingUpdateRef.current && !isEnvironmentMutationPending) {
onClose();
}
}}
className="flex h-[calc(100%-var(--padding-xl))] w-[calc(100%-var(--padding-xl))] flex-col rounded-md border border-solid border-(--hl-sm) bg-(--color-bg) p-(--padding-lg) text-(--color-font)"
>
<Dialog className="flex h-full flex-1 flex-col overflow-hidden outline-hidden">
<Dialog
data-testid="WorkspaceEnvironmentsDialog"
data-save-state={isEnvironmentMutationPending ? 'saving' : 'idle'}
aria-busy={isEnvironmentMutationPending}
className="flex h-full flex-1 flex-col overflow-hidden outline-hidden"
>
{({ close }) => (
<div className="flex h-full flex-1 flex-col gap-4 overflow-hidden">
<div className="flex items-center justify-between gap-2">
<Heading slot="title" className="text-2xl">
Manage Environments
</Heading>
<Button
isDisabled={isEnvironmentMutationPending}
className="flex aspect-square h-6 shrink-0 items-center justify-center rounded-xs text-sm text-(--color-font) ring-1 ring-transparent transition-all hover:bg-(--hl-xs) focus:ring-(--hl-md) focus:ring-inset aria-pressed:bg-(--hl-sm)"
onPress={close}
onPress={() => requestClose(close)}
>
<Icon icon="x" />
</Button>
Expand Down Expand Up @@ -551,9 +585,15 @@ export const WorkspaceEnvironmentsEditModal = ({ onClose }: { onClose: () => voi
* Environment data can be used for <a href={docsTemplateTags}>Nunjucks Templating</a> in your
requests.
</p>
{isEnvironmentMutationPending && (
<p data-testid="WorkspaceEnvironmentsSaveStatus" className="text-sm italic">
Saving environments...
</p>
)}
</div>
<Button
onPress={close}
isDisabled={isEnvironmentMutationPending}
onPress={() => requestClose(close)}
className="rounded-xs border border-solid border-(--hl-md) px-3 py-2 text-(--color-font) transition-colors hover:no-underline"
>
Close
Expand Down
Loading