Skip to content

Commit

Permalink
Fix/mock-feedback-4 (#7277)
Browse files Browse the repository at this point in the history
* ignore .node files

* add header count

* add status bar

* copy in full url

* placeholder and language

* validate url

* validate path

* fix possible dupe route bug

* clean up language

* layout

* fix submit

* error messages

* fix test
  • Loading branch information
jackkav authored Apr 18, 2024
1 parent 8289377 commit 883f907
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 56 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ packages/insomnia-smoke-test/traces
*.tsbuildinfo
dist
.history
*.node
4 changes: 3 additions & 1 deletion packages/insomnia-smoke-test/tests/smoke/mock.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@ test('can make a mock route', async ({ page }) => {
await page.getByLabel('New Mock Server').click();
await page.getByRole('button', { name: 'Create', exact: true }).click();
await page.getByRole('button', { name: 'New Mock Route' }).click();
await page.locator('#prompt-input').fill('/123');
await page.getByRole('button', { name: 'Create' }).click();
await page.getByLabel('Project Actions').click();
await page.getByText('Rename').click();
await page.locator('#prompt-input').fill('/123');
await page.locator('#prompt-input').fill('/456');
await page.getByRole('button', { name: 'Rename' }).click();

await page.getByRole('button', { name: 'Test' }).click();
Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/ui/components/design-empty-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export const DesignEmptyState: FC<Props> = ({ onImport }) => {
<span className="truncate flex items-center gap-2">
Or import an existing OpenAPI spec or
<Button
className="pointer-events-auto font-bold text-[--hl-lg] hover:text-[--hl] focus:text-[--hl] transition-colors"
className="underline pointer-events-auto font-bold text-[--hl-lg] hover:text-[--hl] focus:text-[--hl] transition-colors"
onPress={async () => {
const spec = await import('./example-openapi-spec');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import { RequestLoaderData } from '../../routes/request';
import { WorkspaceLoaderData } from '../../routes/workspace';
import { HelpTooltip } from '../help-tooltip';
import { Icon } from '../icon';
import { showPrompt } from '../modals';
import { showModal, showPrompt } from '../modals';
import { AlertModal } from '../modals/alert-modal';

export const MockResponseExtractor = () => {
const {
Expand All @@ -32,11 +33,11 @@ export const MockResponseExtractor = () => {
const [selectedMockRoute, setSelectedMockRoute] = useState('');
return (
<div className="px-32 h-full flex flex-col justify-center">
<div className="flex place-content-center text-9xl pb-2">
<div className="flex place-content-center text-9xl pb-2 text-[--hl-md]">
<Icon icon="cube" />
</div>
<div className="flex place-content-center pb-2">
Export this response to a mock route.
Copy this response to a new mock route or overwrite an existing one.
</div>
<form
onSubmit={async e => {
Expand All @@ -61,6 +62,7 @@ export const MockResponseExtractor = () => {
} catch (e) {
console.log(e);
}
// Create new mock server and route
if (!selectedMockServer) {
showPrompt({
title: 'Create Mock Route',
Expand Down Expand Up @@ -89,6 +91,7 @@ export const MockResponseExtractor = () => {
});
return;
}
// Create new mock route
if (!selectedMockRoute) {
showPrompt({
title: 'Create Mock Route',
Expand All @@ -97,9 +100,14 @@ export const MockResponseExtractor = () => {
onComplete: async name => {
invariant(activeResponse, 'Active response must be defined');
const body = await fs.readFile(activeResponse.bodyPath);

// setSelectedMockRoute(newRoute._id);

const hasRouteInServer = mockServerAndRoutes.find(s => s._id === selectedMockServer)?.routes.find(r => r.name === name);
if (hasRouteInServer) {
showModal(AlertModal, {
title: 'Error',
message: `Path "${name}" must be unique. Please enter a different name.`,
});
return;
};
fetcher.submit(
JSON.stringify({
name: name,
Expand Down Expand Up @@ -152,7 +160,7 @@ export const MockResponseExtractor = () => {
<label>
Choose Mock Route
<HelpTooltip position="top" className="space-left">
Select from created mock routes to send this request to
Select from created mock routes to overwrite with this response
</HelpTooltip>
<select
value={selectedMockRoute}
Expand All @@ -173,22 +181,22 @@ export const MockResponseExtractor = () => {
</label>
</div>
</div>
<div className="flex justify-end">
<div className="flex mt-2">
<Button
type="submit"
className="mr-2 hover:no-underline bg-[--color-surprise] hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font-surprise] transition-colors rounded-sm"
>
{selectedMockRoute ? 'Overwrite' : 'Create'}
</Button>
<Button
isDisabled={!selectedMockServer || !selectedMockRoute}
onPress={() => {
const mockWorkspaceId = mockServerAndRoutes.find(s => s._id === selectedMockServer)?.parentId;
navigate(`/organization/${organizationId}/project/${projectId}/workspace/${mockWorkspaceId}/mock-server/mock-route/${selectedMockRoute}`);
}}
className="mr-2 hover:no-underline bg-[--color-surprise] hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font-surprise] transition-colors rounded-sm"
>
Go to mock
</Button>
<Button
type="submit"
className="hover:no-underline bg-[--color-surprise] hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font-surprise] transition-colors rounded-sm"
>
Extract to mock route
Go to route
</Button>
</div>
</form>
Expand Down
15 changes: 15 additions & 0 deletions packages/insomnia/src/ui/components/mocks/mock-response-pane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ import { useRootLoaderData } from '../../routes/root';
import { Dropdown, DropdownButton, DropdownItem, DropdownSection, ItemContent } from '../base/dropdown';
import { TabItem, Tabs } from '../base/tabs';
import { CodeEditor } from '../codemirror/code-editor';
import { Pane, PaneHeader } from '../panes/pane';
import { PlaceholderResponsePane } from '../panes/placeholder-response-pane';
import { ResponseTimer } from '../response-timer';
import { SizeTag } from '../tags/size-tag';
import { StatusTag } from '../tags/status-tag';
import { TimeTag } from '../tags/time-tag';
import { getTimeFromNow } from '../time-from-now';
import { ResponseHeadersViewer } from '../viewers/response-headers-viewer';
import { ResponseTimelineViewer } from '../viewers/response-timeline-viewer';
Expand Down Expand Up @@ -69,6 +73,16 @@ export const MockResponsePane = () => {
);
}
return (
<Pane type="response">
{!activeResponse ? null : (
<PaneHeader className="row-spaced">
<div aria-atomic="true" aria-live="polite" className="no-wrap scrollable scrollable--no-bars pad-left">
<StatusTag statusCode={activeResponse.statusCode} statusMessage={activeResponse.statusMessage} />
<TimeTag milliseconds={activeResponse.elapsedTime} />
<SizeTag bytesRead={activeResponse.bytesRead} bytesContent={activeResponse.bytesContent} />
</div>
</PaneHeader>
)}
<Tabs aria-label="Mock response">
<TabItem
key="preview"
Expand Down Expand Up @@ -112,6 +126,7 @@ export const MockResponsePane = () => {
<HistoryViewWrapperComponentFactory mockServer={mockServer} mockRoute={mockRoute} />
</TabItem>
</Tabs>
</Pane>
);
};

Expand Down
5 changes: 4 additions & 1 deletion packages/insomnia/src/ui/components/mocks/mock-url-bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,11 @@ export const MockUrlBar = ({ onPathUpdate, onSend }: { onPathUpdate: (path: stri
onPress={() => {
const compoundId = mockRoute.parentId + pathInput;
showModal(AlertModal, {
title: 'Full URL',
title: 'Full URL',
message: mockbinUrl + '/bin/' + compoundId,
onConfirm: () => window.clipboard.writeText(mockbinUrl + '/bin/' + compoundId),
addCancel: true,
okLabel: 'Copy',
});
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ export const MockServerSettingsModal = ({ onClose }: { onClose: () => void }) =>
return;
}

if (mockServerType === 'self-hosted' && !mockServerUrl) {
showModal(AlertModal, {
title: 'URL required',
message: 'Please enter a self-hosted mock server URL.',
});
return;
}
if (mockServerType === 'self-hosted') {
try {
new URL(mockServerUrl);
} catch (e) {
showModal(AlertModal, {
title: 'Invalid URL',
message: 'Please enter a valid URL.',
});
return;
}
}

fetcher.submit(
{
name,
Expand Down Expand Up @@ -99,7 +118,7 @@ export const MockServerSettingsModal = ({ onClose }: { onClose: () => void }) =>
<Heading className="text-lg font-bold">Cloud Mock</Heading>
</div>
<p className='pt-2'>
The mock server runs on Insomnia cloud, ideal for creating API mocks collaboratively.
Runs on Insomnia cloud, ideal for collaboration.
</p>
</Radio>
<Radio
Expand All @@ -111,32 +130,31 @@ export const MockServerSettingsModal = ({ onClose }: { onClose: () => void }) =>
<Heading className="text-lg font-bold">Self-hosted Mock</Heading>
</div>
<p className="pt-2">
The mock servers are self hosted, ideal for private usage and lower latency.
Runs locally or on your infrastructure, ideal for private usage and lower latency.
</p>
</Radio>
</div>
</RadioGroup>
<div className="flex items-center gap-2 text-sm">
<Icon icon="info-circle" />
<span>
To learn more about self hosting. <Link href="https://github.com/Kong/insomnia-mockbin" className='underline'>Click here</Link>
</span>
</div>
<TextField
name="mockServerUrl"
defaultValue={serverType === 'cloud' ? 'https://mocks.insomnia.rest' : 'http://localhost:8080'}
className={`group relative flex-1 flex flex-col gap-2 ${serverType === 'cloud' ? 'disabled' : ''}`}
>
<Label className='text-sm text-[--hl]'>
Mock server URL
Self-hosted mock server URL
</Label>
<Input
disabled={serverType === 'cloud'}
placeholder="https://mocks.insomnia.rest"
placeholder={serverType === 'cloud' ? '' : 'https://example.com'}
className="py-1 placeholder:italic w-full pl-2 pr-7 rounded-sm border border-solid border-[--hl-sm] bg-[--color-bg] text-[--color-font] focus:outline-none focus:ring-1 focus:ring-[--hl-md] transition-colors"
/>
</TextField>
<div className="flex justify-between gap-2 items-center">
<div className="flex items-center gap-2 text-sm">
<Icon icon="info-circle" />
<span>
You can self-host a mock server. Click here to <Link href="https://github.com/Kong/insomnia-mockbin" className='underline'>learn more</Link>
</span>
</div>
<div className="flex justify-end gap-2 items-center">
<div className='flex items-center gap-2'>
<Button
onPress={close}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ export const WorkspaceSettingsModal = ({ workspace, mockServer, onClose }: Props
<Heading className="text-lg font-bold">Cloud Mock</Heading>
</div>
<p className='pt-2'>
The mock server runs on Insomnia cloud, ideal for creating API mocks collaboratively.
Runs on Insomnia cloud, ideal for collaboration.
</p>
</Radio>
<Radio
Expand All @@ -162,29 +162,32 @@ export const WorkspaceSettingsModal = ({ workspace, mockServer, onClose }: Props
<Heading className="text-lg font-bold">Self-hosted Mock</Heading>
</div>
<p className="pt-2">
The mock servers are self hosted, ideal for private usage and lower latency.
Runs locally or on your infrastructure, ideal for private usage and lower latency.
</p>
</Radio>
</div>
</RadioGroup>
<div className="flex items-center gap-2 text-sm">
<Icon icon="info-circle" />
<span>
To learn more about self hosting. <Link href="https://github.com/Kong/insomnia-mockbin" className='underline'>Click here</Link>
</span>
</div>
<TextField
autoFocus
name="name"
defaultValue={mockServer?.url || ''}
className={`group relative flex-1 flex flex-col gap-2 ${mockServer?.useInsomniaCloud ? 'disabled' : ''}`}
>
<Label className='text-sm text-[--hl]'>
Mock server URL
Self-hosted mock server URL
</Label>
<Input
disabled={mockServer?.useInsomniaCloud}
placeholder="https://mock.insomnia.rest"
placeholder={mockServer?.useInsomniaCloud ? '' : 'https://example.com'}
onChange={e => mockServer && mockServerPatcher(mockServer._id, { url: e.target.value })}
className="py-1 placeholder:italic w-full pl-2 pr-7 rounded-sm border border-solid border-[--hl-sm] bg-[--color-bg] text-[--color-font] focus:outline-none focus:ring-1 focus:ring-[--hl-md] transition-colors"
/>
<Label className='text-sm text-[--hl]'>
You can self-host a mock server. Click here to <Link href="https://github.com/Kong/insomnia-mockbin" className='underline'>learn more</Link>
</Label>
</TextField>
</>
)}
Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/ui/routes/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1227,7 +1227,7 @@ export const createMockRouteAction: ActionFunction = async ({ request, params })

const patch = await request.json();
invariant(typeof patch.name === 'string', 'Name is required');
// TODO: remove this hack
// TODO: remove this hack which enables a mock server to be created alongside a route
if (patch.mockServerName) {
const activeWorkspace = await models.workspace.getById(workspaceId);
invariant(activeWorkspace, 'Active workspace not found');
Expand Down
Loading

0 comments on commit 883f907

Please sign in to comment.