mirror of
https://github.com/Kong/insomnia.git
synced 2026-10-23 15:19:04 -04:00
Unit test improvements (#2337)
* Start working on insomnia-testing package to generate Mocha code * Moved some things around * Integration tests and ability to run multiple files * Fix integration tests * Rename runSuite to runMochaTests * Add types for test results * Fix lint errors * Play with Chia assertions * insomnia and chai globals, plus a bunch of improvements * Stub chai and axios Flow types * Ability to reference requests by ID * Fix chai flow-typed location * Address PR comments (small tweaks) * Basic UI going * Lots and lots and lots * Pretty-much done with the unit test UI * Minor CSS tweak * Activity bar triple toggle and more * Minor tweak * Unit Test and Suite deletion * Bump Designer version * Fix eslint stuff * Fix insomnia-testing tests * Fix tests * lib@2.2.9 * Remove tests tab from response pane * Hook up Insomnia networking * Fix linting * Bump version for alpha * Remove extra ActivityToggleSwitch * Remove unused import * Add test:pre-release script that excludes CLI tests * Less repetition * Clean some things up * Tweaks after self-cr * Undo request ID tag * Swap out switch for new activity toggle component * Extra check * Remove dead code * Delete dead test * Oops, revert example code * PR feedback * Unit test UI tweaks * More PR comment addresses * Change test name
This commit is contained in:
1 parent
3883dc6feb
commit
eae0d8d433
7 files changed
+154
-89
No files matched your search
@@ -101,7 +101,6 @@ class Editable extends PureComponent {
|
||||
title: singleClick ? 'Click to edit' : 'Double click to edit',
|
||||
onClick: this._handleSingleClickEditStart,
|
||||
onDoubleClick: this._handleEditStart,
|
||||
blankValue,
|
||||
...extra,
|
||||
};
|
||||
|
||||
|
||||
@@ -30,12 +30,14 @@ type Props = {|
|
||||
|
||||
type State = {|
|
||||
testsRunning: Array<UnitTest> | null,
|
||||
resultsError: string | null,
|
||||
|};
|
||||
|
||||
@autobind
|
||||
class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
state = {
|
||||
testsRunning: null,
|
||||
resultsError: null,
|
||||
};
|
||||
|
||||
// Defining it here instead of in render() so it won't act as a changed prop
|
||||
@@ -130,7 +132,7 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
showPrompt({
|
||||
title: 'New Test',
|
||||
defaultValue: 'Returns 200',
|
||||
submitName: 'Create Test',
|
||||
submitName: 'New Test',
|
||||
label: 'Test Name',
|
||||
selectText: true,
|
||||
onComplete: async name => {
|
||||
@@ -165,7 +167,18 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
async _handleDeleteTest(unitTest: UnitTest): Promise<void> {
|
||||
await models.unitTest.remove(unitTest);
|
||||
showAlert({
|
||||
title: `Delete ${unitTest.name}`,
|
||||
message: (
|
||||
<span>
|
||||
Really delete <strong>{unitTest.name}</strong>?
|
||||
</span>
|
||||
),
|
||||
addCancel: true,
|
||||
onConfirm: async () => {
|
||||
await models.unitTest.remove(unitTest);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async _handleSetActiveRequest(
|
||||
@@ -202,10 +215,15 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
await models.unitTest.update(unitTest, { name });
|
||||
}
|
||||
|
||||
async _handleChangeActiveSuiteName(name: string): Promise<void> {
|
||||
const { activeUnitTestSuite } = this.props.wrapperProps;
|
||||
await models.unitTestSuite.update(activeUnitTestSuite, { name });
|
||||
}
|
||||
|
||||
async _runTests(unitTests: Array<UnitTest>): Promise<void> {
|
||||
const { requests, activeWorkspace, activeEnvironment } = this.props.wrapperProps;
|
||||
|
||||
this.setState({ testsRunning: unitTests });
|
||||
this.setState({ testsRunning: unitTests, resultsError: null });
|
||||
|
||||
const tests = [];
|
||||
for (const t of unitTests) {
|
||||
@@ -217,10 +235,18 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
const src = await generate([{ name: 'My Suite', suites: [], tests }]);
|
||||
const results = await runTests(src, {
|
||||
requests,
|
||||
sendRequest: getSendRequestCallback(activeEnvironment ? activeEnvironment._id : null),
|
||||
});
|
||||
const sendRequest = getSendRequestCallback(activeEnvironment ? activeEnvironment._id : null);
|
||||
|
||||
let results;
|
||||
try {
|
||||
results = await runTests(src, { requests, sendRequest });
|
||||
} catch (err) {
|
||||
// Set the state after a timeout so the user still sees the loading state
|
||||
setTimeout(() => {
|
||||
this.setState({ resultsError: err.message, testsRunning: null });
|
||||
}, 400);
|
||||
return;
|
||||
}
|
||||
|
||||
await models.unitTestResult.create({ results, parentId: activeWorkspace._id });
|
||||
|
||||
@@ -251,18 +277,37 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
|
||||
renderResults(): React.Node {
|
||||
const { activeUnitTestResult } = this.props.wrapperProps;
|
||||
const { testsRunning } = this.state;
|
||||
const { testsRunning, resultsError } = this.state;
|
||||
|
||||
if (resultsError) {
|
||||
return (
|
||||
<div className="unit-tests__results">
|
||||
<div className="unit-tests__top-header">
|
||||
<h2>Run Failed</h2>
|
||||
</div>
|
||||
<div className="danger pad">{resultsError}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (testsRunning) {
|
||||
return (
|
||||
<div className="unit-tests__results">
|
||||
<h2>Running {testsRunning.length} Tests...</h2>
|
||||
<div className="unit-tests__top-header">
|
||||
<h2>Running {testsRunning.length} Tests...</h2>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!activeUnitTestResult) {
|
||||
return null;
|
||||
return (
|
||||
<div className="unit-tests__results">
|
||||
<div className="unit-tests__top-header">
|
||||
<h2>No Results</h2>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const { stats, tests } = activeUnitTestResult.results;
|
||||
@@ -271,20 +316,21 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
<div className="unit-tests__results">
|
||||
{activeUnitTestResult && (
|
||||
<div key={activeUnitTestResult._id}>
|
||||
{stats.failures ? (
|
||||
<h2 className="warning">
|
||||
Tests Failed {stats.failures}/{stats.tests}
|
||||
</h2>
|
||||
) : (
|
||||
<h2 className="success">
|
||||
Tests Passed {stats.passes}/{stats.tests}
|
||||
</h2>
|
||||
)}
|
||||
<div className="unit-tests__top-header">
|
||||
{stats.failures ? (
|
||||
<h2 className="warning">
|
||||
Tests Failed {stats.failures}/{stats.tests}
|
||||
</h2>
|
||||
) : (
|
||||
<h2 className="success">
|
||||
Tests Passed {stats.passes}/{stats.tests}
|
||||
</h2>
|
||||
)}
|
||||
</div>
|
||||
<ul>
|
||||
{tests.map((t, i) => (
|
||||
<li key={i}>
|
||||
<SvgIcon icon={t.err.message ? 'error' : 'success'} /> {t.fullTitle} ({t.duration}{' '}
|
||||
ms)
|
||||
<SvgIcon icon={t.err.message ? 'error' : 'success'} /> {t.title} ({t.duration} ms)
|
||||
{t.err.message && (
|
||||
<>
|
||||
<br />
|
||||
@@ -369,11 +415,12 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
|
||||
renderPageBody(): React.Node {
|
||||
const { activeUnitTests, activeUnitTestSuite } = this.props.wrapperProps;
|
||||
const { testsRunning } = this.state;
|
||||
|
||||
if (!activeUnitTestSuite) {
|
||||
return (
|
||||
<div className="unit-tests layout-body--sidebar theme--pane">
|
||||
<div className="unit-tests__tests theme--pane__body">No test suite selected</div>
|
||||
<div className="unit-tests__tests theme--pane__body pad">No test suite selected</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -381,12 +428,27 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
return (
|
||||
<div className="unit-tests layout-body--sidebar theme--pane">
|
||||
<div className="unit-tests__tests theme--pane__body">
|
||||
{activeUnitTests.map(this.renderUnitTest)}
|
||||
<div className="pad">
|
||||
<Button variant="contained" bg="surprise" onClick={this._handleCreateTest}>
|
||||
Create Test
|
||||
<div className="unit-tests__top-header">
|
||||
<h2>
|
||||
<Editable
|
||||
singleClick
|
||||
onSubmit={this._handleChangeActiveSuiteName}
|
||||
value={activeUnitTestSuite.name}
|
||||
/>
|
||||
</h2>
|
||||
<Button variant="outlined" onClick={this._handleCreateTest}>
|
||||
New Test
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
bg="surprise"
|
||||
onClick={this._handleRunTests}
|
||||
size="default"
|
||||
disabled={testsRunning}>
|
||||
{testsRunning ? 'Running... ' : 'Run Tests'}
|
||||
</Button>
|
||||
</div>
|
||||
{activeUnitTests.map(this.renderUnitTest)}
|
||||
</div>
|
||||
{this.renderResults()}
|
||||
</div>
|
||||
@@ -395,6 +457,7 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
|
||||
renderPageSidebar(): React.Node {
|
||||
const { activeUnitTestSuites, activeUnitTestSuite } = this.props.wrapperProps;
|
||||
const { testsRunning } = this.state;
|
||||
const activeId = activeUnitTestSuite ? activeUnitTestSuite._id : 'n/a';
|
||||
|
||||
return (
|
||||
@@ -418,6 +481,12 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
<i className="fa fa-caret-down" />
|
||||
</button>
|
||||
)}>
|
||||
<DropdownItem
|
||||
stayOpenAfterClick
|
||||
onClick={this._handleRunTests}
|
||||
disabled={testsRunning}>
|
||||
{testsRunning ? 'Running... ' : 'Run Tests'}
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this._handleDeleteUnitTestSuite.bind(this, s)}>
|
||||
Delete Suite
|
||||
</DropdownItem>
|
||||
@@ -433,7 +502,6 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
render() {
|
||||
const { handleActivityChange, gitSyncDropdown } = this.props;
|
||||
const { activeWorkspace, activity, settings, activeApiSpec } = this.props.wrapperProps;
|
||||
const { testsRunning } = this.state;
|
||||
return (
|
||||
<PageLayout
|
||||
wrapperProps={this.props.wrapperProps}
|
||||
@@ -460,18 +528,7 @@ class WrapperUnitTest extends React.PureComponent<Props, State> {
|
||||
workspace={activeWorkspace}
|
||||
/>
|
||||
}
|
||||
gridRight={
|
||||
<>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={this._handleRunTests}
|
||||
size="default"
|
||||
disabled={testsRunning}>
|
||||
{testsRunning ? 'Running... ' : 'Run Tests'}
|
||||
</Button>
|
||||
{gitSyncDropdown}
|
||||
</>
|
||||
}
|
||||
gridRight={gitSyncDropdown}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -13,39 +13,12 @@
|
||||
grid-template-columns: 1fr 0;
|
||||
}
|
||||
|
||||
&__header {
|
||||
grid-area: header;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-font);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--hl-md);
|
||||
|
||||
&__buttons {
|
||||
margin-left: auto;
|
||||
|
||||
& > button {
|
||||
align-self: stretch;
|
||||
height: 100%;
|
||||
background: none;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: 0 var(--padding-md);
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__tests {
|
||||
grid-area: body-left;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-font);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
|
||||
&__block {
|
||||
height: auto;
|
||||
@@ -96,13 +69,14 @@
|
||||
grid-area: body-right;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-font);
|
||||
display: grid;
|
||||
grid-template-columns: 100%;
|
||||
grid-template-rows: 1fr auto;
|
||||
overflow-y: auto;
|
||||
|
||||
li {
|
||||
display: block;
|
||||
padding: var(--padding-xs) 0;
|
||||
ul {
|
||||
padding-top: var(--padding-md);
|
||||
li {
|
||||
display: block;
|
||||
padding: var(--padding-xs) var(--padding-md);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,4 +124,22 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__top-header {
|
||||
padding: 0 var(--padding-md);
|
||||
height: var(--line-height-lg);
|
||||
padding-right: 0;
|
||||
border-bottom: 1px solid var(--hl-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
& > * {
|
||||
margin-right: var(--padding-md);
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 0 auto 0 0;
|
||||
font-size: var(--font-size-xl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ html {
|
||||
--padding-xl: calc(var(--font-size) * 5);
|
||||
|
||||
/* Wrapper */
|
||||
--line-height-lg: calc(var(--font-size) * 4.5);
|
||||
--line-height-md: calc(var(--font-size) * 3.6);
|
||||
--line-height-sm: calc(var(--font-size) * 2.88);
|
||||
--line-height-xs: calc(var(--font-size) * 2.448);
|
||||
|
||||
@@ -227,6 +227,7 @@
|
||||
--padding-xl: calc(var(--font-size) * 5);
|
||||
|
||||
/* Wrapper */
|
||||
--line-height-lg: calc(var(--font-size) * 4.5);
|
||||
--line-height-md: calc(var(--font-size) * 3.6);
|
||||
--line-height-sm: calc(var(--font-size) * 2.88);
|
||||
--line-height-xs: calc(var(--font-size) * 2.448);
|
||||
|
||||
@@ -45,4 +45,15 @@ describe('run', () => {
|
||||
expect(sendRequest).toHaveBeenCalledWith('req_123');
|
||||
expect(stats.passes).toBe(1);
|
||||
});
|
||||
|
||||
it('throws on invalid JavaScript', async () => {
|
||||
let err;
|
||||
try {
|
||||
await runTests('this is invalid');
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
|
||||
expect(err).not.toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -58,7 +58,7 @@ async function runInternal<T>(
|
||||
reporter: Reporter | Function,
|
||||
extractResult: (runner: Object) => T,
|
||||
): Promise<T> {
|
||||
return new Promise(resolve => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { bail } = options;
|
||||
// Add global `insomnia` helper.
|
||||
// This is the only way to add new globals to the Mocha environment as far
|
||||
@@ -78,22 +78,26 @@ async function runInternal<T>(
|
||||
mocha.addFile(writeTempFile(src));
|
||||
}
|
||||
|
||||
const runner = mocha.run(() => {
|
||||
resolve(extractResult(runner));
|
||||
try {
|
||||
const runner = mocha.run(() => {
|
||||
resolve(extractResult(runner));
|
||||
|
||||
// Remove global since we don't need it anymore
|
||||
delete global.insomnia;
|
||||
delete global.chai;
|
||||
// Remove global since we don't need it anymore
|
||||
delete global.insomnia;
|
||||
delete global.chai;
|
||||
|
||||
// Clean up temp files
|
||||
for (const f of mocha.files) {
|
||||
fs.unlink(f, err => {
|
||||
if (err) {
|
||||
console.log('Failed to clean up test file', f, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
// Clean up temp files
|
||||
for (const f of mocha.files) {
|
||||
fs.unlink(f, err => {
|
||||
if (err) {
|
||||
console.log('Failed to clean up test file', f, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user