From eae0d8d4332ea0ebdee9a386467b02c464b894b0 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Wed, 1 Jul 2020 15:57:42 -0700 Subject: [PATCH] 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 --- .../app/ui/components/base/editable.js | 1 - .../app/ui/components/wrapper-unit-test.js | 135 +++++++++++++----- .../app/ui/css/components/unit-tests.less | 60 ++++---- .../app/ui/css/constants/dimensions.less | 1 + .../.storybook/preview-head.html | 1 + .../src/run/__tests__/index.test.js | 11 ++ packages/insomnia-testing/src/run/index.js | 34 +++-- 7 files changed, 154 insertions(+), 89 deletions(-) diff --git a/packages/insomnia-app/app/ui/components/base/editable.js b/packages/insomnia-app/app/ui/components/base/editable.js index f7851a39c7..bf54882f56 100644 --- a/packages/insomnia-app/app/ui/components/base/editable.js +++ b/packages/insomnia-app/app/ui/components/base/editable.js @@ -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, }; diff --git a/packages/insomnia-app/app/ui/components/wrapper-unit-test.js b/packages/insomnia-app/app/ui/components/wrapper-unit-test.js index d5e8d0115d..74842f013b 100644 --- a/packages/insomnia-app/app/ui/components/wrapper-unit-test.js +++ b/packages/insomnia-app/app/ui/components/wrapper-unit-test.js @@ -30,12 +30,14 @@ type Props = {| type State = {| testsRunning: Array | null, + resultsError: string | null, |}; @autobind class WrapperUnitTest extends React.PureComponent { 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 { 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 { } async _handleDeleteTest(unitTest: UnitTest): Promise { - await models.unitTest.remove(unitTest); + showAlert({ + title: `Delete ${unitTest.name}`, + message: ( + + Really delete {unitTest.name}? + + ), + addCancel: true, + onConfirm: async () => { + await models.unitTest.remove(unitTest); + }, + }); } async _handleSetActiveRequest( @@ -202,10 +215,15 @@ class WrapperUnitTest extends React.PureComponent { await models.unitTest.update(unitTest, { name }); } + async _handleChangeActiveSuiteName(name: string): Promise { + const { activeUnitTestSuite } = this.props.wrapperProps; + await models.unitTestSuite.update(activeUnitTestSuite, { name }); + } + async _runTests(unitTests: Array): Promise { 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 { } 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 { renderResults(): React.Node { const { activeUnitTestResult } = this.props.wrapperProps; - const { testsRunning } = this.state; + const { testsRunning, resultsError } = this.state; + + if (resultsError) { + return ( +
+
+

Run Failed

+
+
{resultsError}
+
+ ); + } if (testsRunning) { return (
-

Running {testsRunning.length} Tests...

+
+

Running {testsRunning.length} Tests...

+
); } if (!activeUnitTestResult) { - return null; + return ( +
+
+

No Results

+
+
+ ); } const { stats, tests } = activeUnitTestResult.results; @@ -271,20 +316,21 @@ class WrapperUnitTest extends React.PureComponent {
{activeUnitTestResult && (
- {stats.failures ? ( -

- Tests Failed {stats.failures}/{stats.tests} -

- ) : ( -

- Tests Passed {stats.passes}/{stats.tests} -

- )} +
+ {stats.failures ? ( +

+ Tests Failed {stats.failures}/{stats.tests} +

+ ) : ( +

+ Tests Passed {stats.passes}/{stats.tests} +

+ )} +
    {tests.map((t, i) => (
  • - {t.fullTitle} ({t.duration}{' '} - ms) + {t.title} ({t.duration} ms) {t.err.message && ( <>
    @@ -369,11 +415,12 @@ class WrapperUnitTest extends React.PureComponent { renderPageBody(): React.Node { const { activeUnitTests, activeUnitTestSuite } = this.props.wrapperProps; + const { testsRunning } = this.state; if (!activeUnitTestSuite) { return (
    -
    No test suite selected
    +
    No test suite selected
    ); } @@ -381,12 +428,27 @@ class WrapperUnitTest extends React.PureComponent { return (
    - {activeUnitTests.map(this.renderUnitTest)} -
    - +
    + {activeUnitTests.map(this.renderUnitTest)}
    {this.renderResults()}
    @@ -395,6 +457,7 @@ class WrapperUnitTest extends React.PureComponent { 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 { )}> + + {testsRunning ? 'Running... ' : 'Run Tests'} + Delete Suite @@ -433,7 +502,6 @@ class WrapperUnitTest extends React.PureComponent { render() { const { handleActivityChange, gitSyncDropdown } = this.props; const { activeWorkspace, activity, settings, activeApiSpec } = this.props.wrapperProps; - const { testsRunning } = this.state; return ( { workspace={activeWorkspace} /> } - gridRight={ - <> - - {gitSyncDropdown} - - } + gridRight={gitSyncDropdown} /> )} /> diff --git a/packages/insomnia-app/app/ui/css/components/unit-tests.less b/packages/insomnia-app/app/ui/css/components/unit-tests.less index 536004a002..ec02b36d36 100644 --- a/packages/insomnia-app/app/ui/css/components/unit-tests.less +++ b/packages/insomnia-app/app/ui/css/components/unit-tests.less @@ -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); + } + } } diff --git a/packages/insomnia-app/app/ui/css/constants/dimensions.less b/packages/insomnia-app/app/ui/css/constants/dimensions.less index 2505f4ffad..c72df81a96 100644 --- a/packages/insomnia-app/app/ui/css/constants/dimensions.less +++ b/packages/insomnia-app/app/ui/css/constants/dimensions.less @@ -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); diff --git a/packages/insomnia-components/.storybook/preview-head.html b/packages/insomnia-components/.storybook/preview-head.html index 41afe8ab7f..e358b3c89b 100644 --- a/packages/insomnia-components/.storybook/preview-head.html +++ b/packages/insomnia-components/.storybook/preview-head.html @@ -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); diff --git a/packages/insomnia-testing/src/run/__tests__/index.test.js b/packages/insomnia-testing/src/run/__tests__/index.test.js index ad9a593216..452ec41e37 100644 --- a/packages/insomnia-testing/src/run/__tests__/index.test.js +++ b/packages/insomnia-testing/src/run/__tests__/index.test.js @@ -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(); + }); }); diff --git a/packages/insomnia-testing/src/run/index.js b/packages/insomnia-testing/src/run/index.js index d6a0ab7c3c..46c96e8b1c 100644 --- a/packages/insomnia-testing/src/run/index.js +++ b/packages/insomnia-testing/src/run/index.js @@ -58,7 +58,7 @@ async function runInternal( reporter: Reporter | Function, extractResult: (runner: Object) => T, ): Promise { - 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( 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); + } }); }