Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update mockup diff visualization #90

Merged
merged 20 commits into from
Jun 27, 2019
Merged
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
"express": "^4.16.4",
"react": "^16.8.6",
"react-chartjs": "^1.2.0",
"react-diff-viewer": "^1.1.0",
"react-dom": "^16.8.6",
"react-json-view": "^1.19.1",
"socket.io": "^2.2.0",
Expand Down
10 changes: 5 additions & 5 deletions src/assets/no-data.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/main/components/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ const Button = styled.button`
if (props.variation === 'positive') return '0 0 1px 2px #DEF0DB';
return '0 0 1px 2px #96C1E9';
}}
}
`;

export default Button;
2 changes: 1 addition & 1 deletion src/main/components/DataTree.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const DataTree = (props) => {
theme='chalk'
iconStyle='circle'
style={styles}
collapsed={0}
collapsed={2}
onAdd={(onAdd) ? changeObject : false}
onEdit={(onEdit) ? changeObject : false}
onDelete={(onDelete) ? changeObject : false}
Expand Down
44 changes: 31 additions & 13 deletions src/main/components/Mockup.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import React, { useState } from 'react';
import React, { useState, useContext } from 'react';
import NameForm from './NameForm.jsx';
import MockupName from './MockupName.jsx';
import DataTree from './DataTree.jsx';
import Button from './Button.jsx';
import AssertionsForm from './AssertionsForm.jsx'
import AssertionsForm from './AssertionsForm.jsx';
import { TestsContext } from '../testsContext';


const dataTreeOptions = {
onAdd: true,
Expand All @@ -15,29 +17,45 @@ const dataTreeOptions = {
const Mockup = (props) => {
const { test, index, saveUpdatedTree } = props;

// State
const [name, setName] = useState(test.name);
const [tests, setTests] = useContext(TestsContext);

// Mode controls test assertions and name edit form—it’s set by child components
const [mode, setMode] = useState('view');

function setName(name, testIndex) {
const testsClone = [...tests];
// This if-statement just makes sure the test name wasn't just whitespace
if (name.replace(/\s/g, '') !== '') testsClone[testIndex].name = name;
else testsClone[testIndex].name = `Test #${testIndex + 1}`;
setTests(testsClone);
}

function deleteTest(testIndex) {
const testsClone = [];
for (let i = 0; i < tests.length; i += 1) {
if (i !== testIndex) testsClone.push(tests[i]);
}
setTests(testsClone);
}

return (
<article className="mockup" key={`mockup-${index}`}>
{mode === 'editName'
? <NameForm name={name} index={index} setMode={setMode} setName={setName} />
: <MockupName name={name || `Test #${index + 1}`} setMode={setMode} />
? <NameForm name={test.name} index={index} setMode={setMode} setName={setName} />
: <MockupName name={test.name} setMode={setMode} />
}
<DataTree
treeId={index}
data={test.payload}
name={name}
options={dataTreeOptions}
saveUpdatedTree={saveUpdatedTree}
/>
treeId={index}
data={test.payload}
name={test.name}
options={dataTreeOptions}
saveUpdatedTree={saveUpdatedTree}
/>
{mode === 'editAssertions'
? <AssertionsForm setMode={setMode} index={index}/>
? <AssertionsForm setMode={setMode} index={index} />
: <Button enabled={true} onClick={() => setMode('editAssertions')}>Edit Test Assertions</Button>
}
<Button enabled={true} onClick={() => deleteTest(index)}>Delete Test</Button>
</article>
);
};
Expand Down
6 changes: 4 additions & 2 deletions src/main/components/NameForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import styled from 'styled-components';
import { TestsContext } from '../testsContext';
import InlineForm from './InlineForm.jsx';
import InlineInput from './InlineInput.jsx';
import Button from './Button.jsx';

// Form styles
const Form = styled(InlineForm)`
Expand Down Expand Up @@ -38,7 +39,7 @@ const NameForm = (props) => {
testsCopy[index].name = newName;

// Update name in test array, then set mockup “mode” to trigger re-render
setName(newName);
setName(newName, index);
setTests(testsCopy);
setMode('view');
};
Expand All @@ -53,10 +54,11 @@ const NameForm = (props) => {
<Input
name="name"
type="text"
placeholder={name || `Test #${index + 1}`}
placeholder={name}
onChange={updateName}
/>
<Label htmlFor="name">Name</Label>
<Button style={{ 'margin-left': '100px' }} enabled={true} type="submit">Save</Button>
</Form>
);
};
Expand Down
14 changes: 7 additions & 7 deletions src/main/components/RequestBar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const RequestBar = (props) => {
})
.then((res) => {
setTests([{
payload: res, status: '', name: '', diff: {},
payload: res, status: '', name: 'Test #1', diff: {},
}]);
setData(res);
});
Expand Down Expand Up @@ -152,12 +152,12 @@ const RequestBar = (props) => {
{
(SourceOrDest === 'dest')
&& <Select
name='method'
id='fetchTypeInput'
multiple={false}
value={selected}
onChange={handleChange}
>
name='method'
id='fetchTypeInput'
multiple={false}
value={selected}
onChange={handleChange}
>
<option value='POST'>POST</option>
<option value='PATCH'>PATCH</option>
<option value='PUT'>PUT</option>
Expand Down
103 changes: 71 additions & 32 deletions src/main/components/ResponseComponent.jsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,37 @@
import React from 'react';
import React, { useState } from 'react';
import ReactDiffViewer from 'react-diff-viewer';
import styled from 'styled-components';
import ResponseWrapper from './ResponseWrapper.jsx';

const ResponseWrapper = styled.article`
background-color: #F0F3F4;
border-radius: 3px;
box-shadow: inset;
box-shadow: inset 0 0 1px 0 rgba(41, 47, 50, 0.25);
display: flex;
justify-content: space-between;
// Styles
const CardButton = styled.button`
background: #F0F3F4;
border: none;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
border-top: 1px solid #BCC1C2;
box-shadow: inset 0 0 1px 0 rgba(41, 47, 50, 0.35);
color: #555B5E;
font-family: 'Halyard Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 0.825em;
padding: 1em;
margin-bottom: 1em;

:hover {
cursor: pointer;
background: #555B5E;
color: #F0F3F4;
}

:active {
background: #555B5E;
color: #F0F3F4;
outline: 0;
}

:focus {
outline: 0;
box-shadow: inset 0 0 3px 0 rgba(41, 47, 50, 0.5);
}
`;

const Icon = styled.span`
Expand All @@ -22,20 +44,26 @@ const Icon = styled.span`
}};
`;

const Code = styled.textarea`
background-color: #292F32;
border: none;
border-radius: 3px;
color: #D4E6F7;
display: flex;
padding: 1em;
font-family: 'IBM Plex Mono', monospace;
font-size: 0.725em;
resize: none;
`;
const diffColors = {
variables: {
addedBackground: '#DEF0DB',
addedColor: '#1A3715',
removedBackground: '#FFE5E5',
removedColor: '#5C0505',
wordAddedBackground: '#B2D2A2',
wordRemovedBackground: '#FBB6B6',
addedGutterBackground: '#B2D2A2',
removedGutterBackground: '#FBB6B6',
gutterBackground: '#F0F3F4',
gutterBackgroundDark: '#F0F3F4',
highlightBackground: '#fffbdd',
highlightGutterBackground: '#fff5b1',
},
};

const Header = styled.header`
font-family: 'Halyard Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
padding: 1em;
h3 {
color: #292F32;
margin: 0;
Expand All @@ -45,9 +73,12 @@ const Header = styled.header`

const ResponseComponent = (props) => {
const {
status, expectedStatus, payload, name, index,
status, expectedStatus, payload, data, name, index,
} = props;

const [showDiff, setShowDiff] = useState(false);

// Event handlers
const didPass = () => {
if (!status) return 'pending';
if (!expectedStatus) {
Expand All @@ -70,7 +101,7 @@ const ResponseComponent = (props) => {

const renderExpectedStatus = () => {
if (!expectedStatus) {
return 'No assertion given';
return 'Expecting status code in the 200–299 range';
}
if (expectedStatus.length === 2) {
const [lower, upper] = expectedStatus;
Expand All @@ -83,7 +114,7 @@ const ResponseComponent = (props) => {

const renderTestResult = () => {
if (!expectedStatus) {
return 'No assertion given';
return 'Expecting status code in the 200–299 range';
}
if (expectedStatus[1]) {
const [lower, upper] = expectedStatus;
Expand All @@ -99,17 +130,25 @@ const ResponseComponent = (props) => {
return (
<ResponseWrapper>
<Header>
<h3>{ name || `Test #${index + 1}` }</h3>
<h3>{name}</h3>
<Icon didPass={didPass()}>{renderCheckmark()}</Icon>
{ status || 'Ready to send' }
<p>{ status ? renderTestResult() : renderExpectedStatus() }</p>
{status || 'Ready to send'}
<p>{status ? renderTestResult() : renderExpectedStatus()}</p>
</Header>
<Code
cols='50'
rows='5'
value={JSON.stringify(payload)}
readOnly
/>
{showDiff ? <a onClick={() => setShowDiff(false)}>Collapse Changes</a> : null}
{showDiff
? <div className="diff">
<ReactDiffViewer
oldValue={JSON.stringify(data, null, 2)}
newValue={JSON.stringify(payload, null, 2)}
splitView={false}
styles={diffColors}
hideLineNumbers={true}
/>
</div>
: <CardButton onClick={() => setShowDiff(true)}>Show Changes</CardButton>
}

</ResponseWrapper>
);
};
Expand Down
36 changes: 36 additions & 0 deletions src/main/components/ResponseWrapper.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import styled from 'styled-components';

const ResponseWrapper = styled.article`
background-color: #F0F3F4;
border-radius: 3px;
box-shadow: inset;
box-shadow: inset 0 0 2px 0 rgba(41, 47, 50, 0.25);
display: flex;
flex-direction: column;
justify-content: space-between;
margin-bottom: 1em;
a {
color: #555B5E;
font-family: 'Halyard Text', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
font-size: 0.825em;
text-align: center;
:hover {
cursor: pointer;
}
}
div.diff {
border-top: 1px solid #E2E6E9;
margin: 0.25em;
max-height: 480px;
overflow-y: scroll;
}
table {
border-collapse: collapse;
}
pre {
font-family: 'IBM Plex Mono', monospace;
font-size: 90%;
}
`;

export default ResponseWrapper;
Loading