diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2019-04-04 15:16:46 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-05-29 20:21:13 +0200 |
commit | 46d075adfb3c4a666010a14d925203690bad0f10 (patch) | |
tree | e8ea0e2c628e2ddab3b02dd703a0f6dd31aa8ade /server/sonar-web | |
parent | 7853e9f613ae662e17a568bd70b3d65e5898ba80 (diff) | |
download | sonarqube-46d075adfb3c4a666010a14d925203690bad0f10.tar.gz sonarqube-46d075adfb3c4a666010a14d925203690bad0f10.zip |
Improve test coverage
Diffstat (limited to 'server/sonar-web')
4 files changed, 833 insertions, 10 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx index 7647847d84c..db901c28c1f 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx @@ -26,7 +26,9 @@ import { mockIssue, mockLocation, mockEvent, - mockCurrentUser + mockCurrentUser, + mockPullRequest, + mockComponent } from '../../../../helpers/testMocks'; import handleRequiredAuthentication from '../../../../app/utils/handleRequiredAuthentication'; import { @@ -60,10 +62,10 @@ jest.mock('keymaster', () => { }); const ISSUES = [ - { key: 'foo' } as T.Issue, - { key: 'bar' } as T.Issue, - { key: 'third' } as T.Issue, - { key: 'fourth' } as T.Issue + mockIssue(false, { key: 'foo' }), + mockIssue(false, { key: 'bar' }), + mockIssue(true, { key: 'third' }), + mockIssue(false, { key: 'fourth' }) ]; const FACETS = [{ property: 'severities', values: [{ val: 'MINOR', count: 4 }] }]; const PAGING = { pageIndex: 1, pageSize: 100, total: 4 }; @@ -110,6 +112,45 @@ it('should open standard facets for vulnerabilities and hotspots', () => { expect(fetchFacet).lastCalledWith('owaspTop10'); }); +it('should switch to source view if an issue is selected', async () => { + const wrapper = shallowRender(); + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); + + wrapper.setProps({ location: mockLocation({ query: { open: 'third' } }) }); + await waitAndUpdate(wrapper); + expect(wrapper).toMatchSnapshot(); +}); + +it('should correctly bind key events for issue navigation', async () => { + const push = jest.fn(); + const wrapper = shallowRender({ router: mockRouter({ push }) }); + await waitAndUpdate(wrapper); + + expect(wrapper.state('selected')).toBe(ISSUES[0].key); + + keydown('down'); + expect(wrapper.state('selected')).toBe(ISSUES[1].key); + + keydown('up'); + keydown('up'); + expect(wrapper.state('selected')).toBe(ISSUES[0].key); + + keydown('down'); + keydown('down'); + keydown('down'); + keydown('down'); + keydown('down'); + keydown('down'); + expect(wrapper.state('selected')).toBe(ISSUES[3].key); + + keydown('right'); + expect(push).toBeCalledTimes(1); + + keydown('left'); + expect(push).toBeCalledTimes(2); +}); + it('should be able to uncheck all issue with global checkbox', async () => { const wrapper = shallowRender(); await waitAndUpdate(wrapper); @@ -311,6 +352,40 @@ describe('keydown event handler', () => { }); }); +it('should fetch more issues', async () => { + const wrapper = shallowRender({ + fetchIssues: fetchIssuesMockFactory() + }); + const instance = wrapper.instance(); + await waitAndUpdate(wrapper); + + await instance.fetchMoreIssues(); + await waitAndUpdate(wrapper); + expect(wrapper.state('issues')).toHaveLength(4); +}); + +it('should refresh branch status if issues are updated', async () => { + const fetchBranchStatus = jest.fn(); + const branchLike = mockPullRequest(); + const component = mockComponent(); + const wrapper = shallowRender({ branchLike, component, fetchBranchStatus }); + const instance = wrapper.instance(); + await waitAndUpdate(wrapper); + + const updatedIssue: T.Issue = { ...ISSUES[0], type: 'SECURITY_HOTSPOT' }; + instance.handleIssueChange(updatedIssue); + expect(wrapper.state('issues')).toEqual([updatedIssue, ISSUES[1], ISSUES[2], ISSUES[3]]); + expect(fetchBranchStatus).toBeCalledWith(branchLike, component.key); + + fetchBranchStatus.mockClear(); + instance.handleBulkChangeDone(); + expect(fetchBranchStatus).toBeCalled(); + + fetchBranchStatus.mockClear(); + instance.handleReload(); + expect(fetchBranchStatus).toBeCalled(); +}); + function fetchIssuesMockFactory(keyCount = 0, lineCount = 1) { return jest.fn().mockImplementation(({ p }: any) => Promise.resolve({ diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/App-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/App-test.tsx.snap index 0aa0114a24b..c49a5fd8b32 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/App-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/App-test.tsx.snap @@ -19,3 +19,682 @@ exports[`should check max 500 issues 1`] = ` issues.bulk_change_X_issues.500 </Button> `; + +exports[`should switch to source view if an issue is selected 1`] = ` +<div + className="layout-page issues" + id="issues-page" +> + <Suggestions + suggestions="issues" + /> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="issues.page" + /> + <ScreenPositionHelper + className="layout-page-side-outer" + > + <Component /> + </ScreenPositionHelper> + <div + className="layout-page-main" + > + <div + className="layout-page-header-panel layout-page-main-header issues-main-header" + > + <div + className="layout-page-header-panel-inner layout-page-main-header-inner" + > + <div + className="layout-page-main-inner" + > + <A11ySkipTarget + anchor="issues_main" + /> + <div + className="pull-left" + > + <Checkbox + checked={false} + className="spacer-right vertical-middle" + disabled={false} + id="issues-selection" + onCheck={[Function]} + thirdState={false} + /> + <Button + disabled={true} + id="issues-bulk-change" + onClick={[Function]} + > + bulk_change + </Button> + </div> + <PageActions + canSetHome={false} + effortTotal={1} + onReload={[Function]} + paging={ + Object { + "pageIndex": 1, + "pageSize": 100, + "total": 4, + } + } + selectedIndex={0} + /> + </div> + </div> + </div> + <div + className="layout-page-main-inner" + > + <DeferredSpinner + loading={false} + timeout={100} + > + <div> + <IssuesList + checked={Array []} + component={ + Object { + "breadcrumbs": Array [], + "key": "foo", + "name": "bar", + "organization": "John", + "qualifier": "Doe", + } + } + issues={ + Array [ + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [], + "fromHotspot": false, + "key": "foo", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + }, + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [], + "fromHotspot": false, + "key": "bar", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + }, + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [ + Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + ], + "fromHotspot": false, + "key": "third", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + }, + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [], + "fromHotspot": false, + "key": "fourth", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + }, + ] + } + onFilterChange={[Function]} + onIssueChange={[Function]} + onIssueCheck={[Function]} + onIssueClick={[Function]} + onPopupToggle={[Function]} + organization={ + Object { + "key": "foo", + } + } + selectedIssue={ + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [], + "fromHotspot": false, + "key": "foo", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + } + } + /> + <ListFooter + count={4} + loadMore={[Function]} + loading={false} + total={4} + /> + </div> + </DeferredSpinner> + </div> + </div> +</div> +`; + +exports[`should switch to source view if an issue is selected 2`] = ` +<div + className="layout-page issues" + id="issues-page" +> + <Suggestions + suggestions="issues" + /> + <HelmetWrapper + defer={true} + encodeSpecialCharacters={true} + title="Reduce the number of conditional operators (4) used in the expression" + /> + <ScreenPositionHelper + className="layout-page-side-outer" + > + <Component /> + </ScreenPositionHelper> + <div + className="layout-page-main" + > + <A11ySkipTarget + anchor="issues_main" + /> + <div + className="layout-page-main-inner" + > + <IssuesSourceViewer + issues={ + Array [ + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [], + "fromHotspot": false, + "key": "foo", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + }, + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [], + "fromHotspot": false, + "key": "bar", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + }, + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [ + Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + ], + "fromHotspot": false, + "key": "third", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + }, + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [], + "fromHotspot": false, + "key": "fourth", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + }, + ] + } + loadIssues={[Function]} + locationsNavigator={false} + onIssueChange={[Function]} + onIssueSelect={[Function]} + onLocationSelect={[Function]} + openIssue={ + Object { + "actions": Array [], + "component": "main.js", + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": Array [ + Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + ], + "fromHotspot": false, + "key": "third", + "line": 25, + "message": "Reduce the number of conditional operators (4) used in the expression", + "organization": "myorg", + "project": "myproject", + "projectKey": "foo", + "projectName": "Foo", + "projectOrganization": "org", + "rule": "javascript:S1067", + "ruleName": "foo", + "secondaryLocations": Array [ + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + Object { + "component": "main.js", + "textRange": Object { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + "severity": "MAJOR", + "status": "OPEN", + "textRange": Object { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + } + } + /> + </div> + </div> +</div> +`; diff --git a/server/sonar-web/src/main/js/helpers/__tests__/qualityGates-test.ts b/server/sonar-web/src/main/js/helpers/__tests__/qualityGates-test.ts new file mode 100644 index 00000000000..1b440f03edf --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/__tests__/qualityGates-test.ts @@ -0,0 +1,71 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { + isSameStatusConditionList, + extractStatusConditionsFromProjectStatus +} from '../qualityGates'; +import { mockQualityGateStatusCondition, mockQualityGateProjectStatus } from '../testMocks'; + +describe('extractStatusConditionsFromProjectStatus', () => { + it('should correclty extract the conditions for the project status', () => { + expect(extractStatusConditionsFromProjectStatus(mockQualityGateProjectStatus())).toEqual([ + { + actual: '0', + error: '1.0', + level: 'OK', + metric: 'new_bugs', + op: 'GT', + period: 1 + } + ]); + }); +}); + +describe('isSameStatusConditionList', () => { + it('should correctly return true if the conditions are the same', () => { + expect(isSameStatusConditionList()).toBe(true); + expect(isSameStatusConditionList([], [])).toBe(true); + expect( + isSameStatusConditionList( + [mockQualityGateStatusCondition()], + [mockQualityGateStatusCondition()] + ) + ).toBe(true); + }); + + it('should correctly return false if any condition is different', () => { + expect(isSameStatusConditionList([mockQualityGateStatusCondition()])).toBe(false); + expect(isSameStatusConditionList(undefined, [mockQualityGateStatusCondition()])).toBe(false); + expect(isSameStatusConditionList([], [mockQualityGateStatusCondition()])).toBe(false); + expect(isSameStatusConditionList([mockQualityGateStatusCondition()], [])).toBe(false); + expect( + isSameStatusConditionList( + [mockQualityGateStatusCondition({ metric: 'foo' })], + [mockQualityGateStatusCondition({ metric: 'bar' })] + ) + ).toBe(false); + expect( + isSameStatusConditionList( + [mockQualityGateStatusCondition({ metric: 'foo', level: '2.0' })], + [mockQualityGateStatusCondition({ metric: 'foo', level: '1.0' })] + ) + ).toBe(false); + }); +}); diff --git a/server/sonar-web/src/main/js/helpers/qualityGates.ts b/server/sonar-web/src/main/js/helpers/qualityGates.ts index 5dbb8a257b1..090647a8781 100644 --- a/server/sonar-web/src/main/js/helpers/qualityGates.ts +++ b/server/sonar-web/src/main/js/helpers/qualityGates.ts @@ -34,12 +34,10 @@ export function extractStatusConditionsFromProjectStatus( } export function isSameStatusConditionList( - conditions?: T.QualityGateStatusCondition[], - prevConditions?: T.QualityGateStatusCondition[] + conditions: T.QualityGateStatusCondition[] = [], + prevConditions: T.QualityGateStatusCondition[] = [] ): boolean { - if (conditions === undefined || prevConditions === undefined) { - return !(prevConditions || conditions); - } else if (conditions.length !== prevConditions.length) { + if (conditions.length !== prevConditions.length) { return false; } else { const filtered = conditions.filter(c1 => { |