ソースを参照

Update to React 16 and Enzyme 3 (#2776)

* Update to React 16

* Add missing promise catch

* Fix jest tests

* extract react-dom mock

* remove empty snapshots

* Fix modals
tags/7.0-RC1
Grégoire Aubert 6年前
コミット
ee9b4cce45
コミッターのメールアドレスに関連付けられたアカウントが存在しません
100個のファイルの変更752行の追加473行の削除
  1. 23
    0
      server/sonar-web/config/jest/SetupEnzyme.js
  2. 6
    0
      server/sonar-web/config/jest/SetupTestEnvironment.js
  3. 16
    15
      server/sonar-web/package.json
  4. 26
    0
      server/sonar-web/src/main/js/__mocks__/react-dom.ts
  5. 34
    41
      server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx
  6. 5
    5
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx
  7. 1
    0
      server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap
  8. 7
    9
      server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx
  9. 1
    0
      server/sonar-web/src/main/js/app/components/search/__tests__/SearchResult-test.js
  10. 2
    2
      server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.js
  11. 11
    4
      server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.tsx
  12. 15
    8
      server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx
  13. 15
    11
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/ScannerContext-test.tsx
  14. 15
    11
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Stacktrace-test.tsx
  15. 2
    0
      server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/TaskActions-test.tsx
  16. 3
    1
      server/sonar-web/src/main/js/apps/code/components/App.tsx
  17. 5
    1
      server/sonar-web/src/main/js/apps/component-measures/components/App.js
  18. 1
    1
      server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.js
  19. 3
    0
      server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.js.snap
  20. 1
    0
      server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx
  21. 1
    1
      server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx
  22. 0
    2
      server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap
  23. 3
    5
      server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx
  24. 6
    6
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx
  25. 1
    1
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx
  26. 10
    2
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx
  27. 1
    1
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginLicense-test.tsx
  28. 1
    1
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginUrls-test.tsx
  29. 25
    5
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap
  30. 10
    0
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap
  31. 0
    2
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginLicense-test.tsx.snap
  32. 0
    2
      server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginUrls-test.tsx.snap
  33. 2
    4
      server/sonar-web/src/main/js/apps/organizations/components/OrganizationAdmin.js
  34. 1
    3
      server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js
  35. 9
    7
      server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationAdmin-test.js
  36. 2
    0
      server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationMembers-test.js
  37. 11
    10
      server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationPage-test.js
  38. 0
    2
      server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationAdmin-test.js.snap
  39. 4
    3
      server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap
  40. 9
    5
      server/sonar-web/src/main/js/apps/organizations/components/forms/AddMemberForm.js
  41. 9
    5
      server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js
  42. 9
    5
      server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js
  43. 7
    3
      server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/AddMemberForm-test.js
  44. 7
    5
      server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/ManageMemberGroupsForm-test.js
  45. 5
    3
      server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/RemoveMemberForm-test.js
  46. 11
    7
      server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/AddMemberForm-test.js.snap
  47. 11
    11
      server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap
  48. 11
    8
      server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/RemoveMemberForm-test.js.snap
  49. 4
    4
      server/sonar-web/src/main/js/apps/organizations/routes.js
  50. 2
    6
      server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.js.snap
  51. 1
    1
      server/sonar-web/src/main/js/apps/overview/meta/MetaLink.js
  52. 9
    3
      server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTags-test.js
  53. 0
    1
      server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaLink-test.js.snap
  54. 2
    2
      server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/GraphsTooltipsContentDuplication-test.js
  55. 0
    2
      server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/GraphsTooltipsContentDuplication-test.js.snap
  56. 9
    6
      server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js
  57. 11
    5
      server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddGraphMetric.js
  58. 3
    3
      server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js
  59. 9
    5
      server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js
  60. 3
    2
      server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js
  61. 11
    11
      server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LongBranchesPattern-test.tsx
  62. 6
    6
      server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
  63. 4
    2
      server/sonar-web/src/main/js/apps/projects/components/__tests__/FavoriteFilter-test.tsx
  64. 1
    1
      server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLanguages-test.tsx
  65. 1
    1
      server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardQualityGate-test.tsx
  66. 69
    3
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
  67. 0
    2
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/FavoriteFilter-test.tsx.snap
  68. 8
    26
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap
  69. 0
    2
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap
  70. 4
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap
  71. 6
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap
  72. 10
    5
      server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx
  73. 8
    6
      server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx
  74. 1
    0
      server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx
  75. 1
    0
      server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx
  76. 2
    2
      server/sonar-web/src/main/js/apps/quality-gates/components/Details.js
  77. 2
    2
      server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx
  78. 10
    7
      server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.tsx
  79. 14
    9
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx
  80. 7
    6
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsFormSelect-test.tsx
  81. 15
    25
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx
  82. 1
    1
      server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsFormSelect-test.tsx.snap
  83. 1
    1
      server/sonar-web/src/main/js/apps/quality-profiles/routes.ts
  84. 1
    0
      server/sonar-web/src/main/js/apps/system/components/info-items/__tests__/__snapshots__/SysInfoItem-test.tsx.snap
  85. 5
    18
      server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeNotif-test.tsx
  86. 1
    5
      server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeNotif-test.tsx.snap
  87. 11
    11
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewOrganizationForm-test.js
  88. 11
    11
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewProjectForm-test.js
  89. 27
    20
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js
  90. 10
    10
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectKeyStep-test.js
  91. 8
    3
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js
  92. 17
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationStep-test.js.snap
  93. 18
    0
      server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap
  94. 2
    2
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Command-test.js
  95. 16
    11
      server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Command-test.js.snap
  96. 2
    2
      server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx
  97. 5
    3
      server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.js
  98. 22
    0
      server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.js.snap
  99. 3
    1
      server/sonar-web/src/main/js/components/common/MultiSelect.js
  100. 0
    0
      server/sonar-web/src/main/js/components/common/__tests__/BranchStatus-test.tsx

+ 23
- 0
server/sonar-web/config/jest/SetupEnzyme.js ファイルの表示

@@ -0,0 +1,23 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact 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 Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

Enzyme.configure({ adapter: new Adapter() });

+ 6
- 0
server/sonar-web/config/jest/SetupTestEnvironment.js ファイルの表示

@@ -22,3 +22,9 @@ window.t = window.tp = function() {
const args = Array.prototype.slice.call(arguments, 0);
return args.join('.');
};

// Fix for https://github.com/facebook/jest/issues/4545
// Try to remove when jest 21.3.0 is out
window.requestAnimationFrame = function(callback) {
setTimeout(callback, 0);
};

+ 16
- 15
server/sonar-web/package.json ファイルの表示

@@ -26,14 +26,14 @@
"lodash": "4.17.4",
"prop-types": "15.6.0",
"rc-tooltip": "3.5.0",
"react": "15.6.2",
"react-dom": "15.6.2",
"react": "16.0.0",
"react-dom": "16.0.0",
"react-draggable": "3.0.3",
"react-helmet": "5.2.0",
"react-intl": "2.4.0",
"react-modal": "3.0.4",
"react-redux": "5.0.6",
"react-router": "3.0.5",
"react-router": "3.2.0",
"react-select": "1.0.0-rc.10",
"react-virtualized": "9.12.0",
"redux": "3.7.2",
@@ -46,20 +46,19 @@
"@types/classnames": "2.2.3",
"@types/d3-array": "1.2.1",
"@types/d3-scale": "1.0.10",
"@types/date-fns": "2.6.0",
"@types/enzyme": "2.8.6",
"@types/enzyme": "3.1.1",
"@types/escape-html": "0.0.20",
"@types/jest": "21.1.5",
"@types/jquery": "3.2.11",
"@types/lodash": "4.14.79",
"@types/lodash": "4.14.80",
"@types/prop-types": "15.5.2",
"@types/react": "16.0.2",
"@types/react-dom": "15.5.2",
"@types/react": "16.0.19",
"@types/react-dom": "16.0.2",
"@types/react-helmet": "5.0.3",
"@types/react-intl": "2.3.2",
"@types/react-modal": "2.2.2",
"@types/react-redux": "5.0.10",
"@types/react-router": "3.0.5",
"@types/react-redux": "5.0.12",
"@types/react-router": "3.0.13",
"@types/react-select": "1.0.59",
"autoprefixer": "7.1.6",
"awesome-typescript-loader": "3.2.3",
@@ -77,8 +76,9 @@
"babel-preset-react": "^6.22.0",
"chalk": "2.3.0",
"css-loader": "0.28.7",
"enzyme": "2.9.1",
"enzyme-to-json": "2.0.1",
"enzyme": "3.1.0",
"enzyme-adapter-react-16": "1.0.2",
"enzyme-to-json": "3.1.4",
"eslint": "4.9.0",
"eslint-plugin-import": "2.8.0",
"eslint-plugin-jsx-a11y": "6.0.2",
@@ -96,10 +96,10 @@
"postcss-loader": "2.0.8",
"prettier": "1.7.4",
"react-error-overlay": "1.0.7",
"react-test-renderer": "15.6.2",
"react-test-renderer": "16.0.0",
"rimraf": "2.6.2",
"style-loader": "0.19.0",
"ts-jest": "21.1.3",
"ts-jest": "21.1.4",
"typescript": "2.5.3",
"typescript-eslint-parser": "8.0.1",
"webpack": "3.8.1",
@@ -141,7 +141,8 @@
},
"setupFiles": [
"<rootDir>/config/polyfills.js",
"<rootDir>/config/jest/SetupTestEnvironment.js"
"<rootDir>/config/jest/SetupTestEnvironment.js",
"<rootDir>/config/jest/SetupEnzyme.js"
],
"snapshotSerializers": ["enzyme-to-json/serializer"],
"testPathIgnorePatterns": [

+ 26
- 0
server/sonar-web/src/main/js/__mocks__/react-dom.ts ファイルの表示

@@ -0,0 +1,26 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:contact 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.
*/
const ReactDOM = require.requireActual('react-dom');

//FIXME To be dropped when https://github.com/airbnb/enzyme/issues/1150 is resolved
module.exports = {
...ReactDOM,
createPortal: (children: React.ReactNode) => children
};

+ 34
- 41
server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx ファイルの表示

@@ -17,23 +17,29 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first */
jest.mock('../../../api/branches', () => ({ getBranches: jest.fn() }));
jest.mock('../../../api/components', () => ({ getComponentData: jest.fn() }));
jest.mock('../../../api/nav', () => ({ getComponentNavigation: jest.fn() }));

// mock this, because some of its children are using redux store
jest.mock('../nav/component/ComponentNav', () => ({
default: () => null
}));

import * as React from 'react';
import { shallow, mount } from 'enzyme';
import { ComponentContainer } from '../ComponentContainer';
import { getBranches } from '../../../api/branches';
import { getComponentData } from '../../../api/components';
import { getComponentNavigation } from '../../../api/nav';
import { doAsync } from '../../../helpers/testUtils';

jest.mock('../../../api/branches', () => ({ getBranches: jest.fn(() => Promise.resolve([])) }));
jest.mock('../../../api/components', () => ({
getComponentData: jest.fn(() => Promise.resolve({}))
}));
jest.mock('../../../api/nav', () => ({
getComponentNavigation: jest.fn(() =>
Promise.resolve({
breadcrumbs: [{ key: 'portfolioKey', name: 'portfolio', qualifier: 'VW' }]
})
)
}));

// mock this, because some of its children are using redux store
jest.mock('../nav/component/ComponentNav', () => ({
default: () => null
}));

const Inner = () => <div />;

@@ -60,10 +66,8 @@ it('changes component', () => {
expect(wrapper.state().component).toEqual({ qualifier: 'TRK', visibility: 'private' });
});

it("loads branches for module's project", () => {
(getBranches as jest.Mock<any>).mockImplementation(() => Promise.resolve([]));
(getComponentData as jest.Mock<any>).mockImplementation(() => Promise.resolve({}));
(getComponentNavigation as jest.Mock<any>).mockImplementation(() =>
it("loads branches for module's project", async () => {
(getComponentNavigation as jest.Mock<any>).mockImplementationOnce(() =>
Promise.resolve({
breadcrumbs: [
{ key: 'projectKey', name: 'project', qualifier: 'TRK' },
@@ -78,38 +82,28 @@ it("loads branches for module's project", () => {
</ComponentContainer>
);

return doAsync().then(() => {
expect(getBranches).toBeCalledWith('projectKey');
expect(getComponentData).toBeCalledWith('moduleKey', undefined);
expect(getComponentNavigation).toBeCalledWith('moduleKey', undefined);
});
await new Promise(setImmediate);
expect(getBranches).toBeCalledWith('projectKey');
expect(getComponentData).toBeCalledWith('moduleKey', undefined);
expect(getComponentNavigation).toBeCalledWith('moduleKey', undefined);
});

it("doesn't load branches portfolio", () => {
(getBranches as jest.Mock<any>).mockImplementation(() => Promise.resolve([]));
(getComponentData as jest.Mock<any>).mockImplementation(() => Promise.resolve({}));
(getComponentNavigation as jest.Mock<any>).mockImplementation(() =>
Promise.resolve({
breadcrumbs: [{ key: 'portfolioKey', name: 'portfolio', qualifier: 'VW' }]
})
);

it("doesn't load branches portfolio", async () => {
const wrapper = mount(
<ComponentContainer fetchOrganizations={jest.fn()} location={{ query: { id: 'portfolioKey' } }}>
<Inner />
</ComponentContainer>
);

return doAsync().then(() => {
expect(getBranches).not.toBeCalled();
expect(getComponentData).toBeCalledWith('portfolioKey', undefined);
expect(getComponentNavigation).toBeCalledWith('portfolioKey', undefined);
expect(wrapper.find(Inner).exists()).toBeTruthy();
});
await new Promise(setImmediate);
expect(getBranches).not.toBeCalled();
expect(getComponentData).toBeCalledWith('portfolioKey', undefined);
expect(getComponentNavigation).toBeCalledWith('portfolioKey', undefined);
wrapper.update();
expect(wrapper.find(Inner).exists()).toBeTruthy();
});

it('updates branches on change', () => {
(getBranches as jest.Mock<any>).mockImplementation(() => Promise.resolve([]));
const wrapper = shallow(
<ComponentContainer fetchOrganizations={jest.fn()} location={{ query: { id: 'portfolioKey' } }}>
<Inner />
@@ -125,8 +119,8 @@ it('updates branches on change', () => {
expect(getBranches).toBeCalledWith('projectKey');
});

it('loads organization', () => {
(getComponentData as jest.Mock<any>).mockImplementation(() =>
it('loads organization', async () => {
(getComponentData as jest.Mock<any>).mockImplementationOnce(() =>
Promise.resolve({ organization: 'org' })
);

@@ -140,7 +134,6 @@ it('loads organization', () => {
</ComponentContainer>
);

return doAsync().then(() => {
expect(fetchOrganizations).toBeCalledWith(['org']);
});
await new Promise(setImmediate);
expect(fetchOrganizations).toBeCalledWith(['org']);
});

+ 5
- 5
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx ファイルの表示

@@ -17,7 +17,11 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first, import/order */
/* eslint-disable import/order */
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import ComponentNav from '../ComponentNav';

jest.mock('../ComponentNavFavorite', () => ({
// eslint-disable-next-line
default: function ComponentNavFavorite() {
@@ -47,10 +51,6 @@ jest.mock('../../../../../api/ce', () => ({
getTasksForComponent: jest.fn(() => Promise.resolve({ queue: [] }))
}));

import * as React from 'react';
import { mount, shallow } from 'enzyme';
import ComponentNav from '../ComponentNav';

const getTasksForComponent = require('../../../../../api/ce').getTasksForComponent as jest.Mock<
any
>;

+ 1
- 0
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap ファイルの表示

@@ -21,6 +21,7 @@ exports[`renders 1`] = `
"qualifier": "TRK",
}
}
currentTask={undefined}
isInProgress={true}
isPending={true}
/>

+ 7
- 9
server/sonar-web/src/main/js/app/components/nav/settings/SettingsEditionsNotif.tsx ファイルの表示

@@ -64,7 +64,7 @@ export default class SettingsEditionsNotif extends React.PureComponent<Props, St
</button>
)}
{!preventRestart &&
this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />}
this.state.openRestart && <RestartForm onClose={this.hanleCloseRestart} />}
</NavBarNotif>
);
}
@@ -73,14 +73,12 @@ export default class SettingsEditionsNotif extends React.PureComponent<Props, St
const { editionStatus } = this.props;
return (
<NavBarNotif className="alert alert-danger">
{edition ? (
translateWithParameters(
'marketplace.status_x.' + editionStatus.installationStatus,
edition.name
)
) : (
translate('marketplace.status', editionStatus.installationStatus)
)}
{edition
? translateWithParameters(
'marketplace.status_x.' + editionStatus.installationStatus,
edition.name
)
: translate('marketplace.status', editionStatus.installationStatus)}
{edition && (
<a
className="button spacer-left"

+ 1
- 0
server/sonar-web/src/main/js/app/components/search/__tests__/SearchResult-test.js ファイルの表示

@@ -118,6 +118,7 @@ it('shows tooltip after delay', () => {
expect(wrapper.find('Tooltip').prop('visible')).toBe(false);

jest.runAllTimers();
wrapper.update();
expect(wrapper.find('Tooltip').prop('visible')).toBe(true);

wrapper.setProps({ selected: false });

+ 2
- 2
server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.js ファイルの表示

@@ -30,8 +30,8 @@ export default class UserExternalIdentity extends React.PureComponent {
this.fetchIdentityProviders();
}

componentDidUpdate(nextProps) {
if (nextProps.user !== this.props.user) {
componentDidUpdate(prevProps) {
if (prevProps.user !== this.props.user) {
this.this.fetchIdentityProviders();
}
}

+ 11
- 4
server/sonar-web/src/main/js/apps/background-tasks/components/Stacktrace.tsx ファイルの表示

@@ -47,11 +47,18 @@ export default class Stacktrace extends React.PureComponent<Props, State> {
}

loadStacktrace() {
getTask(this.props.task.id, ['stacktrace']).then(task => {
if (this.mounted) {
this.setState({ loading: false, stacktrace: task.errorStacktrace });
getTask(this.props.task.id, ['stacktrace']).then(
task => {
if (this.mounted) {
this.setState({ loading: false, stacktrace: task.errorStacktrace });
}
},
() => {
if (this.mounted) {
this.setState({ loading: false });
}
}
});
);
}

handleCloseClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {

+ 15
- 8
server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx ファイルの表示

@@ -56,15 +56,22 @@ export default class Workers extends React.PureComponent<{}, State> {

loadWorkers = () => {
this.setState({ loading: true });
getWorkers().then(({ canSetWorkerCount, value }) => {
if (this.mounted) {
this.setState({
canSetWorkerCount,
loading: false,
workerCount: value
});
getWorkers().then(
({ canSetWorkerCount, value }) => {
if (this.mounted) {
this.setState({
canSetWorkerCount,
loading: false,
workerCount: value
});
}
},
() => {
if (this.mounted) {
this.setState({ loading: false });
}
}
});
);
};

closeForm = (newWorkerCount?: number) =>

+ 15
- 11
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/ScannerContext-test.tsx ファイルの表示

@@ -17,15 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first, import/order */
jest.mock('../../../../api/ce', () => ({
getTask: jest.fn()
}));

/* eslint-disable import/order */
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import ScannerContext from '../ScannerContext';
import { click, doAsync } from '../../../../helpers/testUtils';
import { click } from '../../../../helpers/testUtils';

jest.mock('react-dom');

jest.mock('../../../../api/ce', () => ({
getTask: jest.fn(() => Promise.resolve({ scannerContext: 'context' }))
}));

const getTask = require('../../../../api/ce').getTask as jest.Mock<any>;

@@ -37,6 +39,10 @@ const task = {
type: 'REPORT'
};

beforeEach(() => {
getTask.mockClear();
});

it('renders', () => {
const wrapper = shallow(<ScannerContext onClose={jest.fn()} task={task} />);
wrapper.setState({ scannerContext: 'context' });
@@ -50,12 +56,10 @@ it('closes', () => {
expect(onClose).toBeCalled();
});

it('fetches scanner context on mount', () => {
getTask.mockImplementation(() => Promise.resolve({ scannerContext: 'context' }));
it('fetches scanner context on mount', async () => {
const wrapper = mount(<ScannerContext onClose={jest.fn()} task={task} />);
expect(wrapper.state()).toEqual({});
expect(getTask).toBeCalledWith('123', ['scannerContext']);
return doAsync().then(() => {
expect(wrapper.state()).toEqual({ scannerContext: 'context' });
});
await new Promise(setImmediate);
expect(wrapper.state()).toEqual({ scannerContext: 'context' });
});

+ 15
- 11
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Stacktrace-test.tsx ファイルの表示

@@ -17,15 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first, import/order */
jest.mock('../../../../api/ce', () => ({
getTask: jest.fn()
}));

/* eslint-disable import/order */
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import Stacktrace from '../Stacktrace';
import { click, doAsync } from '../../../../helpers/testUtils';
import { click } from '../../../../helpers/testUtils';

jest.mock('react-dom');

jest.mock('../../../../api/ce', () => ({
getTask: jest.fn(() => Promise.resolve({ errorStacktrace: 'stacktrace' }))
}));

const getTask = require('../../../../api/ce').getTask as jest.Mock<any>;

@@ -37,6 +39,10 @@ const task = {
type: 'REPORT'
};

beforeEach(() => {
getTask.mockClear();
});

it('renders', () => {
const wrapper = shallow(<Stacktrace onClose={jest.fn()} task={task} />);
wrapper.setState({ loading: false, stacktrace: 'stacktrace' });
@@ -50,12 +56,10 @@ it('closes', () => {
expect(onClose).toBeCalled();
});

it('fetches scanner context on mount', () => {
getTask.mockImplementation(() => Promise.resolve({ errorStacktrace: 'stacktrace' }));
it('fetches scanner context on mount', async () => {
const wrapper = mount(<Stacktrace onClose={jest.fn()} task={task} />);
expect(wrapper.state()).toEqual({ loading: true });
expect(getTask).toBeCalledWith('123', ['stacktrace']);
return doAsync().then(() => {
expect(wrapper.state()).toEqual({ loading: false, stacktrace: 'stacktrace' });
});
await new Promise(setImmediate);
expect(wrapper.state()).toEqual({ loading: false, stacktrace: 'stacktrace' });
});

+ 2
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/TaskActions-test.tsx ファイルの表示

@@ -35,6 +35,7 @@ it('shows stack trace', () => {
click(wrapper.find('.js-task-show-stacktrace'));
expect(wrapper.find('Stacktrace')).toMatchSnapshot();
wrapper.find('Stacktrace').prop<Function>('onClose')();
wrapper.update();
expect(wrapper.find('Stacktrace').exists()).toBeFalsy();
});

@@ -43,6 +44,7 @@ it('shows scanner context', () => {
click(wrapper.find('.js-task-show-scanner-context'));
expect(wrapper.find('ScannerContext')).toMatchSnapshot();
wrapper.find('ScannerContext').prop<Function>('onClose')();
wrapper.update();
expect(wrapper.find('ScannerContext').exists()).toBeFalsy();
});


+ 3
- 1
server/sonar-web/src/main/js/apps/code/components/App.tsx ファイルの表示

@@ -94,7 +94,9 @@ export default class App extends React.PureComponent<Props, State> {
retrieveComponentChildren(component.key, isPortfolio, branch && getBranchName(branch))
.then(() => {
addComponent(component);
this.handleUpdate();
if (this.mounted) {
this.handleUpdate();
}
})
.catch(e => {
if (this.mounted) {

+ 5
- 1
server/sonar-web/src/main/js/apps/component-measures/components/App.js ファイルの表示

@@ -117,7 +117,11 @@ export default class App extends React.PureComponent {
});
}
},
() => this.setState({ loading: false })
() => {
if (this.mounted) {
this.setState({ loading: false });
}
}
);
};


+ 1
- 1
server/sonar-web/src/main/js/apps/component-measures/components/__tests__/App-test.js ファイルの表示

@@ -42,7 +42,7 @@ const PROPS = {
branch: { isMain: true, name: 'master' },
component: { key: 'foo' },
location: { pathname: '/component_measures', query: { metric: 'coverage' } },
fetchMeasures: () => {},
fetchMeasures: () => Promise.resolve({ measures: [] }),
fetchMetrics: () => {},
metrics: METRICS,
metricsKey: ['lines_to_cover', 'coverage', 'duplicated_lines_density', 'new_bugs'],

+ 3
- 0
server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/AssigneeFacet-test.js.snap ファイルの表示

@@ -14,6 +14,7 @@ exports[`should render 1`] = `
active={false}
disabled={false}
halfWidth={false}
key=""
name="unassigned"
onClick={[Function]}
stat="5"
@@ -100,6 +101,7 @@ exports[`should select unassigned 1`] = `
active={true}
disabled={false}
halfWidth={false}
key=""
name="unassigned"
onClick={[Function]}
stat="5"
@@ -162,6 +164,7 @@ exports[`should select user 1`] = `
active={false}
disabled={false}
halfWidth={false}
key=""
name="unassigned"
onClick={[Function]}
stat="5"

+ 1
- 0
server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx ファイルの表示

@@ -62,6 +62,7 @@ it('should display an error message', () => {
it('should open the license form', () => {
const wrapper = getWrapper({ editions: DEFAULT_EDITIONS });
(wrapper.instance() as EditionBoxes).handleOpenLicenseForm(DEFAULT_EDITIONS[0]);
wrapper.update();
expect(wrapper.find('LicenseEditionForm').exists()).toBeTruthy();
});


+ 1
- 1
server/sonar-web/src/main/js/apps/marketplace/__tests__/PendingActions-test.tsx ファイルの表示

@@ -38,7 +38,7 @@ it('should display pending actions', () => {
});

it('should not display anything', () => {
expect(getWrapper({ pending: { installing: [], updating: [], removing: [] } })).toMatchSnapshot();
expect(getWrapper({ pending: { installing: [], updating: [], removing: [] } }).type()).toBeNull();
});

it('should open the restart form', () => {

+ 0
- 2
server/sonar-web/src/main/js/apps/marketplace/__tests__/__snapshots__/PendingActions-test.tsx.snap ファイルの表示

@@ -59,5 +59,3 @@ exports[`should display pending actions 1`] = `
</div>
</div>
`;

exports[`should not display anything 1`] = `null`;

+ 3
- 5
server/sonar-web/src/main/js/apps/marketplace/components/EditionBox.tsx ファイルの表示

@@ -43,11 +43,9 @@ export default class EditionBox extends React.PureComponent<Props> {
if (canInstall && !isInstalled) {
return (
<button disabled={installInProgress || uninstallInProgress} onClick={this.handleInstall}>
{this.props.isDowngrade ? (
translate('marketplace.downgrade')
) : (
translate('marketplace.upgrade')
)}
{this.props.isDowngrade
? translate('marketplace.downgrade')
: translate('marketplace.upgrade')}
</button>
);
}

+ 6
- 6
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/EditionBoxBadge-test.tsx ファイルの表示

@@ -31,7 +31,7 @@ const DEFAULT_STATUS: EditionStatus = {
it('should display installed badge', () => {
expect(
getWrapper({
editionStatus: {
status: {
currentEditionKey: 'foo',
nextEditionKey: '',
installationStatus: 'NONE'
@@ -43,7 +43,7 @@ it('should display installed badge', () => {
it('should display installing badge', () => {
expect(
getWrapper({
editionStatus: {
status: {
currentEditionKey: 'foo',
nextEditionKey: 'foo',
installationStatus: 'AUTOMATIC_IN_PROGRESS'
@@ -55,7 +55,7 @@ it('should display installing badge', () => {
it('should display pending badge', () => {
expect(
getWrapper({
editionStatus: {
status: {
currentEditionKey: '',
nextEditionKey: 'foo',
installationStatus: 'AUTOMATIC_READY'
@@ -67,13 +67,13 @@ it('should display pending badge', () => {
it('should not display a badge', () => {
expect(
getWrapper({
editionStatus: {
status: {
currentEditionKey: '',
nextEditionKey: 'bar',
installationStatus: 'AUTOMATIC_READY'
}
})
).toMatchSnapshot();
}).type()
).toBeNull();
});

function getWrapper(props = {}) {

+ 1
- 1
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionForm-test.tsx ファイルの表示

@@ -63,8 +63,8 @@ it('should update the edition status after install', async () => {
const updateEditionStatus = jest.fn();
const wrapper = getWrapper({ updateEditionStatus });
const form = wrapper.instance() as LicenseEditionForm;
form.mounted = true;
form.handleLicenseChange('mylicense', 'AUTOMATIC_INSTALL');
wrapper.update();
click(wrapper.find('button'));
expect(applyLicense).toHaveBeenCalledWith({ license: 'mylicense' });
await new Promise(setImmediate);

+ 10
- 2
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/LicenseEditionSet-test.tsx ファイルの表示

@@ -26,7 +26,8 @@ import LicenseEditionSet from '../LicenseEditionSet';
jest.mock('../../../../api/marketplace', () => ({
getLicensePreview: jest.fn(() =>
Promise.resolve({ nextEditionKey: 'foo', previewStatus: 'NO_INSTALL' })
)
),
getFormData: jest.fn(() => Promise.resolve({ serverId: 'foo', ncloc: 1000 }))
}));

jest.mock('lodash', () => {
@@ -56,6 +57,13 @@ it('should display correctly', () => {
expect(getWrapper()).toMatchSnapshot();
});

it('should display the get license link with parameters', async () => {
const wrapper = getWrapper();
await new Promise(setImmediate);
wrapper.update();
expect(wrapper.find('a')).toMatchSnapshot();
});

it('should correctly display status message after checking license', async () => {
await testLicenseStatus('NO_INSTALL');
await testLicenseStatus('AUTOMATIC_INSTALL');
@@ -79,10 +87,10 @@ async function testLicenseStatus(status: string) {
);
const updateLicense = jest.fn();
const wrapper = getWrapper({ updateLicense });
(wrapper.instance() as LicenseEditionSet).mounted = true;
change(wrapper.find('textarea'), 'mylicense');
expect(getLicensePreview).toHaveBeenCalled();
await new Promise(setImmediate);
expect(updateLicense).toHaveBeenCalled();
wrapper.update();
expect(wrapper.find('p.alert')).toMatchSnapshot();
}

+ 1
- 1
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginLicense-test.tsx ファイルの表示

@@ -26,5 +26,5 @@ it('should display the license field', () => {
});

it('should not display anything', () => {
expect(shallow(<PluginLicense />)).toMatchSnapshot();
expect(shallow(<PluginLicense />).type()).toBeNull();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/PluginUrls-test.tsx ファイルの表示

@@ -30,7 +30,7 @@ it('should display only one url', () => {
});

it('should not display anything', () => {
expect(getWrapper({ homepageUrl: undefined, issueTrackerUrl: undefined })).toMatchSnapshot();
expect(getWrapper({ homepageUrl: undefined, issueTrackerUrl: undefined }).type()).toBeNull();
});

function getWrapper(plugin = {}) {

+ 25
- 5
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/EditionBoxBadge-test.tsx.snap ファイルの表示

@@ -1,9 +1,29 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should display installed badge 1`] = `null`;
exports[`should display installed badge 1`] = `
<span
className="marketplace-edition-badge badge badge-normal-size"
>
<CheckIcon
className="little-spacer-right text-bottom"
size={14}
/>
marketplace.installed
</span>
`;

exports[`should display installing badge 1`] = `null`;
exports[`should display installing badge 1`] = `
<span
className="marketplace-edition-badge badge badge-normal-size"
>
marketplace.installing
</span>
`;

exports[`should display pending badge 1`] = `null`;

exports[`should not display a badge 1`] = `null`;
exports[`should display pending badge 1`] = `
<span
className="marketplace-edition-badge badge badge-normal-size"
>
marketplace.pending
</span>
`;

+ 10
- 0
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/LicenseEditionSet-test.tsx.snap ファイルの表示

@@ -75,3 +75,13 @@ exports[`should display correctly 1`] = `
</a>
</div>
`;

exports[`should display the get license link with parameters 1`] = `
<a
className="display-inline-block spacer-top"
href="license_url?serverId=foo&ncloc=1000"
target="_blank"
>
marketplace.i_need_a_license
</a>
`;

+ 0
- 2
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginLicense-test.tsx.snap ファイルの表示

@@ -24,5 +24,3 @@ exports[`should display the license field 1`] = `
</li>
</Tooltip>
`;

exports[`should not display anything 1`] = `null`;

+ 0
- 2
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginUrls-test.tsx.snap ファイルの表示

@@ -48,5 +48,3 @@ exports[`should display the urls 1`] = `
</ul>
</li>
`;

exports[`should not display anything 1`] = `null`;

+ 2
- 4
server/sonar-web/src/main/js/apps/organizations/components/OrganizationAdmin.js ファイルの表示

@@ -23,12 +23,12 @@ import { connect } from 'react-redux';
import { getOrganizationByKey } from '../../../store/rootReducer';
import handleRequiredAuthorization from '../../../app/utils/handleRequiredAuthorization';

class OrganizationAdmin extends React.PureComponent {
export class OrganizationAdmin extends React.PureComponent {
/*:: props: {
children?: React.Element<*>,
organization: { canAdmin: boolean }
};
*/
*/

componentDidMount() {
this.checkPermissions();
@@ -62,5 +62,3 @@ const mapStateToProps = (state, ownProps) => ({
});

export default connect(mapStateToProps)(OrganizationAdmin);

export const UnconnectedOrganizationAdmin = OrganizationAdmin;

+ 1
- 3
server/sonar-web/src/main/js/apps/organizations/components/OrganizationPage.js ファイルの表示

@@ -49,7 +49,7 @@ type State = {
};
*/

class OrganizationPage extends React.PureComponent {
export class OrganizationPage extends React.PureComponent {
/*:: mounted: boolean; */
/*:: props: Props; */
state /*: State */ = { loading: true };
@@ -108,5 +108,3 @@ const mapStateToProps = (state, ownProps /*: OwnProps */) => ({
const mapDispatchToProps = { fetchOrganization };

export default connect(mapStateToProps, mapDispatchToProps)(OrganizationPage);

export const UnconnectedOrganizationPage = OrganizationPage;

+ 9
- 7
server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationAdmin-test.js ファイルの表示

@@ -19,15 +19,17 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { UnconnectedOrganizationAdmin } from '../OrganizationAdmin';
import { OrganizationAdmin } from '../OrganizationAdmin';

jest.mock('../../../../app/utils/handleRequiredAuthorization', () => jest.fn());

it('should render children', () => {
const organization = { canAdmin: true };
expect(
shallow(
<UnconnectedOrganizationAdmin organization={organization}>
<OrganizationAdmin organization={organization}>
<div>hello</div>
</UnconnectedOrganizationAdmin>
</OrganizationAdmin>
)
).toMatchSnapshot();
});
@@ -36,9 +38,9 @@ it('should not render anything', () => {
const organization = { canAdmin: false };
expect(
shallow(
<UnconnectedOrganizationAdmin organization={organization}>
<OrganizationAdmin organization={organization}>
<div>hello</div>
</UnconnectedOrganizationAdmin>
)
).toMatchSnapshot();
</OrganizationAdmin>
).type()
).toBeNull();
});

+ 2
- 0
server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationMembers-test.js ファイルの表示

@@ -35,6 +35,7 @@ it('should not render actions for non admin', () => {
members={members}
status={status}
fetchOrganizationMembers={jest.fn()}
fetchOrganizationGroups={jest.fn()}
fetchMoreOrganizationMembers={jest.fn()}
/>
);
@@ -48,6 +49,7 @@ it('should render actions for admin', () => {
members={members}
status={{ ...status, loading: true }}
fetchOrganizationMembers={jest.fn()}
fetchOrganizationGroups={jest.fn()}
fetchMoreOrganizationMembers={jest.fn()}
/>
);

+ 11
- 10
server/sonar-web/src/main/js/apps/organizations/components/__tests__/OrganizationPage-test.js ファイルの表示

@@ -19,15 +19,17 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { UnconnectedOrganizationPage } from '../OrganizationPage';
import { OrganizationPage } from '../OrganizationPage';

const fetchOrganization = () => Promise.resolve();

it('smoke test', () => {
const wrapper = shallow(
<UnconnectedOrganizationPage params={{ organizationKey: 'foo' }}>
<OrganizationPage fetchOrganization={fetchOrganization} params={{ organizationKey: 'foo' }}>
<div>hello</div>
</UnconnectedOrganizationPage>
</OrganizationPage>
);
expect(wrapper).toMatchSnapshot();
expect(wrapper.type()).toBeNull();

const organization = { key: 'foo', name: 'Foo', isDefault: false, canAdmin: false };
wrapper.setProps({ organization });
@@ -36,9 +38,9 @@ it('smoke test', () => {

it('not found', () => {
const wrapper = shallow(
<UnconnectedOrganizationPage params={{ organizationKey: 'foo' }}>
<OrganizationPage fetchOrganization={fetchOrganization} params={{ organizationKey: 'foo' }}>
<div>hello</div>
</UnconnectedOrganizationPage>
</OrganizationPage>
);
wrapper.setState({ loading: false });
expect(wrapper).toMatchSnapshot();
@@ -47,12 +49,11 @@ it('not found', () => {
it('should correctly update when the organization changes', () => {
const fetchOrganization = jest.fn(() => Promise.resolve());
const wrapper = shallow(
<UnconnectedOrganizationPage
params={{ organizationKey: 'foo' }}
fetchOrganization={fetchOrganization}>
<OrganizationPage params={{ organizationKey: 'foo' }} fetchOrganization={fetchOrganization}>
<div>hello</div>
</UnconnectedOrganizationPage>
</OrganizationPage>
);
wrapper.setProps({ params: { organizationKey: 'bar' } });
expect(fetchOrganization).toHaveBeenCalledTimes(2);
expect(fetchOrganization.mock.calls).toMatchSnapshot();
});

+ 0
- 2
server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationAdmin-test.js.snap ファイルの表示

@@ -1,7 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should not render anything 1`] = `null`;

exports[`should render children 1`] = `
<div>
hello

+ 4
- 3
server/sonar-web/src/main/js/apps/organizations/components/__tests__/__snapshots__/OrganizationPage-test.js.snap ファイルの表示

@@ -4,15 +4,16 @@ exports[`not found 1`] = `<NotFound />`;

exports[`should correctly update when the organization changes 1`] = `
Array [
Array [
"foo",
],
Array [
"bar",
],
]
`;

exports[`smoke test 1`] = `null`;

exports[`smoke test 2`] = `
exports[`smoke test 1`] = `
<div>
<HelmetWrapper
defaultTitle="Foo"

+ 9
- 5
server/sonar-web/src/main/js/apps/organizations/components/forms/AddMemberForm.js ファイルの表示

@@ -77,10 +77,11 @@ export default class AddMemberForm extends React.PureComponent {
};

renderModal() {
const header = translate('users.add');
return (
<Modal contentLabel="modal form" onRequestClose={this.closeForm}>
<Modal key="add-member-modal" contentLabel={header} onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{translate('users.add')}</h2>
<h2>{header}</h2>
</header>
<form onSubmit={this.handleSubmit}>
<div className="modal-body">
@@ -111,11 +112,14 @@ export default class AddMemberForm extends React.PureComponent {
}

render() {
return (
<button onClick={this.openForm}>
const buttonComponent = (
<button key="add-member-button" onClick={this.openForm}>
{translate('organization.members.add')}
{this.state.open && this.renderModal()}
</button>
);
if (this.state.open) {
return [buttonComponent, this.renderModal()];
}
return buttonComponent;
}
}

+ 9
- 5
server/sonar-web/src/main/js/apps/organizations/components/forms/ManageMemberGroupsForm.js ファイルの表示

@@ -105,10 +105,11 @@ export default class ManageMemberGroupsForm extends React.PureComponent {
};

renderModal() {
const header = translate('organization.members.manage_groups');
return (
<Modal contentLabel="modal form" onRequestClose={this.closeForm}>
<Modal key="manage-member-modal" contentLabel={header} onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{translate('organization.members.manage_groups')}</h2>
<h2>{header}</h2>
</header>
<form onSubmit={this.handleSubmit}>
<div className="modal-body">
@@ -146,11 +147,14 @@ export default class ManageMemberGroupsForm extends React.PureComponent {
}

render() {
return (
<a onClick={this.openForm} href="#">
const buttonComponent = (
<a key="manage-member-button" onClick={this.openForm} href="#">
{translate('organization.members.manage_groups')}
{this.state.open && this.renderModal()}
</a>
);
if (this.state.open) {
return [buttonComponent, this.renderModal()];
}
return buttonComponent;
}
}

+ 9
- 5
server/sonar-web/src/main/js/apps/organizations/components/forms/RemoveMemberForm.js ファイルの表示

@@ -61,10 +61,11 @@ export default class RemoveMemberForm extends React.PureComponent {
};

renderModal() {
const header = translate('users.remove');
return (
<Modal contentLabel="modal form" onRequestClose={this.closeForm}>
<Modal key="remove-member-modal" contentLabel={header} onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{translate('users.remove')}</h2>
<h2>{header}</h2>
</header>
<form onSubmit={this.handleSubmit}>
<div className="modal-body markdown">
@@ -92,11 +93,14 @@ export default class RemoveMemberForm extends React.PureComponent {
}

render() {
return (
<a onClick={this.openForm} href="#">
const buttonComponent = (
<a key="remove-member-button" onClick={this.openForm} href="#">
{translate('organization.members.remove')}
{this.state.open && this.renderModal()}
</a>
);
if (this.state.open) {
return [buttonComponent, this.renderModal()];
}
return buttonComponent;
}
}

+ 7
- 3
server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/AddMemberForm-test.js ファイルの表示

@@ -18,21 +18,25 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
import { shallow, mount } from 'enzyme';
import { shallow } from 'enzyme';
import { click } from '../../../../../helpers/testUtils';
import AddMemberForm from '../AddMemberForm';

jest.mock('react-dom');

const memberLogins = ['admin'];

it('should render and open the modal', () => {
const wrapper = shallow(<AddMemberForm memberLogins={memberLogins} addMember={jest.fn()} />);
expect(wrapper).toMatchSnapshot();
wrapper.setState({ open: true });
expect(wrapper).toMatchSnapshot();

// FIXME Can probably be removed when https://github.com/airbnb/enzyme/issues/1149 is resolved
expect(wrapper.first().getElements()).toMatchSnapshot();
});

it('should correctly handle user interactions', () => {
const wrapper = mount(<AddMemberForm memberLogins={memberLogins} addMember={jest.fn()} />);
const wrapper = shallow(<AddMemberForm memberLogins={memberLogins} addMember={jest.fn()} />);
click(wrapper.find('button'));
expect(wrapper.state('open')).toBeTruthy();
wrapper.instance().closeForm();

+ 7
- 5
server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/ManageMemberGroupsForm-test.js ファイルの表示

@@ -18,10 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
import { shallow, mount } from 'enzyme';
import { shallow } from 'enzyme';
import { click, mockEvent } from '../../../../../helpers/testUtils';
import ManageMemberGroupsForm from '../ManageMemberGroupsForm';

jest.mock('react-dom');

const member = { login: 'admin', name: 'Admin Istrator', avatar: '', groupCount: 3 };
const organization = { name: 'MyOrg', key: 'myorg' };
const organizationGroups = [
@@ -48,8 +50,8 @@ const userGroups = {
11: { id: 11, name: 'pull-request-analysers', description: 'Technical accounts', selected: true }
};

const getMountedForm = function(updateFunc = jest.fn()) {
const wrapper = mount(
function getMountedForm(updateFunc = jest.fn()) {
const wrapper = shallow(
<ManageMemberGroupsForm
member={member}
organization={organization}
@@ -62,7 +64,7 @@ const getMountedForm = function(updateFunc = jest.fn()) {
instance.setState({ loading: false, userGroups });
});
return { wrapper, instance };
};
}

it('should render and open the modal', () => {
const wrapper = shallow(
@@ -75,7 +77,7 @@ it('should render and open the modal', () => {
);
expect(wrapper).toMatchSnapshot();
wrapper.setState({ open: true });
expect(wrapper).toMatchSnapshot();
expect(wrapper.first().getElements()).toMatchSnapshot();
});

it('should correctly handle user interactions', () => {

+ 5
- 3
server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/RemoveMemberForm-test.js ファイルの表示

@@ -18,10 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import React from 'react';
import { shallow, mount } from 'enzyme';
import { shallow } from 'enzyme';
import { click, mockEvent } from '../../../../../helpers/testUtils';
import RemoveMemberForm from '../RemoveMemberForm';

jest.mock('react-dom');

const member = { login: 'admin', name: 'Admin Istrator', avatar: '', groupCount: 3 };
const organization = { name: 'MyOrg' };

@@ -31,12 +33,12 @@ it('should render and open the modal', () => {
);
expect(wrapper).toMatchSnapshot();
wrapper.setState({ open: true });
expect(wrapper).toMatchSnapshot();
expect(wrapper.first().getElements()).toMatchSnapshot();
});

it('should correctly handle user interactions', () => {
const removeMember = jest.fn();
const wrapper = mount(
const wrapper = shallow(
<RemoveMemberForm member={member} removeMember={removeMember} organization={organization} />
);
const instance = wrapper.instance();

+ 11
- 7
server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/AddMemberForm-test.js.snap ファイルの表示

@@ -2,6 +2,7 @@

exports[`should render and open the modal 1`] = `
<button
key="add-member-button"
onClick={[Function]}
>
organization.members.add
@@ -9,12 +10,14 @@ exports[`should render and open the modal 1`] = `
`;

exports[`should render and open the modal 2`] = `
<button
onClick={[Function]}
>
organization.members.add
Array [
<button
onClick={[Function]}
>
organization.members.add
</button>,
<Modal
contentLabel="modal form"
contentLabel="users.add"
onRequestClose={[Function]}
>
<header
@@ -45,6 +48,7 @@ exports[`should render and open the modal 2`] = `
}
handleValueChange={[Function]}
searchUsers={[Function]}
selectedUser={undefined}
/>
</div>
</div>
@@ -68,6 +72,6 @@ exports[`should render and open the modal 2`] = `
</div>
</footer>
</form>
</Modal>
</button>
</Modal>,
]
`;

+ 11
- 11
server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/ManageMemberGroupsForm-test.js.snap ファイルの表示

@@ -71,6 +71,7 @@ Object {
exports[`should render and open the modal 1`] = `
<a
href="#"
key="manage-member-button"
onClick={[Function]}
>
organization.members.manage_groups
@@ -78,13 +79,15 @@ exports[`should render and open the modal 1`] = `
`;

exports[`should render and open the modal 2`] = `
<a
href="#"
onClick={[Function]}
>
organization.members.manage_groups
Array [
<a
href="#"
onClick={[Function]}
>
organization.members.manage_groups
</a>,
<Modal
contentLabel="modal form"
contentLabel="organization.members.manage_groups"
onRequestClose={[Function]}
>
<header
@@ -117,7 +120,6 @@ exports[`should render and open the modal 2`] = `
"name": "professionals",
}
}
key="7"
onCheck={[Function]}
/>
<OrganizationGroupCheckbox
@@ -130,7 +132,6 @@ exports[`should render and open the modal 2`] = `
"name": "pull-request-analysers",
}
}
key="11"
onCheck={[Function]}
/>
<OrganizationGroupCheckbox
@@ -143,7 +144,6 @@ exports[`should render and open the modal 2`] = `
"name": "sonar-administrators",
}
}
key="1"
onCheck={[Function]}
/>
</ul>
@@ -167,8 +167,8 @@ exports[`should render and open the modal 2`] = `
</div>
</footer>
</form>
</Modal>
</a>
</Modal>,
]
`;

exports[`should reset the selected groups when the modal is opened 1`] = `

+ 11
- 8
server/sonar-web/src/main/js/apps/organizations/components/forms/__tests__/__snapshots__/RemoveMemberForm-test.js.snap ファイルの表示

@@ -16,6 +16,7 @@ Array [
exports[`should render and open the modal 1`] = `
<a
href="#"
key="remove-member-button"
onClick={[Function]}
>
organization.members.remove
@@ -23,13 +24,15 @@ exports[`should render and open the modal 1`] = `
`;

exports[`should render and open the modal 2`] = `
<a
href="#"
onClick={[Function]}
>
organization.members.remove
Array [
<a
href="#"
onClick={[Function]}
>
organization.members.remove
</a>,
<Modal
contentLabel="modal form"
contentLabel="users.remove"
onRequestClose={[Function]}
>
<header
@@ -70,6 +73,6 @@ exports[`should render and open the modal 2`] = `
</div>
</footer>
</form>
</Modal>
</a>
</Modal>,
]
`;

+ 4
- 4
server/sonar-web/src/main/js/apps/organizations/routes.js ファイルの表示

@@ -17,13 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import OrganizationPage from './components/OrganizationPage';
import OrganizationPageContainer from './components/OrganizationPage';
import OrganizationPageExtension from '../../app/components/extensions/OrganizationPageExtension';
import OrganizationContainer from './components/OrganizationContainer';
import OrganizationProjects from './components/OrganizationProjects';
import OrganizationFavoriteProjects from './components/OrganizationFavoriteProjects';
import OrganizationRules from './components/OrganizationRules';
import OrganizationAdmin from './components/OrganizationAdmin';
import OrganizationAdminContainer from './components/OrganizationAdmin';
import OrganizationEdit from './components/OrganizationEdit';
import OrganizationGroups from './components/OrganizationGroups';
import OrganizationMembersContainer from './components/OrganizationMembersContainer';
@@ -38,7 +38,7 @@ import issuesRoutes from '../issues/routes';
const routes = [
{
path: ':organizationKey',
component: OrganizationPage,
component: OrganizationPageContainer,
childRoutes: [
{
indexRoute: {
@@ -90,7 +90,7 @@ const routes = [
component: OrganizationPageExtension
},
{
component: OrganizationAdmin,
component: OrganizationAdminContainer,
childRoutes: [
{ path: 'delete', component: OrganizationDelete },
{ path: 'edit', component: OrganizationEdit },

+ 2
- 6
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.js.snap ファイルの表示

@@ -25,18 +25,14 @@ exports[`renders 2`] = `
<ul
className="text-left"
>
<li
key="foo"
>
<li>
Foo
:
<DateTooltipFormatter
date="2017-01-01T11:39:03+0100"
/>
</li>
<li
key="bar"
>
<li>
Bar
:
<DateTooltipFormatter

+ 1
- 1
server/sonar-web/src/main/js/apps/overview/meta/MetaLink.js ファイルの表示

@@ -74,7 +74,7 @@ export default class MetaLink extends React.PureComponent {
className="link-with-icon"
href={link.url}
target="_blank"
onClick={!isClickable(link) && this.handleClick}>
onClick={!isClickable(link) ? this.handleClick : undefined}>
{this.renderLinkIcon(link)}
&nbsp;
{link.name}

+ 9
- 3
server/sonar-web/src/main/js/apps/overview/meta/__tests__/MetaTags-test.js ファイルの表示

@@ -39,15 +39,21 @@ const componentWithTags = {
};

it('should render without tags and admin rights', () => {
expect(shallow(<MetaTags component={component} />)).toMatchSnapshot();
expect(
shallow(<MetaTags component={component} />, { disableLifecycleMethods: true })
).toMatchSnapshot();
});

it('should render with tags and admin rights', () => {
expect(shallow(<MetaTags component={componentWithTags} />)).toMatchSnapshot();
expect(
shallow(<MetaTags component={componentWithTags} />, { disableLifecycleMethods: true })
).toMatchSnapshot();
});

it('should open the tag selector on click', () => {
const wrapper = shallow(<MetaTags component={componentWithTags} />);
const wrapper = shallow(<MetaTags component={componentWithTags} />, {
disableLifecycleMethods: true
});
expect(wrapper).toMatchSnapshot();

// open

+ 0
- 1
server/sonar-web/src/main/js/apps/overview/meta/__tests__/__snapshots__/MetaLink-test.js.snap ファイルの表示

@@ -67,7 +67,6 @@ exports[`should match snapshot 1`] = `
<a
className="link-with-icon"
href="http://example.com"
onClick={false}
target="_blank"
>
<i

+ 2
- 2
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/GraphsTooltipsContentDuplication-test.js ファイルの表示

@@ -48,6 +48,6 @@ it('should render correctly', () => {

it('should render null when data is missing', () => {
expect(
shallow(<GraphsTooltipsContentDuplication {...DEFAULT_PROPS} tooltipIdx={0} />)
).toMatchSnapshot();
shallow(<GraphsTooltipsContentDuplication {...DEFAULT_PROPS} tooltipIdx={0} />).type()
).toBeNull();
});

+ 0
- 2
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/GraphsTooltipsContentDuplication-test.js.snap ファイルの表示

@@ -25,5 +25,3 @@ exports[`should render correctly 1`] = `
</tr>
</tbody>
`;

exports[`should render null when data is missing 1`] = `null`;

+ 9
- 6
server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddEventForm.js ファイルの表示

@@ -97,10 +97,11 @@ export default class AddEventForm extends React.PureComponent {
};

renderModal() {
const header = translate(this.props.addEventButtonText);
return (
<Modal contentLabel="modal form" onRequestClose={this.closeForm}>
<Modal key="add-event-modal" contentLabel={header} onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{translate(this.props.addEventButtonText)}</h2>
<h2>{header}</h2>
</header>

<form onSubmit={this.handleSubmit}>
@@ -111,7 +112,6 @@ export default class AddEventForm extends React.PureComponent {
value={this.state.name}
autoFocus={true}
disabled={this.state.processing}
className="input-medium"
type="text"
onChange={this.changeInput}
/>
@@ -136,11 +136,14 @@ export default class AddEventForm extends React.PureComponent {
}

render() {
return (
<a className="js-add-event" href="#" onClick={this.openForm}>
const linkComponent = (
<a key="add-event-link" className="js-add-event" href="#" onClick={this.openForm}>
{translate(this.props.addEventButtonText)}
{this.state.open && this.renderModal()}
</a>
);
if (this.state.open) {
return [linkComponent, this.renderModal()];
}
return linkComponent;
}
}

+ 11
- 5
server/sonar-web/src/main/js/apps/projectActivity/components/forms/AddGraphMetric.js ファイルの表示

@@ -102,10 +102,11 @@ export default class AddGraphMetric extends React.PureComponent {

renderModal() {
const { metricsTypeFilter } = this.props;
const header = translate('project_activity.graphs.custom.add_metric');
return (
<Modal contentLabel="graph metric add" onRequestClose={this.closeForm}>
<Modal key="add-metric-modal" contentLabel={header} onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{translate('project_activity.graphs.custom.add_metric')}</h2>
<h2>{header}</h2>
</header>
<form onSubmit={this.handleSubmit}>
<div className="modal-body">
@@ -165,11 +166,16 @@ export default class AddGraphMetric extends React.PureComponent {
);
}

return (
<button className={this.props.className} onClick={this.openForm}>
const buttonComponent = (
<button key="add-metric-button" className={this.props.className} onClick={this.openForm}>
{translate('project_activity.graphs.custom.add')}
{this.state.open && this.renderModal()}
</button>
);

if (this.state.open) {
return [buttonComponent, this.renderModal()];
}

return buttonComponent;
}
}

+ 3
- 3
server/sonar-web/src/main/js/apps/projectActivity/components/forms/ChangeEventForm.js ファイルの表示

@@ -95,10 +95,11 @@ export default class ChangeEventForm extends React.PureComponent {
};

render() {
const header = translate(this.props.changeEventButtonText);
return (
<Modal contentLabel="modal form" onRequestClose={this.closeForm}>
<Modal contentLabel={header} onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{translate(this.props.changeEventButtonText)}</h2>
<h2>{header}</h2>
</header>

<form onSubmit={this.handleSubmit}>
@@ -109,7 +110,6 @@ export default class ChangeEventForm extends React.PureComponent {
value={this.state.name}
autoFocus={true}
disabled={this.state.processing}
className="input-medium"
type="text"
onChange={this.changeInput}
/>

+ 9
- 5
server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveAnalysisForm.js ファイルの表示

@@ -88,10 +88,11 @@ export default class RemoveAnalysisForm extends React.PureComponent {
};

renderModal() {
const header = translate('project_activity.delete_analysis');
return (
<Modal contentLabel="modal form" onRequestClose={this.closeForm}>
<Modal key="delete-analysis-modal" contentLabel={header} onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{translate('project_activity.delete_analysis')}</h2>
<h2>{header}</h2>
</header>

<form onSubmit={this.handleSubmit}>
@@ -117,11 +118,14 @@ export default class RemoveAnalysisForm extends React.PureComponent {
}

render() {
return (
<a className="js-delete-analysis" href="#" onClick={this.openForm}>
const linkComponent = (
<a key="delete-analysis-link" className="js-delete-analysis" href="#" onClick={this.openForm}>
{translate('project_activity.delete_analysis')}
{this.state.open && this.renderModal()}
</a>
);
if (this.state.open) {
return [linkComponent, this.renderModal()];
}
return linkComponent;
}
}

+ 3
- 2
server/sonar-web/src/main/js/apps/projectActivity/components/forms/RemoveEventForm.js ファイルの表示

@@ -81,10 +81,11 @@ export default class RemoveEventForm extends React.PureComponent {
};

render() {
const header = translate(this.props.removeEventButtonText);
return (
<Modal contentLabel="modal form" onRequestClose={this.closeForm}>
<Modal contentLabel={header} onRequestClose={this.closeForm}>
<header className="modal-head">
<h2>{translate(this.props.removeEventButtonText)}</h2>
<h2>{header}</h2>
</header>

<form onSubmit={this.handleSubmit}>

+ 11
- 11
server/sonar-web/src/main/js/apps/projectBranches/components/__tests__/LongBranchesPattern-test.tsx ファイルの表示

@@ -17,16 +17,16 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first, import/order */
jest.mock('../../../../api/settings', () => ({
getValues: jest.fn(() => Promise.resolve([]))
}));

/* eslint-disable import/order */
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import { shallow } from 'enzyme';
import LongBranchesPattern from '../LongBranchesPattern';
import { click } from '../../../../helpers/testUtils';

jest.mock('../../../../api/settings', () => ({
getValues: jest.fn(() => Promise.resolve([]))
}));

const getValues = require('../../../../api/settings').getValues as jest.Mock<any>;

beforeEach(() => {
@@ -41,25 +41,25 @@ it('renders', () => {

it('opens form', () => {
const wrapper = shallow(<LongBranchesPattern project="project" />);
(wrapper.instance() as LongBranchesPattern).mounted = true;
wrapper.setState({ loading: false, setting: { value: 'release-.*' } });

click(wrapper.find('a'));
expect(wrapper.find('LongBranchesPatternForm').exists()).toBeTruthy();

wrapper.find('LongBranchesPatternForm').prop<Function>('onClose')();
wrapper.update();
expect(wrapper.find('LongBranchesPatternForm').exists()).toBeFalsy();
});

it('fetches setting value on mount', () => {
mount(<LongBranchesPattern project="project" />);
shallow(<LongBranchesPattern project="project" />);
expect(getValues).lastCalledWith('sonar.branch.longLivedBranches.regex', 'project');
});

it('fetches new setting value after change', () => {
const wrapper = mount(<LongBranchesPattern project="project" />);
expect(getValues.mock.calls).toHaveLength(1);
const wrapper = shallow(<LongBranchesPattern project="project" />);
expect(getValues).toHaveBeenCalledTimes(1);

(wrapper.instance() as LongBranchesPattern).handleChange();
expect(getValues.mock.calls).toHaveLength(2);
expect(getValues).toHaveBeenCalledTimes(2);
});

+ 6
- 6
server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx ファイルの表示

@@ -17,7 +17,12 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first, import/order */
/* eslint-disable import/order */
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import AllProjects from '../AllProjects';
import { getView, saveSort, saveView, saveVisualization } from '../../../../helpers/storage';

jest.mock('../ProjectsList', () => ({
// eslint-disable-next-line
default: function ProjectsList() {
@@ -54,11 +59,6 @@ jest.mock('../../../../helpers/storage', () => ({
saveVisualization: jest.fn()
}));

import * as React from 'react';
import { mount, shallow } from 'enzyme';
import AllProjects from '../AllProjects';
import { getView, saveSort, saveView, saveVisualization } from '../../../../helpers/storage';

const fetchProjects = require('../../utils').fetchProjects as jest.Mock<any>;

beforeEach(() => {

+ 4
- 2
server/sonar-web/src/main/js/apps/projects/components/__tests__/FavoriteFilter-test.tsx ファイルの表示

@@ -69,6 +69,8 @@ it('does not save last selection with organization', () => {

it('does not render for anonymous', () => {
expect(
shallow(<FavoriteFilter query={query} />, { context: { currentUser: { isLoggedIn: false } } })
).toMatchSnapshot();
shallow(<FavoriteFilter query={query} />, {
context: { currentUser: { isLoggedIn: false } }
}).type()
).toBeNull();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLanguages-test.tsx ファイルの表示

@@ -49,5 +49,5 @@ it('handles unknown languages', () => {
});

it('does not render', () => {
expect(shallow(<ProjectCardLanguages />, { context: { languages } })).toMatchSnapshot();
expect(shallow(<ProjectCardLanguages />, { context: { languages } }).type()).toBeNull();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardQualityGate-test.tsx ファイルの表示

@@ -26,5 +26,5 @@ it('renders', () => {
});

it('does not render', () => {
expect(shallow(<ProjectCardQualityGate />)).toMatchSnapshot();
expect(shallow(<ProjectCardQualityGate />).type()).toBeNull();
});

+ 69
- 3
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap ファイルの表示

@@ -28,7 +28,29 @@ exports[`renders 1`] = `
>
<PageSidebar
isFavorite={false}
query={Object {}}
query={
Object {
"coverage": undefined,
"duplications": undefined,
"gate": undefined,
"languages": undefined,
"maintainability": undefined,
"new_coverage": undefined,
"new_duplications": undefined,
"new_lines": undefined,
"new_maintainability": undefined,
"new_reliability": undefined,
"new_security": undefined,
"reliability": undefined,
"search": undefined,
"security": undefined,
"size": undefined,
"sort": undefined,
"tags": undefined,
"view": undefined,
"visualization": undefined,
}
}
view="overall"
visualization="risk"
/>
@@ -67,7 +89,29 @@ exports[`renders 1`] = `
},
]
}
query={Object {}}
query={
Object {
"coverage": undefined,
"duplications": undefined,
"gate": undefined,
"languages": undefined,
"maintainability": undefined,
"new_coverage": undefined,
"new_duplications": undefined,
"new_lines": undefined,
"new_maintainability": undefined,
"new_reliability": undefined,
"new_security": undefined,
"reliability": undefined,
"search": undefined,
"security": undefined,
"size": undefined,
"sort": undefined,
"tags": undefined,
"view": undefined,
"visualization": undefined,
}
}
selectedSort="name"
total={0}
view="overall"
@@ -92,7 +136,29 @@ exports[`renders 1`] = `
},
]
}
query={Object {}}
query={
Object {
"coverage": undefined,
"duplications": undefined,
"gate": undefined,
"languages": undefined,
"maintainability": undefined,
"new_coverage": undefined,
"new_duplications": undefined,
"new_lines": undefined,
"new_maintainability": undefined,
"new_reliability": undefined,
"new_security": undefined,
"reliability": undefined,
"search": undefined,
"security": undefined,
"size": undefined,
"sort": undefined,
"tags": undefined,
"view": undefined,
"visualization": undefined,
}
}
/>
<ListFooter
count={1}

+ 0
- 2
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/FavoriteFilter-test.tsx.snap ファイルの表示

@@ -1,7 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`does not render for anonymous 1`] = `null`;

exports[`handles organization 1`] = `
<header
className="page-header text-center"

+ 8
- 26
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap ファイルの表示

@@ -1,7 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`does not render 1`] = `null`;

exports[`handles unknown languages 1`] = `
<div
className="project-card-languages"
@@ -9,15 +7,11 @@ exports[`handles unknown languages 1`] = `
<Tooltip
overlay={
<span>
<span
key="cpp"
>
<span>
cpp
<br />
</span>
<span
key="Java"
>
<span>
Java
<br />
</span>
@@ -39,15 +33,11 @@ exports[`handles unknown languages 2`] = `
<Tooltip
overlay={
<span>
<span
key="unknown"
>
<span>
unknown
<br />
</span>
<span
key="Java"
>
<span>
Java
<br />
</span>
@@ -69,15 +59,11 @@ exports[`renders 1`] = `
<Tooltip
overlay={
<span>
<span
key="Java"
>
<span>
Java
<br />
</span>
<span
key="JavaScript"
>
<span>
JavaScript
<br />
</span>
@@ -99,15 +85,11 @@ exports[`sorts languages 1`] = `
<Tooltip
overlay={
<span>
<span
key="JavaScript"
>
<span>
JavaScript
<br />
</span>
<span
key="Java"
>
<span>
Java
<br />
</span>

+ 0
- 2
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap ファイルの表示

@@ -1,7 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`does not render 1`] = `null`;

exports[`renders 1`] = `
<div
className="project-card-measure project-card-quality-gate spacer-left"

+ 4
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap ファイルの表示

@@ -57,6 +57,7 @@ exports[`should render maximum 10 languages in the searchbox results 1`] = `
},
]
}
organization={undefined}
property="languages"
query={
Object {
@@ -130,6 +131,7 @@ exports[`should render the languages facet with the selected languages 1`] = `
},
]
}
organization={undefined}
property="languages"
query={
Object {
@@ -333,6 +335,7 @@ exports[`should render the languages without the ones in the facet 1`] = `
}
footer={
<SearchableFilterFooter
isFavorite={undefined}
options={
Array [
Object {
@@ -349,6 +352,7 @@ exports[`should render the languages without the ones in the facet 1`] = `
},
]
}
organization={undefined}
property="languages"
query={
Object {

+ 6
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap ファイルの表示

@@ -60,6 +60,7 @@ exports[`should render maximum 10 tags in the searchbox results 1`] = `
},
]
}
organization={undefined}
property="tags"
query={
Object {
@@ -121,6 +122,7 @@ exports[`should render the tags facet with the selected tags 1`] = `
onInputChange={[Function]}
onOpen={[Function]}
options={Array []}
organization={undefined}
property="tags"
query={
Object {
@@ -294,10 +296,12 @@ exports[`should render the tags without the ones in the facet 1`] = `
}
footer={
<SearchableFilterFooter
isFavorite={undefined}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
options={Array []}
organization={undefined}
property="tags"
query={
Object {
@@ -340,6 +344,7 @@ exports[`should render the tags without the ones in the facet 2`] = `
}
footer={
<SearchableFilterFooter
isFavorite={undefined}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
@@ -359,6 +364,7 @@ exports[`should render the tags without the ones in the facet 2`] = `
},
]
}
organization={undefined}
property="tags"
query={
Object {

+ 10
- 5
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/App-test.tsx ファイルの表示

@@ -17,7 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first, import/order */
/* eslint-disable import/order */
import * as React from 'react';
import { mount } from 'enzyme';
import App, { Props } from '../App';

jest.mock('react-dom');

jest.mock('lodash', () => {
const lodash = require.requireActual('lodash');
lodash.debounce = (fn: Function) => (...args: any[]) => fn(args);
@@ -34,10 +40,6 @@ jest.mock('rc-tooltip', () => ({

jest.mock('../../../api/components', () => ({ getComponents: jest.fn() }));

import * as React from 'react';
import { mount } from 'enzyme';
import App, { Props } from '../App';

const getComponents = require('../../../api/components').getComponents as jest.Mock<any>;

const organization = { key: 'org', name: 'org', projectVisibility: 'public' };
@@ -121,12 +123,15 @@ it('creates project', () => {
expect(wrapper.find('CreateProjectForm').exists()).toBeFalsy();

wrapper.find('Header').prop<Function>('onProjectCreate')();
wrapper.update();
expect(wrapper.find('CreateProjectForm').exists()).toBeTruthy();

wrapper.find('CreateProjectForm').prop<Function>('onProjectCreated')();
wrapper.update();
expect(getComponents.mock.calls).toHaveLength(2);

wrapper.find('CreateProjectForm').prop<Function>('onClose')();
wrapper.update();
expect(wrapper.find('CreateProjectForm').exists()).toBeFalsy();
});


+ 8
- 6
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/BulkApplyTemplateModal-test.tsx ファイルの表示

@@ -17,17 +17,19 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first, import/order */
jest.mock('../../../api/permissions', () => ({
bulkApplyTemplate: jest.fn(() => Promise.resolve()),
getPermissionTemplates: jest.fn(() => Promise.resolve({ permissionTemplates: [] }))
}));

/* eslint-disable import/order */
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import BulkApplyTemplateModal, { Props } from '../BulkApplyTemplateModal';
import { click } from '../../../helpers/testUtils';

jest.mock('react-dom');

jest.mock('../../../api/permissions', () => ({
bulkApplyTemplate: jest.fn(() => Promise.resolve()),
getPermissionTemplates: jest.fn(() => Promise.resolve({ permissionTemplates: [] }))
}));

const bulkApplyTemplate = require('../../../api/permissions').bulkApplyTemplate as jest.Mock<any>;
const getPermissionTemplates = require('../../../api/permissions')
.getPermissionTemplates as jest.Mock<any>;

+ 1
- 0
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Header-test.tsx ファイルの表示

@@ -48,6 +48,7 @@ it('changes default visibility', () => {
expect(onVisibilityChange).toBeCalledWith(Visibility.Private);

modalWrapper.prop<Function>('onClose')();
wrapper.update();
expect(wrapper.find('ChangeVisibilityForm').exists()).toBeFalsy();
});


+ 1
- 0
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/Search-test.tsx ファイルの表示

@@ -97,6 +97,7 @@ it('bulk applies permission template', () => {
click(wrapper.find('.js-bulk-apply-permission-template'));
expect(wrapper.find('BulkApplyTemplateModal')).toMatchSnapshot();
wrapper.find('BulkApplyTemplateModal').prop<Function>('onClose')();
wrapper.update();
expect(wrapper.find('BulkApplyTemplateModal').exists()).toBeFalsy();
});


+ 2
- 2
server/sonar-web/src/main/js/apps/quality-gates/components/Details.js ファイルの表示

@@ -37,8 +37,8 @@ export default class Details extends React.PureComponent {
this.fetchDetails();
}

componentDidUpdate(nextProps) {
if (nextProps.params.id !== this.props.params.id) {
componentDidUpdate(prevProps) {
if (prevProps.params.id !== this.props.params.id) {
this.fetchDetails();
}
}

+ 2
- 2
server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.tsx ファイルの表示

@@ -19,6 +19,7 @@
*/
import * as React from 'react';
import Helmet from 'react-helmet';
import { WithRouterProps } from 'react-router';
import ProfileNotFound from './ProfileNotFound';
import ProfileHeader from '../details/ProfileHeader';
import { Profile } from '../types';
@@ -32,11 +33,10 @@ interface Props {
onRequestFail: (reasong: any) => void;
organization: string | null;
profiles: Profile[];
router: { replace: (path: any) => void };
updateProfiles: () => Promise<void>;
}

export default class ProfileContainer extends React.PureComponent<Props> {
export default class ProfileContainer extends React.PureComponent<Props & WithRouterProps> {
componentDidMount() {
const { location, profiles, router } = this.props;
if (location.query.key) {

+ 10
- 7
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.tsx ファイルの表示

@@ -17,14 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { shallow } from 'enzyme';
import Helmet from 'react-helmet';
import { WithRouterProps } from 'react-router';
import ProfileContainer from '../ProfileContainer';
import ProfileNotFound from '../ProfileNotFound';
import ProfileHeader from '../../details/ProfileHeader';
import { createFakeProfile } from '../../utils';

const routerProps = { router: {} } as WithRouterProps;

it('should render ProfileHeader', () => {
const targetProfile = createFakeProfile({ language: 'js', name: 'fake' });
const profiles = [targetProfile, createFakeProfile({ language: 'js', name: 'another' })];
@@ -35,8 +38,8 @@ it('should render ProfileHeader', () => {
onRequestFail={jest.fn()}
organization={null}
profiles={profiles}
router={{} as any}
updateProfiles={updateProfiles}>
updateProfiles={updateProfiles}
{...routerProps}>
<div />
</ProfileContainer>
);
@@ -57,8 +60,8 @@ it('should render ProfileNotFound', () => {
onRequestFail={jest.fn()}
organization={null}
profiles={profiles}
router={{} as any}
updateProfiles={jest.fn()}>
updateProfiles={jest.fn()}
{...routerProps}>
<div />
</ProfileContainer>
);
@@ -74,8 +77,8 @@ it('should render Helmet', () => {
onRequestFail={jest.fn()}
organization={null}
profiles={profiles}
router={{} as any}
updateProfiles={updateProfiles}>
updateProfiles={updateProfiles}
{...routerProps}>
<div />
</ProfileContainer>
);

+ 14
- 9
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissions-test.tsx ファイルの表示

@@ -17,17 +17,17 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first, import/order */
jest.mock('../../../../api/quality-profiles', () => ({
searchUsers: jest.fn(() => Promise.resolve([])),
searchGroups: jest.fn(() => Promise.resolve([]))
}));

/* eslint-disable import/order */
import * as React from 'react';
import { mount, shallow } from 'enzyme';
import ProfilePermissions from '../ProfilePermissions';
import { click } from '../../../../helpers/testUtils';

jest.mock('../../../../api/quality-profiles', () => ({
searchUsers: jest.fn(() => Promise.resolve([])),
searchGroups: jest.fn(() => Promise.resolve([]))
}));

const searchUsers = require('../../../../api/quality-profiles').searchUsers as jest.Mock<any>;
const searchGroups = require('../../../../api/quality-profiles').searchGroups as jest.Mock<any>;

@@ -50,16 +50,21 @@ it('renders', () => {
expect(wrapper).toMatchSnapshot();
});

it('opens add users form', () => {
it('opens add users form', async () => {
searchUsers.mockImplementationOnce(() =>
Promise.resolve({ users: [{ login: 'luke', name: 'Luke Skywalker' }] })
);
const wrapper = shallow(<ProfilePermissions profile={profile} />);
(wrapper.instance() as ProfilePermissions).mounted = true;
wrapper.setState({ loading: false, users: [{ login: 'luke', name: 'Luke Skywalker' }] });
expect(searchUsers).toHaveBeenCalled();
await new Promise(setImmediate);
wrapper.update();
expect(wrapper.find('ProfilePermissionsForm').exists()).toBeFalsy();

click(wrapper.find('button'));
expect(wrapper.find('ProfilePermissionsForm').exists()).toBeTruthy();

wrapper.find('ProfilePermissionsForm').prop<Function>('onClose')();
wrapper.update();
expect(wrapper.find('ProfilePermissionsForm').exists()).toBeFalsy();
});


+ 7
- 6
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfilePermissionsFormSelect-test.tsx ファイルの表示

@@ -17,23 +17,22 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* eslint-disable import/first */
import * as React from 'react';
import { shallow } from 'enzyme';
import ProfilePermissionsFormSelect from '../ProfilePermissionsFormSelect';

jest.mock('lodash', () => {
const lodash = require.requireActual('lodash');
lodash.debounce = (fn: Function) => (...args: any[]) => fn(...args);
return lodash;
});

import * as React from 'react';
import { shallow } from 'enzyme';
import ProfilePermissionsFormSelect from '../ProfilePermissionsFormSelect';

it('renders', () => {
expect(
shallow(
<ProfilePermissionsFormSelect
onChange={jest.fn()}
onSearch={jest.fn()}
onSearch={jest.fn(() => Promise.resolve([]))}
selected={{ name: 'lambda' }}
/>
)
@@ -49,6 +48,8 @@ it('searches', () => {
selected={{ name: 'lambda' }}
/>
);
expect(onSearch).toBeCalledWith('');
onSearch.mockClear();

wrapper.prop<Function>('onInputChange')('f');
expect(onSearch).not.toBeCalled();

+ 15
- 25
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx ファイルの表示

@@ -20,7 +20,6 @@
import * as React from 'react';
import { shallow } from 'enzyme';
import ProfileRules from '../ProfileRules';
import { doAsync } from '../../../../helpers/testUtils';
import * as apiRules from '../../../../api/rules';
import * as apiQP from '../../../../api/quality-profiles';

@@ -82,16 +81,15 @@ const apiResponseActive = {
}
});

it('should render the quality profiles rules with sonarway comparison', () => {
it('should render the quality profiles rules with sonarway comparison', async () => {
const wrapper = shallow(<ProfileRules organization="foo" profile={PROFILE} />);
const instance = wrapper.instance() as any;
instance.mounted = true;
instance.loadRules();
return doAsync(() => {
wrapper.update();
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(1);
expect(wrapper).toMatchSnapshot();
});
await new Promise(setImmediate);
wrapper.update();
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(1);
expect(wrapper).toMatchSnapshot();
});

it('should show a button to activate more rules for admins', () => {
@@ -116,22 +114,18 @@ it('should not show a button to activate more rules on built in profiles', () =>
expect(wrapper.find('.js-activate-rules')).toHaveLength(0);
});

it('should not show sonarway comparison for built in profiles', () => {
it('should not show sonarway comparison for built in profiles', async () => {
(apiQP as any).getQualityProfile = jest.fn(() => Promise.resolve());
const wrapper = shallow(
<ProfileRules organization={null} profile={{ ...PROFILE, isBuiltIn: true }} />
);
const instance = wrapper.instance() as any;
instance.mounted = true;
instance.loadRules();
return doAsync(() => {
wrapper.update();
expect(apiQP.getQualityProfile).toHaveBeenCalledTimes(0);
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0);
});
await new Promise(setImmediate);
wrapper.update();
expect(apiQP.getQualityProfile).toHaveBeenCalledTimes(0);
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0);
});

it('should not show sonarway comparison if there is no missing rules', () => {
it('should not show sonarway comparison if there is no missing rules', async () => {
(apiQP as any).getQualityProfile = jest.fn(() =>
Promise.resolve({
compareToSonarWay: {
@@ -142,12 +136,8 @@ it('should not show sonarway comparison if there is no missing rules', () => {
})
);
const wrapper = shallow(<ProfileRules organization={null} profile={PROFILE} />);
const instance = wrapper.instance() as any;
instance.mounted = true;
instance.loadRules();
return doAsync(() => {
wrapper.update();
expect(apiQP.getQualityProfile).toHaveBeenCalledTimes(1);
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0);
});
await new Promise(setImmediate);
wrapper.update();
expect(apiQP.getQualityProfile).toHaveBeenCalledTimes(1);
expect(wrapper.find('ProfileRulesSonarWayComparison')).toHaveLength(0);
});

+ 1
- 1
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfilePermissionsFormSelect-test.tsx.snap ファイルの表示

@@ -6,7 +6,7 @@ exports[`renders 1`] = `
className="Select-big"
clearable={false}
filterOptions={[Function]}
isLoading={false}
isLoading={true}
noResultsText="no_results"
onChange={[Function]}
onInputChange={[Function]}

+ 1
- 1
server/sonar-web/src/main/js/apps/quality-profiles/routes.ts ファイルの表示

@@ -17,7 +17,7 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { withRouter, RouterState, IndexRouteProps, RouteComponent } from 'react-router';
import { RouterState, IndexRouteProps, RouteComponent, withRouter } from 'react-router';

const routes = [
{

+ 1
- 0
server/sonar-web/src/main/js/apps/system/components/info-items/__tests__/__snapshots__/SysInfoItem-test.tsx.snap ファイルの表示

@@ -129,6 +129,7 @@ Array [
>
<CheckIcon>
<svg
className={undefined}
height={16}
viewBox="0 0 16 16"
width={16}

+ 5
- 18
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/SystemUpgradeNotif-test.tsx ファイルの表示

@@ -81,29 +81,20 @@ beforeEach(() => {

it('should display correctly', async () => {
const wrapper = shallow(<SystemUpgradeNotif />);
expect(wrapper).toMatchSnapshot();

const instance = wrapper.instance() as SystemUpgradeNotif;
instance.mounted = true;
instance.fetchSystemUpgrade();

expect(wrapper.type()).toBeNull();
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});

it('should display nothing', async () => {
getSystemUpgrades.mockImplementationOnce(() =>
Promise.resolve({ updateCenterRefresh: '', upgrades: [] })
);
getSystemUpgrades.mockImplementationOnce(() => {
return Promise.resolve({ updateCenterRefresh: '', upgrades: [] });
});
const wrapper = shallow(<SystemUpgradeNotif />);
const instance = wrapper.instance() as SystemUpgradeNotif;
instance.mounted = true;
instance.fetchSystemUpgrade();

await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(wrapper.type()).toBeNull();
});

it('should fetch upgrade when mounting', () => {
@@ -113,12 +104,8 @@ it('should fetch upgrade when mounting', () => {

it('should open the upgrade form', async () => {
const wrapper = shallow(<SystemUpgradeNotif />);
const instance = wrapper.instance() as SystemUpgradeNotif;
instance.mounted = true;
instance.fetchSystemUpgrade();
await new Promise(setImmediate);
wrapper.update();

click(wrapper.find('button'));
expect(wrapper.find('SystemUpgradeForm').exists()).toBeTruthy();
});

+ 1
- 5
server/sonar-web/src/main/js/apps/system/components/system-upgrade/__tests__/__snapshots__/SystemUpgradeNotif-test.tsx.snap ファイルの表示

@@ -1,8 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should display correctly 1`] = `null`;

exports[`should display correctly 2`] = `
exports[`should display correctly 1`] = `
<div
className="page-notifs"
>
@@ -19,5 +17,3 @@ exports[`should display correctly 2`] = `
</div>
</div>
`;

exports[`should display nothing 1`] = `null`;

+ 11
- 11
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewOrganizationForm-test.js ファイルの表示

@@ -21,7 +21,7 @@
import React from 'react';
import { mount } from 'enzyme';
import NewOrganizationForm from '../NewOrganizationForm';
import { change, doAsync, submit } from '../../../../helpers/testUtils';
import { change, submit } from '../../../../helpers/testUtils';

jest.mock('../../../../api/organizations', () => ({
createOrganization: () => Promise.resolve(),
@@ -29,28 +29,28 @@ jest.mock('../../../../api/organizations', () => ({
getOrganization: () => Promise.resolve(null)
}));

it('creates new organization', () => {
it('creates new organization', async () => {
const onDone = jest.fn();
const wrapper = mount(<NewOrganizationForm onDelete={jest.fn()} onDone={onDone} />);
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'foo');
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});

it('deletes organization', () => {
it('deletes organization', async () => {
const onDelete = jest.fn();
const wrapper = mount(<NewOrganizationForm onDelete={onDelete} onDone={jest.fn()} />);
wrapper.setState({ done: true, loading: false, organization: 'foo' });
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});

+ 11
- 11
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/NewProjectForm-test.js ファイルの表示

@@ -21,35 +21,35 @@
import React from 'react';
import { mount } from 'enzyme';
import NewProjectForm from '../NewProjectForm';
import { change, doAsync, submit } from '../../../../helpers/testUtils';
import { change, submit } from '../../../../helpers/testUtils';

jest.mock('../../../../api/components', () => ({
createProject: () => Promise.resolve(),
deleteProject: () => Promise.resolve()
}));

it('creates new project', () => {
it('creates new project', async () => {
const onDone = jest.fn();
const wrapper = mount(<NewProjectForm onDelete={jest.fn()} onDone={onDone} />);
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'foo');
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});

it('deletes project', () => {
it('deletes project', async () => {
const onDelete = jest.fn();
const wrapper = mount(<NewProjectForm onDelete={onDelete} onDone={jest.fn()} />);
wrapper.setState({ done: true, loading: false, projectKey: 'foo' });
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});

+ 27
- 20
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/OrganizationStep-test.js ファイルの表示

@@ -21,14 +21,19 @@
import React from 'react';
import { mount } from 'enzyme';
import OrganizationStep from '../OrganizationStep';
import { click, doAsync } from '../../../../helpers/testUtils';
import { click } from '../../../../helpers/testUtils';
import { getMyOrganizations } from '../../../../api/organizations';

jest.mock('../../../../api/organizations', () => ({
getMyOrganizations: () => Promise.resolve(['user', 'another'])
getMyOrganizations: jest.fn(() => Promise.resolve(['user', 'another']))
}));

const currentUser = { isLoggedIn: true, login: 'user' };

beforeEach(() => {
getMyOrganizations.mockClear();
});

// FIXME
// - if `mount` is used, then it's not possible to correctly set the state,
// because the mocked api call is used
@@ -49,7 +54,7 @@ it.skip('works with personal organization', () => {
expect(onContinue).toBeCalledWith('user');
});

it('works with existing organization', () => {
it('works with existing organization', async () => {
const onContinue = jest.fn();
const wrapper = mount(
<OrganizationStep
@@ -61,19 +66,20 @@ it('works with existing organization', () => {
stepNumber={1}
/>
);
return doAsync(() => {
click(wrapper.find('.js-existing'));
expect(wrapper).toMatchSnapshot();
wrapper
.find('Select')
.first()
.prop('onChange')({ value: 'another' });
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('another');
});
await new Promise(setImmediate);
wrapper.update();
click(wrapper.find('.js-existing'));
expect(wrapper).toMatchSnapshot();
wrapper
.find('Select')
.first()
.prop('onChange')({ value: 'another' });
wrapper.update();
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('another');
});

it('works with new organization', () => {
it('works with new organization', async () => {
const onContinue = jest.fn();
const wrapper = mount(
<OrganizationStep
@@ -85,10 +91,11 @@ it('works with new organization', () => {
stepNumber={1}
/>
);
return doAsync(() => {
click(wrapper.find('.js-new'));
wrapper.find('NewOrganizationForm').prop('onDone')('new');
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('new');
});
await new Promise(setImmediate);
wrapper.update();
click(wrapper.find('.js-new'));
wrapper.find('NewOrganizationForm').prop('onDone')('new');
wrapper.update();
click(wrapper.find('.js-continue'));
expect(onContinue).toBeCalledWith('new');
});

+ 10
- 10
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/ProjectKeyStep-test.js ファイルの表示

@@ -28,28 +28,28 @@ jest.mock('../../../../api/components', () => ({
deleteProject: () => Promise.resolve()
}));

it('creates new project', () => {
it('creates new project', async () => {
const onDone = jest.fn();
const wrapper = mount(<ProjectKeyStep onDelete={jest.fn()} onDone={onDone} />);
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'foo');
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(onDone).toBeCalledWith('foo');
});

it('deletes project', () => {
it('deletes project', async () => {
const onDelete = jest.fn();
const wrapper = mount(<ProjectKeyStep onDelete={onDelete} onDone={jest.fn()} />);
wrapper.setState({ done: true, loading: false, projectKey: 'foo' });
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => {
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(onDelete).toBeCalled();
});

+ 8
- 3
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/TokenStep-test.js ファイルの表示

@@ -21,7 +21,7 @@
import React from 'react';
import { mount } from 'enzyme';
import TokenStep from '../TokenStep';
import { change, click, doAsync, submit } from '../../../../helpers/testUtils';
import { change, click, submit } from '../../../../helpers/testUtils';

jest.mock('../../../../api/user-tokens', () => ({
getTokens: () => Promise.resolve([{ name: 'foo' }]),
@@ -43,11 +43,14 @@ it('generates token', async () => {
/>
);
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
change(wrapper.find('input'), 'my token');
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => expect(wrapper).toMatchSnapshot());
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});

it('revokes token', async () => {
@@ -66,7 +69,9 @@ it('revokes token', async () => {
expect(wrapper).toMatchSnapshot();
submit(wrapper.find('form'));
expect(wrapper).toMatchSnapshot(); // spinner
return doAsync(() => expect(wrapper).toMatchSnapshot());
await new Promise(setImmediate);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});

it('continues', async () => {

+ 17
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/OrganizationStep-test.js.snap ファイルの表示

@@ -25,6 +25,9 @@ exports[`works with existing organization 1`] = `
>
<div
className="boxed-group onboarding-step is-open"
onClick={undefined}
role={undefined}
tabIndex={undefined}
>
<div
className="onboarding-step-number"
@@ -79,6 +82,7 @@ exports[`works with existing organization 1`] = `
},
]
}
value={undefined}
>
<Select
addLabelText="Add \\"{label}\\"?"
@@ -134,11 +138,13 @@ exports[`works with existing organization 1`] = `
searchable={true}
simpleValue={false}
tabSelectsValue={true}
value={undefined}
valueComponent={[Function]}
valueKey="value"
>
<div
className="Select input-super-large Select--single is-searchable"
style={undefined}
>
<div
className="Select-control"
@@ -147,6 +153,7 @@ exports[`works with existing organization 1`] = `
onTouchEnd={[Function]}
onTouchMove={[Function]}
onTouchStart={[Function]}
style={undefined}
>
<span
className="Select-multi-value-wrapper"
@@ -159,8 +166,11 @@ exports[`works with existing organization 1`] = `
</div>
<AutosizeInput
aria-activedescendant="react-select-2--value"
aria-describedby={undefined}
aria-expanded="false"
aria-haspopup="false"
aria-label={undefined}
aria-labelledby={undefined}
aria-owns=""
className="Select-input"
minWidth="5"
@@ -169,6 +179,7 @@ exports[`works with existing organization 1`] = `
onFocus={[Function]}
required={false}
role="combobox"
tabIndex={undefined}
value=""
>
<div
@@ -188,9 +199,14 @@ exports[`works with existing organization 1`] = `
/>
<input
aria-activedescendant="react-select-2--value"
aria-describedby={undefined}
aria-expanded="false"
aria-haspopup="false"
aria-label={undefined}
aria-labelledby={undefined}
aria-owns=""
className={undefined}
id={undefined}
onBlur={[Function]}
onChange={[Function]}
onFocus={[Function]}
@@ -202,6 +218,7 @@ exports[`works with existing organization 1`] = `
"width": "5px",
}
}
tabIndex={undefined}
value=""
/>
<div

+ 18
- 0
server/sonar-web/src/main/js/apps/tutorials/onboarding/__tests__/__snapshots__/TokenStep-test.js.snap ファイルの表示

@@ -24,6 +24,9 @@ exports[`generates token 1`] = `
>
<div
className="boxed-group onboarding-step is-open"
onClick={undefined}
role={undefined}
tabIndex={undefined}
>
<div
className="onboarding-step-number"
@@ -126,6 +129,9 @@ exports[`generates token 2`] = `
>
<div
className="boxed-group onboarding-step is-open"
onClick={undefined}
role={undefined}
tabIndex={undefined}
>
<div
className="onboarding-step-number"
@@ -225,6 +231,9 @@ exports[`generates token 3`] = `
>
<div
className="boxed-group onboarding-step is-open"
onClick={undefined}
role={undefined}
tabIndex={undefined}
>
<div
className="onboarding-step-number"
@@ -322,6 +331,9 @@ exports[`revokes token 1`] = `
>
<div
className="boxed-group onboarding-step is-open"
onClick={undefined}
role={undefined}
tabIndex={undefined}
>
<div
className="onboarding-step-number"
@@ -419,6 +431,9 @@ exports[`revokes token 2`] = `
>
<div
className="boxed-group onboarding-step is-open"
onClick={undefined}
role={undefined}
tabIndex={undefined}
>
<div
className="onboarding-step-number"
@@ -498,6 +513,9 @@ exports[`revokes token 3`] = `
>
<div
className="boxed-group onboarding-step is-open"
onClick={undefined}
role={undefined}
tabIndex={undefined}
>
<div
className="onboarding-step-number"

+ 2
- 2
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/Command-test.js ファイルの表示

@@ -19,9 +19,9 @@
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import { mount } from 'enzyme';
import Command from '../Command';

it('renders correctly', () => {
expect(shallow(<Command command={'foo\nbar'} />)).toMatchSnapshot();
expect(mount(<Command command={'foo\nbar'} />)).toMatchSnapshot();
});

+ 16
- 11
server/sonar-web/src/main/js/apps/tutorials/onboarding/commands/__tests__/__snapshots__/Command-test.js.snap ファイルの表示

@@ -1,18 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders correctly 1`] = `
<div
className="onboarding-command"
<Command
command="foo
bar"
>
<pre>
foo
<div
className="onboarding-command"
>
<pre>
foo
bar
</pre>
<button
data-clipboard-text="foo
</pre>
<button
data-clipboard-text="foo
bar"
>
copy
</button>
</div>
>
copy
</button>
</div>
</Command>
`;

+ 2
- 2
server/sonar-web/src/main/js/apps/web-api/components/ResponseExample.tsx ファイルの表示

@@ -43,8 +43,8 @@ export default class ResponseExample extends React.PureComponent<Props, State> {
this.fetchResponseExample();
}

componentDidUpdate(nextProps: Props) {
if (nextProps.action !== this.props.action) {
componentDidUpdate(prevProps: Props) {
if (prevProps.action !== this.props.action) {
this.fetchResponseExample();
}
}

+ 5
- 3
server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.js ファイルの表示

@@ -115,9 +115,11 @@ export default class LineCode extends React.PureComponent {
}

attachEvents() {
this.symbols = this.codeNode.querySelectorAll('.sym');
for (const symbol of this.symbols) {
symbol.addEventListener('click', this.handleSymbolClick);
if (this.codeNode) {
this.symbols = this.codeNode.querySelectorAll('.sym');
for (const symbol of this.symbols) {
symbol.addEventListener('click', this.handleSymbolClick);
}
}
}


+ 22
- 0
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.js.snap ファイルの表示

@@ -2,13 +2,18 @@

exports[`should display bubbles 1`] = `
<Bubble
color={undefined}
key="0"
link={undefined}
onClick={undefined}
r={45}
tooltip={undefined}
x={-10}
y={52.3015873015873}
>
<circle
className="bubble-chart-bubble"
onClick={undefined}
r={45}
style={
Object {
@@ -23,13 +28,18 @@ exports[`should display bubbles 1`] = `

exports[`should display bubbles 2`] = `
<Bubble
color={undefined}
key="1"
link={undefined}
onClick={undefined}
r={33.57142857142857}
tooltip={undefined}
x={-75}
y={33.57142857142857}
>
<circle
className="bubble-chart-bubble"
onClick={undefined}
r={33.57142857142857}
style={
Object {
@@ -44,9 +54,12 @@ exports[`should display bubbles 2`] = `

exports[`should render bubble links 1`] = `
<Bubble
color={undefined}
key="0"
link="foo"
onClick={undefined}
r={45}
tooltip={undefined}
x={-10}
y={52.3015873015873}
>
@@ -61,6 +74,7 @@ exports[`should render bubble links 1`] = `
>
<circle
className="bubble-chart-bubble"
onClick={undefined}
r={45}
style={
Object {
@@ -77,9 +91,12 @@ exports[`should render bubble links 1`] = `

exports[`should render bubble links 2`] = `
<Bubble
color={undefined}
key="1"
link="bar"
onClick={undefined}
r={33.57142857142857}
tooltip={undefined}
x={-75}
y={33.57142857142857}
>
@@ -94,6 +111,7 @@ exports[`should render bubble links 2`] = `
>
<circle
className="bubble-chart-bubble"
onClick={undefined}
r={33.57142857142857}
style={
Object {
@@ -110,10 +128,12 @@ exports[`should render bubble links 2`] = `

exports[`should render bubbles with click handlers 1`] = `
<Bubble
color={undefined}
key="0"
link="foo"
onClick={[Function]}
r={45}
tooltip={undefined}
x={-10}
y={52.3015873015873}
>
@@ -134,10 +154,12 @@ exports[`should render bubbles with click handlers 1`] = `

exports[`should render bubbles with click handlers 2`] = `
<Bubble
color={undefined}
key="1"
link="bar"
onClick={[Function]}
r={33.57142857142857}
tooltip={undefined}
x={-75}
y={33.57142857142857}
>

+ 3
- 1
server/sonar-web/src/main/js/components/common/MultiSelect.js ファイルの表示

@@ -68,7 +68,9 @@ export default class MultiSelect extends React.PureComponent {
componentDidMount() {
this.updateSelectedElements(this.props);
this.updateUnselectedElements(this.props);
this.container.addEventListener('keydown', this.handleKeyboard, true);
if (this.container) {
this.container.addEventListener('keydown', this.handleKeyboard, true);
}
}

componentWillReceiveProps(nextProps /*: Props */) {

+ 0
- 0
server/sonar-web/src/main/js/components/common/__tests__/BranchStatus-test.tsx ファイルの表示


変更されたファイルが多すぎるため、一部のファイルは表示されません

読み込み中…
キャンセル
保存