Skip to content

Commit a24e12d

Browse files
LemickMickaël Beguin
andauthored
[FEATURE] Add minify response (#51)
Co-authored-by: Mickaël Beguin <mickael.b@bridgeapi.io>
1 parent a7a254a commit a24e12d

File tree

11 files changed

+85
-24
lines changed

11 files changed

+85
-24
lines changed

intellij-plugin/gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
pluginGroup=com.lemick
22
pluginName=hoverfly-ui-intellij-plugin
33
pluginRepositoryUrl=https://github.com/Lemick/hoverfly-ui
4-
pluginVersion=1.1.2
4+
pluginVersion=1.2.0
55
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
66
pluginSinceBuild=222
77
pluginUntilBuild=241.*
88
# IntelliJ Platform Properties -> https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html#configuration-intellij-extension
99
platformType=IC
10-
platformVersion=2023.3.4
10+
platformVersion=2023.3.6
1111
# Plugin Dependencies -> https://plugins.jetbrains.com/docs/intellij/plugin-dependencies.html
1212
# Example: platformPlugins = com.intellij.java, com.jetbrains.php:203.4449.22
1313
platformPlugins=
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const simulationWithPrettyResponse = {
2+
meta: {
3+
schemaVersion: 'v5.2',
4+
hoverflyVersion: 'v1.6.0',
5+
timeExported: '2024-03-25T18:14:10.185Z'
6+
},
7+
data: {
8+
pairs: [
9+
{
10+
request: {},
11+
response: {
12+
status: 200,
13+
body: '{\n "is_prettified": true,\n "will_be_minified": true\n}'
14+
}
15+
}
16+
]
17+
}
18+
};
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
export const expectedMinifiedResponse = {
2+
meta: {
3+
schemaVersion: 'v5.2',
4+
hoverflyVersion: 'v1.6.0',
5+
timeExported: '2024-03-25T18:14:10.185Z'
6+
},
7+
data: {
8+
pairs: [
9+
{
10+
request: {},
11+
response: {
12+
status: 200,
13+
body: '{"is_prettified":true,"will_be_minified":true}'
14+
}
15+
}
16+
]
17+
}
18+
};

ui/e2e/utils/WebUiSimulationPage.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export class WebUiSimulationPage {
4545
/**
4646
* Use clipboard to fill monaco editor as it is not an HTML <input>
4747
*/
48-
async setTextEditorContent(editor: Locator, json: string) {
48+
async appendTextToEditor(editor: Locator, json: string) {
4949
await editor.click();
5050
await editor.pressSequentially(json);
5151
}

ui/e2e/web-ui.spec.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import expectedComplete from './test-data/output/expected-complete';
55
import expectedStartFromScratch from './test-data/output/expected-start-from-scratch';
66
import { simulationWithContentType } from './test-data/input/simulation-with-content-type';
77
import { expectContentTypeUpdated } from './test-data/output/expected-content-type-updated';
8+
import { expectedMinifiedResponse } from './test-data/output/expected-minified-response';
9+
import { simulationWithPrettyResponse } from './test-data/input/simulation-with-pretty-response';
810

911
test('should display a simulation example on first app launch', async ({ page }) => {
1012
const simulationPage = new WebUiSimulationPage(page);
@@ -120,14 +122,14 @@ test('should create a full simulation', async ({ page }) => {
120122
const currentTab = simulationPage.requestTabContentBody;
121123
await currentTab.getByRole('button', { name: 'Add first field matcher for body' }).click();
122124
await simulationPage.selectMatcherOption(currentTab, 'JSON Partial');
123-
await simulationPage.setTextEditorContent(
125+
await simulationPage.appendTextToEditor(
124126
currentTab.locator(simulationPage.requestEditor),
125127
'{ "field1": "value1" }'
126128
);
127129
});
128130

129131
await test.step('Edit response HTTP Body', async () => {
130-
await simulationPage.setTextEditorContent(
132+
await simulationPage.appendTextToEditor(
131133
simulationPage.responseBodyEditor,
132134
'{ "response": "body" }'
133135
);
@@ -182,19 +184,29 @@ test('should create a full simulation', async ({ page }) => {
182184
});
183185
});
184186

185-
test('content-type headers should update when body change', async ({ page }) => {
187+
test('Content-type headers should update when body change', async ({ page }) => {
186188
const simulationPage = new WebUiSimulationPage(page);
187189
await simulationPage.goto(JSON.stringify(simulationWithContentType));
188190

189191
await page.getByRole('button', { name: '- →️ 200' }).click();
190192

191-
await simulationPage.setTextEditorContent(
192-
simulationPage.responseBodyEditor,
193-
', how is it going ?'
194-
);
193+
await simulationPage.appendTextToEditor(simulationPage.responseBodyEditor, ', how is it going ?');
195194

196195
const textEditorContent = await simulationPage.getTextEditorContent(
197196
simulationPage.simulationTextEditor
198197
);
199198
expect(JSON.parse(textEditorContent)).toMatchObject(expectContentTypeUpdated);
200199
});
200+
201+
test('Should be able to minify JSON', async ({ page }) => {
202+
const simulationPage = new WebUiSimulationPage(page);
203+
await simulationPage.goto(JSON.stringify(simulationWithPrettyResponse));
204+
205+
await page.getByRole('button', { name: '- →️ 200' }).click();
206+
await page.getByRole('button', { name: 'Minify' }).click();
207+
208+
const textEditorContent = await simulationPage.getTextEditorContent(
209+
simulationPage.simulationTextEditor
210+
);
211+
expect(JSON.parse(textEditorContent)).toMatchObject(expectedMinifiedResponse);
212+
});

ui/src/components/forms/ResponseHeaderForm.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export default function ResponseHeaderForm({
3131

3232
return (
3333
<div className="flex flex-col items-start gap-6">
34-
<FormControl direction="row" className="items-center">
34+
<FormControl direction="row" className="items-center gap-4">
3535
<Label htmlFor="headerName">Name</Label>
3636
<Input
3737
id="headerName"
@@ -43,7 +43,7 @@ export default function ResponseHeaderForm({
4343
onChange={(event) => setHeaderName(event.target.value)}
4444
/>
4545
</FormControl>
46-
<FormControl direction="row" className="items-center">
46+
<FormControl direction="row" className="items-center gap-4">
4747
<Label htmlFor="headerValue">Value</Label>
4848
<Input
4949
id="headerValue"

ui/src/components/forms/ResponseMatcherForm.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { Checkbox } from '@/components/ui/Checkbox';
88
import { TypographyH2 } from '@/components/ui/Typography';
99
import { FormControl } from '@/components/utilities/FormControl';
1010
import { Button } from '@/components/ui/Button';
11-
import { parseIntOrDefault, prettify } from '@/services/json-service';
11+
import { minify, parseIntOrDefault, prettify } from '@/services/json-service';
1212
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/Popover';
13-
import { ClockIcon, Cross1Icon, MagicWandIcon } from '@radix-ui/react-icons';
13+
import { ClockIcon, Cross1Icon, MagicWandIcon, ZoomOutIcon } from '@radix-ui/react-icons';
1414
import ResponseHeaderFormPopover from '@/components/forms/ResponseHeaderFormPopover';
1515
import { updateContentLengthAccordingToBody } from '@/services/headers-service';
1616

@@ -62,6 +62,12 @@ const ResponseMatcherForm = ({ response = {}, onChange }: Props) => {
6262
<MagicWandIcon />
6363
Prettify
6464
</Button>
65+
<Button
66+
variant="outline"
67+
onClick={() => response.body && onResponseBodyChange(minify(response.body))}>
68+
<ZoomOutIcon className="size-5" />
69+
Minify
70+
</Button>
6571
<Popover>
6672
<PopoverTrigger asChild>
6773
<Button variant="outline">
@@ -75,9 +81,7 @@ const ResponseMatcherForm = ({ response = {}, onChange }: Props) => {
7581

7682
<div className="flex flex-col items-start gap-6">
7783
<FormControl direction="column">
78-
<Label htmlFor="fixedDelay">
79-
Fixed Delay <i>(ms)</i>
80-
</Label>
84+
<Label htmlFor="fixedDelay">Fixed Delay (ms)</Label>
8185
<Input
8286
id="fixedDelay"
8387
type="number"
@@ -118,7 +122,7 @@ const ResponseMatcherForm = ({ response = {}, onChange }: Props) => {
118122
dataTestId="response-body-editor"
119123
/>
120124

121-
<FormControl direction="row">
125+
<FormControl direction="row" className="items-center">
122126
<Checkbox
123127
id="encodedBody"
124128
className="form-check-input"
@@ -171,7 +175,7 @@ const ResponseMatcherForm = ({ response = {}, onChange }: Props) => {
171175
</FormControl>
172176
</div>
173177

174-
<div className="w-full flex flex-row ">
178+
<div className="w-full flex flex-row">
175179
<ResponseHeaderFormPopover onChange={onHeaderChangeRequest}>
176180
<Button variant="outline">Add header</Button>
177181
</ResponseHeaderFormPopover>

ui/src/components/forms/matchers/FieldMatchersForm.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ const FieldMatcherForm = ({
114114
<p className="font-medium leading-none">Advanced options</p>
115115

116116
<div className="flex flex-col items-start gap-3">
117-
<FormControl direction="row">
117+
<FormControl direction="row" className="items-center">
118118
<Checkbox
119119
id={generateDomId('ignoreUnknown')}
120120
name="ignoreUnknown"
@@ -123,7 +123,7 @@ const FieldMatcherForm = ({
123123
/>
124124
<Label htmlFor={generateDomId('ignoreUnknown')}>Ignore Unknown</Label>
125125
</FormControl>
126-
<FormControl direction="row">
126+
<FormControl direction="row" className="items-center">
127127
<Checkbox
128128
id={generateDomId('ignoreOrder')}
129129
name="ignoreOrder"
@@ -132,7 +132,7 @@ const FieldMatcherForm = ({
132132
/>
133133
<Label htmlFor={generateDomId('ignoreOrder')}>Ignore Order</Label>
134134
</FormControl>
135-
<FormControl direction="row">
135+
<FormControl direction="row" className="items-center">
136136
<Checkbox
137137
id={generateDomId('ignoreOccurrences')}
138138
name="ignoreOccurrences"

ui/src/components/utilities/FormControl.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ export function FormControl({
1313
return (
1414
<div
1515
className={cn(
16-
'flex items-start [&>*]:cursor-pointer',
17-
direction === 'column' ? 'gap-2' : 'gap-4',
16+
'flex gap-2 items-start [&>*]:cursor-pointer',
1817
direction === 'column' ? 'flex-col' : 'flex-row',
1918
className
2019
)}>

ui/src/components/utilities/InvalidSimulation.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React from 'react';
22
import { Button } from '@/components/ui/Button';
3+
import { ResetIcon } from '@radix-ui/react-icons';
34

45
type Props = {
56
onClick: () => void;
@@ -10,6 +11,7 @@ export default function InvalidSimulation({ onClick = () => {} }: Props) {
1011
<div className="text-center mt-5">
1112
<h5>No valid data pairs</h5>
1213
<Button className="mt-2" variant="secondary" onClick={onClick}>
14+
<ResetIcon />
1315
Reset simulation
1416
</Button>
1517
</div>

ui/src/services/json-service.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ export function prettify(json: string) {
1919
}
2020
}
2121

22+
export function minify(json: string) {
23+
try {
24+
return JSON.stringify(JSON.parse(json));
25+
} catch (error) {
26+
return json;
27+
}
28+
}
29+
2230
export function isJSON(str: string): boolean {
2331
try {
2432
JSON.parse(str);

0 commit comments

Comments
 (0)