Skip to content

Commit

Permalink
Merge pull request #209 from Thiht/issue-183-mock-editor
Browse files Browse the repository at this point in the history
Issue #183 mock editor
  • Loading branch information
Thiht authored Nov 3, 2021
2 parents ea1a83e + bba9bb4 commit 94a4342
Show file tree
Hide file tree
Showing 21 changed files with 1,219 additions and 312 deletions.
34 changes: 21 additions & 13 deletions client/components/Code.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import "codemirror/addon/lint/yaml-lint";
import "codemirror/lib/codemirror.css";
import "codemirror/mode/javascript/javascript";
import "codemirror/mode/ruby/ruby";
import "codemirror/mode/xml/xml";
import "codemirror/mode/yaml/yaml";
import "codemirror/theme/material.css";
import jsyaml from "js-yaml";
Expand All @@ -23,18 +24,21 @@ window.jsyaml = jsyaml;

const largeBodyLength = 5000;

export type Language =
| "go_template"
| "go_template_yaml"
| "yaml"
| "go_template_json"
| "json"
| "lua"
| "xml"
| "txt";

interface Props {
value: string;
language:
| "go_template"
| "go_template_yaml"
| "yaml"
| "go_template_json"
| "json"
| "lua"
| "txt";
value?: string;
language: Language;
collapsible?: boolean;
onBeforeChange?: (value: string) => unknown;
onChange?: (value: string) => unknown;
}

const codeMirrorOptions: EditorConfiguration = {
Expand All @@ -56,7 +60,7 @@ const codeMirrorOptions: EditorConfiguration = {
const Code = ({
value,
language,
onBeforeChange,
onChange: onBeforeChange,
collapsible = true,
}: Props): JSX.Element => {
let mode: string = language;
Expand All @@ -74,6 +78,10 @@ const Code = ({
case "go_template_yaml":
mode = "yaml";
break;

case "xml":
mode = "application/xml";
break;
}

let body = null;
Expand All @@ -98,7 +106,7 @@ const Code = ({
body = (
<Controlled
className="code-editor"
value={value}
value={value || ""}
options={{
...codeMirrorOptions,
mode,
Expand All @@ -115,7 +123,7 @@ const Code = ({
/>
);

if (collapsible && value.length > largeBodyLength) {
if (collapsible && value && value.length > largeBodyLength) {
return (
<Collapse>
<Collapse.Panel
Expand Down
6 changes: 3 additions & 3 deletions client/components/History.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ const HistoryComponent = ({

let body = null;
if (error) {
body = <Alert message={error.message} type="error" />;
body = <Alert message={error.message} type="error" showIcon />;
} else {
const filteredEntries = orderBy(
historyEntries,
Expand Down Expand Up @@ -340,7 +340,7 @@ const HistoryComponent = ({
<p>
This is the history of the requests made during the selected session.
</p>
<p>
<div>
Entries are sorted by
<Button onClick={onSort} type="link">
{entryField}
Expand All @@ -367,7 +367,7 @@ const HistoryComponent = ({
</Select.Option>
</Select>
.
</p>
</div>
<Spin delay={300} spinning={loading && historyEntries.length === 0}>
{body}
</Spin>
Expand Down
66 changes: 66 additions & 0 deletions client/components/MockEditor/BodyMatcherEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import * as React from "react";
import { Button, Col, Form, Row } from "antd";
import Code from "../Code";
import { bodyMatcherToPaths } from "../../modules/utils";
import { KeyValueEditorEngine } from "./KeyValueEditor";

interface BodyMatcherEditorProps {
name: string[];
}

export const BodyMatcherEditor = ({
name,
}: BodyMatcherEditorProps): JSX.Element => {
const [initialized, setInitialized] = React.useState(false);
const [rawJSON, setRawJSON] = React.useState("");

return (
<Form.List name={name}>
{(fields, actions) =>
!initialized ? (
<>
<p>
Please paste a JSON payload below in order to generate the
corresponding body matcher. For better results, only keep the JSON
fields you want to match upon.
</p>
<Code
language="json"
value={rawJSON}
onChange={(value) => setRawJSON(value)}
/>
<Row>
<Col span={24} style={{ textAlign: "right", marginTop: "0.5em" }}>
<Button
onClick={() => {
try {
const json = JSON.parse(rawJSON);
Object.entries(bodyMatcherToPaths(json)).forEach(
([key, value]) => {
// TODO: handle more matchers
actions.add({ key, matcher: "ShouldEqual", value });
}
);
setInitialized(true);
} catch (e) {
console.error("Invalid JSON body", e);
}
}}
>
Generate Body Matcher
</Button>
</Col>
</Row>
</>
) : (
<KeyValueEditorEngine
name={name}
withMatchers={true}
fields={fields}
actions={actions}
/>
)
}
</Form.List>
);
};
110 changes: 110 additions & 0 deletions client/components/MockEditor/KeyValueEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import * as React from "react";
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
import { Button, Form, Input, Select, Space } from "antd";
import { defaultMatcher } from "../../modules/types";
import { positiveMatchers, negativeMatchers, unaryMatchers } from "./utils";
import { FormListFieldData, FormListOperation } from "antd/lib/form/FormList";

export interface KeyValueEditorProps {
name: string[];
withMatchers?: boolean;
}

export const KeyValueEditor = ({
name,
withMatchers,
}: KeyValueEditorProps): JSX.Element => (
<Form.List name={name}>
{(fields, actions) => (
<KeyValueEditorEngine
name={name}
withMatchers={withMatchers}
fields={fields}
actions={actions}
/>
)}
</Form.List>
);

export interface KeyValueEditorEngineProps extends KeyValueEditorProps {
fields: FormListFieldData[];
actions: FormListOperation;
}

export const KeyValueEditorEngine = ({
name,
withMatchers,
fields,
actions,
}: KeyValueEditorEngineProps): JSX.Element => (
<div style={{ margin: "auto" }}>
{fields.map(({ key, name: fieldName, fieldKey, ...restField }) => (
<Space
key={key}
style={{ display: "flex", justifyContent: "center" }}
align="baseline"
>
<MinusCircleOutlined onClick={() => actions.remove(fieldName)} />

<Form.Item
{...restField}
name={[fieldName, "key"]}
fieldKey={[fieldKey, "key"]}
>
<Input placeholder="Key" />
</Form.Item>

{withMatchers && (
<Form.Item
{...restField}
name={[fieldName, "matcher"]}
fieldKey={[fieldKey, "matcher"]}
>
<Select>
<Select.OptGroup label="Positive">
{positiveMatchers.map((matcher) => (
<Select.Option key={matcher} value={matcher}>
{matcher}
</Select.Option>
))}
</Select.OptGroup>
<Select.OptGroup label="Negative">
{negativeMatchers.map((matcher) => (
<Select.Option key={matcher} value={matcher}>
{matcher}
</Select.Option>
))}
</Select.OptGroup>
</Select>
</Form.Item>
)}

<Form.Item noStyle shouldUpdate>
{({ getFieldValue }) => (
<Form.Item
{...restField}
name={[fieldName, "value"]}
fieldKey={[fieldKey, "value"]}
hidden={unaryMatchers.includes(
getFieldValue([...name, fieldKey, "matcher"])
)}
>
<Input placeholder="Value" />
</Form.Item>
)}
</Form.Item>
</Space>
))}

<Form.Item>
<Button
type="dashed"
onClick={() => actions.add({ matcher: defaultMatcher })}
style={{ width: "100%" }}
icon={<PlusOutlined />}
>
Add field
</Button>
</Form.Item>
</div>
);
33 changes: 33 additions & 0 deletions client/components/MockEditor/MockContextEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as React from "react";
import { Form, InputNumber, Switch } from "antd";

export const MockContextEditor = (): JSX.Element => (
<div className="inline-form-items">
<Form.Item name={["context", "times_enabled"]} noStyle>
<Switch size="small" />
</Form.Item>

<Form.Item
label="Limit this mock to be called"
shouldUpdate={(prevValues, currentValues) => prevValues?.context?.times_enabled !==
currentValues?.context?.times_enabled ||
prevValues?.context?.times !== currentValues?.context?.times}
style={{ marginBottom: 0, paddingLeft: "5px" }}
>
{({ getFieldValue }) => (
<>
<Form.Item name={["context", "times"]} noStyle>
<InputNumber
min={1}
disabled={!getFieldValue(["context", "times_enabled"])} />
</Form.Item>
{getFieldValue(["context", "times"]) <= 1 ? (
<span> time</span>
) : (
<span> times</span>
)}
</>
)}
</Form.Item>
</div>
);
31 changes: 31 additions & 0 deletions client/components/MockEditor/MockDynamicResponseEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as React from "react";
import { Form, Select } from "antd";
import Code from "../Code";

export const MockDynamicResponseEditor = (): JSX.Element => (
<>
<Form.Item label="Engine" name={["dynamic_response", "engine"]}>
<Select>
<Select.Option value="go_template_yaml">
Go Template (YAML)
</Select.Option>
<Select.Option value="go_template_json">
Go Template (JSON)
</Select.Option>
<Select.Option value="lua">Lua</Select.Option>
</Select>
</Form.Item>

<Form.Item
noStyle
shouldUpdate={(prevValues, currentValues) => prevValues?.dynamic_response?.engine !==
currentValues?.dynamic_response?.engine}
>
{({ getFieldValue }) => (
<Form.Item label="Script" name={["dynamic_response", "script"]}>
<Code language={getFieldValue(["dynamic_response", "engine"])} />
</Form.Item>
)}
</Form.Item>
</>
);
16 changes: 16 additions & 0 deletions client/components/MockEditor/MockEditor.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.inline-form-items {
display: flex;
align-items: center;
}

.hidden {
visibility: hidden;
}

.display-none {
display: none;
}

.ant-form-item {
margin-bottom: 12px;
}
Loading

0 comments on commit 94a4342

Please sign in to comment.