aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/projects
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/apps/projects')
-rw-r--r--server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts40
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/AllProjects.js)83
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.js)3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js)50
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.js)2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js)23
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.js)4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.js)2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/PageHeader.js)93
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.js)4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js)29
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.js)58
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelectOption.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelectOption.js)36
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.js)26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.js75
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx73
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguagesContainer.ts28
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.js)51
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js)15
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.js)49
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js)21
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.js)9
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectsList.js)25
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsListContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectsListContainer.js)6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooter.js36
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooterContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooterContainer.js)8
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.js)80
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelectOption.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelectOption.js)39
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx180
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/DefaultPageSelector-test.tsx89
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/FavoriteFilter-test.tsx69
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.js)79
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.js)2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.js)16
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelectOption-test.tsx80
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLanguages-test.tsx53
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.js)20
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.js)47
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.js)10
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.js)2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardQualityGate-test.tsx30
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsList-test.tsx48
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelect-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelect-test.js)50
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelectOption-test.tsx80
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap155
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/EmptyInstance-test.tsx.snap11
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/FavoriteFilter-test.tsx.snap93
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap28
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.js.snap)31
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap)12
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.js.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelectOption-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelectOption-test.js.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap107
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.js.snap)2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap)12
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.js.snap)2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap)14
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap23
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap42
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.js.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelectOption-test.tsx.snap25
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js84
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.tsx80
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js87
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.tsx82
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/Filter.js)86
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/FilterHeader.tsx34
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js72
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.tsx70
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.js)71
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilterContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilterContainer.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/MaintainabilityFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/MaintainabilityFilter.js)12
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.js)6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.js)6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.js71
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.tsx61
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.js)11
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.js)11
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.js)11
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.js)55
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/ReliabilityFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/ReliabilityFilter.js)12
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js)49
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.js)41
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterFooter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterFooter.js)47
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterOption.js34
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterOption.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/FilterHeader.js)28
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/SecurityFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/SecurityFilter.js)12
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js77
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.tsx67
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/TagsFilter.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/TagsFilter.js)99
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/TagsFilterContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/filters/TagsFilterContainer.js)5
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/CoverageFilter-test.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/DuplicationsFilter-test.tsx39
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/Filter-test.tsx73
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/FilterHeader-test.tsx36
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/IssuesFilter-test.tsx33
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/LanguagesFilter-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/__tests__/LanguagesFilter-test.js)43
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/MaintainabilityFilter-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewCoverageFilter-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewDuplicationsFilter-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewLinesFilter-test.tsx44
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewMaintainabilityFilter-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewReliabilityFilter-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewSecurityFilter-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx33
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/ReliabilityFilter-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilter-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilter-test.js)27
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx37
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.js)47
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterOption-test.tsx29
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/SecurityFilter-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/SizeFilter-test.tsx44
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/TagsFilter-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/filters/__tests__/TagsFilter-test.js)9
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/CoverageFilter-test.tsx.snap42
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/DuplicationsFilter-test.tsx.snap56
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap596
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/FilterHeader-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/IssuesFilter-test.tsx.snap39
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.js.snap)23
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/MaintainabilityFilter-test.tsx.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewCoverageFilter-test.tsx.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewDuplicationsFilter-test.tsx.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewLinesFilter-test.tsx.snap32
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewMaintainabilityFilter-test.tsx.snap22
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewReliabilityFilter-test.tsx.snap22
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewSecurityFilter-test.tsx.snap22
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap30
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/ReliabilityFilter-test.tsx.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.js.snap)0
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilterContainer-test.tsx.snap8
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterFooter-test.js.snap131
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterFooter-test.tsx.snap18
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterOption-test.tsx.snap13
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SecurityFilter-test.tsx.snap9
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SizeFilter-test.tsx.snap40
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap (renamed from server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.js.snap)29
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/__tests__/utils-test.ts54
-rw-r--r--server/sonar-web/src/main/js/apps/projects/filters/utils.ts (renamed from server/sonar-web/src/main/js/apps/projects/filters/utils.js)13
-rw-r--r--server/sonar-web/src/main/js/apps/projects/types.ts25
-rw-r--r--server/sonar-web/src/main/js/apps/projects/utils.ts (renamed from server/sonar-web/src/main/js/apps/projects/utils.js)18
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.tsx (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.js)30
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.tsx (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.js)30
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.tsx (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.js)31
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.tsx (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.js)31
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/Risk.js)58
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/Security.tsx39
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.js)65
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.tsx (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.js)28
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.ts (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.js)6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Coverage-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Duplications-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Maintainability-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Reliability-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx31
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Security-test.tsx26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/visualizations/Security.js)36
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Visualizations-test.tsx (renamed from server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelectOption-test.js)28
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Coverage-test.tsx.snap32
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Duplications-test.tsx.snap26
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Maintainability-test.tsx.snap27
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Reliability-test.tsx.snap27
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap77
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Security-test.tsx.snap27
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap71
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Visualizations-test.tsx.snap50
169 files changed, 4967 insertions, 1777 deletions
diff --git a/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts
new file mode 100644
index 00000000000..d48601e76ba
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/__tests__/utils-test.ts
@@ -0,0 +1,40 @@
+/*
+ * 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 * as utils from '../utils';
+
+describe('localizeSorting', () => {
+ it('localizes default sorting', () => {
+ expect(utils.localizeSorting()).toBe('projects.sort.name');
+ });
+
+ it('localizes custom sorting', () => {
+ expect(utils.localizeSorting('size')).toBe('projects.sort.size');
+ });
+});
+
+describe('parseSorting', () => {
+ it('parses ascending', () => {
+ expect(utils.parseSorting('size')).toEqual({ sortDesc: false, sortValue: 'size' });
+ });
+
+ it('parses descending', () => {
+ expect(utils.parseSorting('-size')).toEqual({ sortDesc: true, sortValue: 'size' });
+ });
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
index b87bf26551d..bca7874cba0 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
@@ -17,8 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
+import * as PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import PageHeaderContainer from './PageHeaderContainer';
import ProjectsListContainer from './ProjectsListContainer';
@@ -29,32 +29,27 @@ import { parseUrlQuery } from '../store/utils';
import { translate } from '../../../helpers/l10n';
import * as utils from '../utils';
import * as storage from '../../../helpers/storage';
-/*:: import type { RawQuery } from '../../../helpers/query'; */
+import { RawQuery } from '../../../helpers/query';
import '../styles.css';
-/*::
-type Props = {|
- isFavorite: boolean,
- location: { pathname: string, query: RawQuery },
- fetchProjects: (query: string, isFavorite: boolean, organization?: {}) => Promise<*>,
- organization?: { key: string },
- router: {
- push: ({ pathname: string, query?: {} }) => void,
- replace: ({ pathname: string, query?: {} }) => void
- },
- currentUser?: { isLoggedIn: boolean }
-|};
-*/
-
-/*::
-type State = {
- query: RawQuery
-};
-*/
-
-export default class AllProjects extends React.PureComponent {
- /*:: props: Props; */
- state /*: State */ = { query: {} };
+interface Props {
+ isFavorite: boolean;
+ location: { pathname: string; query: RawQuery };
+ fetchProjects: (query: RawQuery, isFavorite: boolean, organization?: {}) => Promise<any>;
+ organization?: { key: string };
+ currentUser?: { isLoggedIn: boolean };
+}
+
+interface State {
+ query: RawQuery;
+}
+
+export default class AllProjects extends React.PureComponent<Props, State> {
+ state: State = { query: {} };
+
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
componentDidMount() {
this.handleQueryChange(true);
@@ -62,7 +57,7 @@ export default class AllProjects extends React.PureComponent {
footer && footer.classList.add('page-footer-with-sidebar');
}
- componentDidUpdate(prevProps /*: Props */) {
+ componentDidUpdate(prevProps: Props) {
if (prevProps.location.query !== this.props.location.query) {
this.handleQueryChange(false);
}
@@ -82,23 +77,29 @@ export default class AllProjects extends React.PureComponent {
isFiltered = () => Object.keys(this.state.query).some(key => this.state.query[key] != null);
getSavedOptions = () => {
- const options = {};
+ const options: {
+ sort?: string;
+ view?: string;
+ visualization?: string;
+ } = {};
if (storage.getSort()) {
- options.sort = storage.getSort();
+ options.sort = storage.getSort() || undefined;
}
if (storage.getView()) {
- options.view = storage.getView();
+ options.view = storage.getView() || undefined;
}
if (storage.getVisualization()) {
- options.visualization = storage.getVisualization();
+ options.visualization = storage.getVisualization() || undefined;
}
return options;
};
- handlePerspectiveChange = (
- { view, visualization } /*: { view: string, visualization?: string } */
- ) => {
- const query /*: { view: ?string, visualization: ?string, sort?: ?string } */ = {
+ handlePerspectiveChange = ({ view, visualization }: { view: string; visualization?: string }) => {
+ const query: {
+ view: string | undefined;
+ visualization: string | undefined;
+ sort?: string | undefined;
+ } = {
view: view === 'overall' ? undefined : view,
visualization
};
@@ -110,7 +111,7 @@ export default class AllProjects extends React.PureComponent {
query.sort = (sort.sortDesc ? '-' : '') + utils.SORTING_SWITCH[sort.sortValue];
}
}
- this.props.router.push({ pathname: this.props.location.pathname, query });
+ this.context.router.push({ pathname: this.props.location.pathname, query });
} else {
this.updateLocationQuery(query);
}
@@ -120,28 +121,28 @@ export default class AllProjects extends React.PureComponent {
storage.saveVisualization(visualization);
};
- handleSortChange = (sort /*: string */, desc /*: boolean */) => {
+ handleSortChange = (sort: string, desc: boolean) => {
const asString = (desc ? '-' : '') + sort;
this.updateLocationQuery({ sort: asString });
storage.saveSort(asString);
};
- handleQueryChange(initialMount /*: boolean */) {
+ handleQueryChange(initialMount: boolean) {
const query = parseUrlQuery(this.props.location.query);
const savedOptions = this.getSavedOptions();
const savedOptionsSet = savedOptions.sort || savedOptions.view || savedOptions.visualization;
// if there is no filter, but there are saved preferences in the localStorage
if (initialMount && !this.isFiltered() && savedOptionsSet) {
- this.props.router.replace({ pathname: this.props.location.pathname, query: savedOptions });
+ this.context.router.replace({ pathname: this.props.location.pathname, query: savedOptions });
} else {
this.setState({ query });
this.props.fetchProjects(query, this.props.isFavorite, this.props.organization);
}
}
- updateLocationQuery = (newQuery /*: { [string]: ?string } */) => {
- this.props.router.push({
+ updateLocationQuery = (newQuery: RawQuery) => {
+ this.context.router.push({
pathname: this.props.location.pathname,
query: {
...this.props.location.query,
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.ts
index f2fa86a3f2a..83d5f49ba66 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.ts
@@ -18,8 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { connect } from 'react-redux';
-import { withRouter } from 'react-router';
import AllProjects from './AllProjects';
import { fetchProjects } from '../store/actions';
-export default connect(null, { fetchProjects })(withRouter(AllProjects));
+export default connect<null, any, any>(null, { fetchProjects })(AllProjects);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js b/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.tsx
index d040e9e7330..f8e73ba7fad 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.tsx
@@ -17,38 +17,30 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { connect } from 'react-redux';
-import { withRouter } from 'react-router';
+import * as PropTypes from 'prop-types';
import AllProjectsContainer from './AllProjectsContainer';
import { getCurrentUser } from '../../../store/rootReducer';
import { isFavoriteSet, isAllSet } from '../../../helpers/storage';
import { searchProjects } from '../../../api/components';
-/*:: import type { RawQuery } from '../../../helpers/query'; */
-/*::
-type Props = {
- currentUser: { isLoggedIn: boolean },
- location: { query: {} },
- router: {
- replace: (location: { pathname?: string, query?: RawQuery }) => void
- }
-};
-*/
+interface Props {
+ currentUser: { isLoggedIn: boolean };
+ location: { query: { [x: string]: string } };
+}
-/*::
-type State = {
- shouldBeRedirected?: boolean,
- shouldForceSorting?: string
-};
-*/
+interface State {
+ shouldBeRedirected?: boolean;
+ shouldForceSorting?: string;
+}
-class DefaultPageSelector extends React.PureComponent {
- /*:: props: Props; */
- /*:: state: State; */
+class DefaultPageSelector extends React.PureComponent<Props, State> {
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
- constructor(props /*: Props */) {
+ constructor(props: Props) {
super(props);
this.state = {};
}
@@ -57,13 +49,13 @@ class DefaultPageSelector extends React.PureComponent {
this.defineIfShouldBeRedirected();
}
- componentDidUpdate(prevProps /*: Props */) {
+ componentDidUpdate(prevProps: Props) {
if (prevProps.location !== this.props.location) {
this.defineIfShouldBeRedirected();
} else if (this.state.shouldBeRedirected === true) {
- this.props.router.replace({ ...this.props.location, pathname: '/projects/favorite' });
+ this.context.router.replace({ ...this.props.location, pathname: '/projects/favorite' });
} else if (this.state.shouldForceSorting != null) {
- this.props.router.replace({
+ this.context.router.replace({
...this.props.location,
query: {
...this.props.location.query,
@@ -117,8 +109,10 @@ class DefaultPageSelector extends React.PureComponent {
}
}
-const mapStateToProps = state => ({
+const mapStateToProps = (state: any) => ({
currentUser: getCurrentUser(state)
});
-export default connect(mapStateToProps)(withRouter(DefaultPageSelector));
+export default connect<any, any, any>(mapStateToProps)(DefaultPageSelector);
+
+export const UnconnectedDefaultPageSelector = DefaultPageSelector;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.js b/server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx
index 1eb2241cfb1..d83a4fb3e5e 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/EmptyInstance.tsx
@@ -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 React from 'react';
+import * as React from 'react';
import { translate } from '../../../helpers/l10n';
export default function EmptyInstance() {
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.tsx
index b2e9de9b6e4..372d0613c3e 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilter.tsx
@@ -17,26 +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.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { IndexLink, Link } from 'react-router';
import { translate } from '../../../helpers/l10n';
import { saveAll, saveFavorite } from '../../../helpers/storage';
-/*:: import type { RawQuery } from '../../../helpers/query'; */
+import { RawQuery } from '../../../helpers/query';
-/*::
-type Props = {
- user: {
- isLoggedIn?: boolean
- },
- organization?: { key: string },
- query: RawQuery
-};
-*/
-
-export default class FavoriteFilter extends React.PureComponent {
- /*:: props: Props; */
+interface Props {
+ user: { isLoggedIn?: boolean };
+ organization?: { key: string };
+ query: RawQuery;
+}
+export default class FavoriteFilter extends React.PureComponent<Props> {
handleSaveFavorite = () => {
if (!this.props.organization) {
saveFavorite();
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.tsx
index d3994b39bdb..515fbf0ae8c 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/FavoriteFilterContainer.tsx
@@ -21,8 +21,8 @@ import { connect } from 'react-redux';
import FavoriteFilter from './FavoriteFilter';
import { getCurrentUser } from '../../../store/rootReducer';
-const mapStateToProps = state => ({
+const mapStateToProps = (state: any) => ({
user: getCurrentUser(state)
});
-export default connect(mapStateToProps)(FavoriteFilter);
+export default connect<any, any, any>(mapStateToProps)(FavoriteFilter);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.js b/server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.ts
index bf2a030408b..fc3ad9992c6 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/FavoriteProjectsContainer.ts
@@ -18,14 +18,13 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { connect } from 'react-redux';
-import { withRouter } from 'react-router';
import AllProjects from './AllProjects';
import { getCurrentUser } from '../../../store/rootReducer';
import { fetchProjects } from '../store/actions';
-const mapStateToProps = state => ({
+const mapStateToProps = (state: any) => ({
isFavorite: true,
currentUser: getCurrentUser(state)
});
-export default connect(mapStateToProps, { fetchProjects })(withRouter(AllProjects));
+export default connect<any, any, any>(mapStateToProps, { fetchProjects })(AllProjects);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.js b/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx
index f69fbebc834..effc6ad0dbb 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx
@@ -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 React from 'react';
+import * as React from 'react';
import { Link } from 'react-router';
import { translate } from '../../../helpers/l10n';
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
index f4a616dc52b..f9fd94bd3dd 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
@@ -17,42 +17,45 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
-import classNames from 'classnames';
+import * as React from 'react';
+import * as classNames from 'classnames';
import SearchFilterContainer from '../filters/SearchFilterContainer';
import Tooltip from '../../../components/controls/Tooltip';
import PerspectiveSelect from './PerspectiveSelect';
import ProjectsSortingSelect from './ProjectsSortingSelect';
import { translate } from '../../../helpers/l10n';
-/*:: import type { RawQuery } from '../../../helpers/query'; */
+import { RawQuery } from '../../../helpers/query';
-/*::
-type Props = {|
- currentUser?: { isLoggedIn: boolean },
- isFavorite?: boolean,
- onPerspectiveChange: ({ view: string, visualization?: string }) => void,
- organization?: { key: string },
- projects: Array<*>,
- projectsAppState: { loading: boolean, total?: number },
- query: RawQuery,
- onSortChange: (sort: string, desc: boolean) => void,
- selectedSort: string,
- view: string,
- visualization?: string
-|};
-*/
+interface Props {
+ currentUser?: { isLoggedIn: boolean };
+ isFavorite?: boolean;
+ onPerspectiveChange: (x: { view: string; visualization?: string }) => void;
+ organization?: { key: string };
+ projects: Array<any>;
+ projectsAppState: { loading: boolean; total?: number };
+ query: RawQuery;
+ onSortChange: (sort: string, desc: boolean) => void;
+ selectedSort: string;
+ view: string;
+ visualization?: string;
+}
+
+export default function PageHeader(props: Props) {
+ const { projectsAppState, projects, currentUser, view } = props;
+ const limitReached =
+ projects != null && projectsAppState.total != null && projects.length < projectsAppState.total;
+ const defaultOption = currentUser && currentUser.isLoggedIn ? 'name' : 'analysis_date';
+
+ return (
+ <header className="page-header projects-topbar-items">
+ <PerspectiveSelect
+ className="projects-topbar-item js-projects-perspective-select"
+ onChange={props.onPerspectiveChange}
+ view={props.view}
+ visualization={props.visualization}
+ />
-export default function PageHeader(props /*: Props */) {
- const renderSortingSelect = () => {
- const { projectsAppState, projects, currentUser, view } = props;
- const limitReached =
- projects != null &&
- projectsAppState.total != null &&
- projects.length < projectsAppState.total;
- const defaultOption = currentUser && currentUser.isLoggedIn ? 'name' : 'analysis_date';
- if (view === 'visualizations' && !limitReached) {
- return (
+ {view === 'visualizations' && !limitReached ? (
<Tooltip overlay={translate('projects.sort.disabled')}>
<div className="projects-topbar-item disabled">
<ProjectsSortingSelect
@@ -64,29 +67,15 @@ export default function PageHeader(props /*: Props */) {
/>
</div>
</Tooltip>
- );
- }
- return (
- <ProjectsSortingSelect
- className="projects-topbar-item js-projects-sorting-select"
- defaultOption={defaultOption}
- onChange={props.onSortChange}
- selectedSort={props.selectedSort}
- view={props.view}
- />
- );
- };
-
- return (
- <header className="page-header projects-topbar-items">
- <PerspectiveSelect
- className="projects-topbar-item js-projects-perspective-select"
- onChange={props.onPerspectiveChange}
- view={props.view}
- visualization={props.visualization}
- />
-
- {renderSortingSelect()}
+ ) : (
+ <ProjectsSortingSelect
+ className="projects-topbar-item js-projects-sorting-select"
+ defaultOption={defaultOption}
+ onChange={props.onSortChange}
+ selectedSort={props.selectedSort}
+ view={props.view}
+ />
+ )}
<SearchFilterContainer
className="projects-topbar-item projects-topbar-item-search"
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.js b/server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.ts
index 7780da0a705..428e43364d3 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.ts
@@ -21,9 +21,9 @@ import { connect } from 'react-redux';
import PageHeader from './PageHeader';
import { getProjects, getProjectsAppState } from '../../../store/rootReducer';
-const mapStateToProps = state => ({
+const mapStateToProps = (state: any) => ({
projects: getProjects(state),
projectsAppState: getProjectsAppState(state)
});
-export default connect(mapStateToProps)(PageHeader);
+export default connect<any, any, any>(mapStateToProps)(PageHeader);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx
index 34c923669db..e1d6e6dddb0 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx
@@ -17,8 +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.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
import { Link } from 'react-router';
import FavoriteFilterContainer from './FavoriteFilterContainer';
import LanguagesFilterContainer from '../filters/LanguagesFilterContainer';
@@ -37,21 +36,18 @@ import SecurityFilter from '../filters/SecurityFilter';
import SizeFilter from '../filters/SizeFilter';
import TagsFilterContainer from '../filters/TagsFilterContainer';
import { translate } from '../../../helpers/l10n';
-/*:: import type { RawQuery } from '../../../helpers/query'; */
+import { RawQuery } from '../../../helpers/query';
-/*::
-type Props = {
- isFavorite: boolean,
- organization?: { key: string },
- query: RawQuery,
- view: string,
- visualization: string
-};
-*/
+interface Props {
+ isFavorite: boolean;
+ organization?: { key: string };
+ query: RawQuery;
+ view: string;
+ visualization: string;
+}
-export default function PageSidebar(
- { query, isFavorite, organization, view, visualization } /*: Props */
-) {
+export default function PageSidebar(props: Props) {
+ const { query, isFavorite, organization, view, visualization } = props;
const isFiltered = Object.keys(query)
.filter(key => !['view', 'visualization', 'sort'].includes(key))
.some(key => query[key] != null);
@@ -60,12 +56,11 @@ export default function PageSidebar(
const pathname = basePathName + (isFavorite ? '/favorite' : '');
const facetProps = { query, isFavorite, organization };
- let linkQuery;
+ let linkQuery: RawQuery | undefined = undefined;
if (view !== 'overall') {
linkQuery = { view };
if (view === 'visualizations') {
- // $FlowFixMe
linkQuery.visualization = visualization;
}
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.js b/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx
index 2ed398b6e60..c27d1fce861 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.tsx
@@ -17,33 +17,32 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
-import Select from 'react-select';
-import PerspectiveSelectOption from './PerspectiveSelectOption';
+import * as React from 'react';
+import * as Select from 'react-select';
+import PerspectiveSelectOption, { Option } from './PerspectiveSelectOption';
import { translate } from '../../../helpers/l10n';
import { VIEWS, VISUALIZATIONS } from '../utils';
-/*::
-export type Option = { label: string, type: string, value: string };
-*/
-
-/*::
-type Props = {|
- className?: string,
- onChange: ({ view: string, visualization?: string }) => void,
- view: string,
- visualization?: string
-|};
-*/
+interface Props {
+ className?: string;
+ onChange: (x: { view: string; visualization?: string }) => void;
+ view: string;
+ visualization?: string;
+}
-export default class PerspectiveSelect extends React.PureComponent {
- /*:: options: Array<Option>; */
- /*:: props: Props; */
+export default class PerspectiveSelect extends React.PureComponent<Props> {
+ handleChange = (option: Option) => {
+ if (option.type === 'view') {
+ this.props.onChange({ view: option.value });
+ } else if (option.type === 'visualization') {
+ this.props.onChange({ view: 'visualizations', visualization: option.value });
+ }
+ };
- constructor(props /*: Props */) {
- super(props);
- this.options = [
+ render() {
+ const { view, visualization } = this.props;
+ const perspective = view === 'visualizations' ? visualization : view;
+ const options = [
...VIEWS.map(opt => ({
type: 'view',
value: opt,
@@ -55,19 +54,6 @@ export default class PerspectiveSelect extends React.PureComponent {
label: translate('projects.visualization', opt)
}))
];
- }
-
- handleChange = (option /*: Option */) => {
- if (option.type === 'view') {
- this.props.onChange({ view: option.value });
- } else if (option.type === 'visualization') {
- this.props.onChange({ view: 'visualizations', visualization: option.value });
- }
- };
-
- render() {
- const { view, visualization } = this.props;
- const perspective = view === 'visualizations' ? visualization : view;
return (
<div className={this.props.className}>
<label>{translate('projects.perspective')}:</label>
@@ -76,7 +62,7 @@ export default class PerspectiveSelect extends React.PureComponent {
clearable={false}
onChange={this.handleChange}
optionComponent={PerspectiveSelectOption}
- options={this.options}
+ options={options}
searchable={false}
value={perspective}
/>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelectOption.js b/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelectOption.tsx
index 6fd3307a98d..0ae8a15e110 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelectOption.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelectOption.tsx
@@ -17,37 +17,37 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
import BubblesIcon from '../../../components/icons-components/BubblesIcon';
import ListIcon from '../../../components/icons-components/ListIcon';
-/*:: import type { Option } from './PerspectiveSelect'; */
-/*::
-type Props = {
- option: Option,
- children?: Element | Text,
- className?: string,
- isFocused?: boolean,
- onFocus: (Option, MouseEvent) => void,
- onSelect: (Option, MouseEvent) => void
-};
-*/
+export interface Option {
+ label: string;
+ type: string;
+ value: string;
+}
-export default class PerspectiveSelectOption extends React.PureComponent {
- /*:: props: Props; */
+interface Props {
+ option: Option;
+ children?: React.ReactNode;
+ className?: string;
+ isFocused?: boolean;
+ onFocus: (option: Option, event: React.SyntheticEvent<HTMLElement>) => void;
+ onSelect: (option: Option, event: React.SyntheticEvent<HTMLElement>) => void;
+}
- handleMouseDown = (event /*: MouseEvent */) => {
+export default class PerspectiveSelectOption extends React.PureComponent<Props> {
+ handleMouseDown = (event: React.SyntheticEvent<HTMLElement>) => {
event.preventDefault();
event.stopPropagation();
this.props.onSelect(this.props.option, event);
};
- handleMouseEnter = (event /*: MouseEvent */) => {
+ handleMouseEnter = (event: React.SyntheticEvent<HTMLElement>) => {
this.props.onFocus(this.props.option, event);
};
- handleMouseMove = (event /*: MouseEvent */) => {
+ handleMouseMove = (event: React.SyntheticEvent<HTMLElement>) => {
if (this.props.isFocused) {
return;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.tsx
index a47d8a82e33..429d7abe1ae 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardContainer.tsx
@@ -17,20 +17,38 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { connect } from 'react-redux';
import ProjectCardLeak from './ProjectCardLeak';
import ProjectCardOverall from './ProjectCardOverall';
import { getComponent, getComponentMeasures } from '../../../store/rootReducer';
-function ProjectCard(props /*: { type?: string } */) {
+interface Props {
+ measures?: { [key: string]: string };
+ organization?: { key: string };
+ project?: {
+ analysisDate?: string;
+ key: string;
+ leakPeriodDate?: string;
+ name: string;
+ tags: Array<string>;
+ isFavorite?: boolean;
+ organization?: string;
+ visibility?: string;
+ };
+ type?: string;
+}
+
+function ProjectCard(props: Props) {
if (props.type === 'leak') {
return <ProjectCardLeak {...props} />;
}
return <ProjectCardOverall {...props} />;
}
-export default connect((state, ownProps) => ({
+const mapStateToProps = (state: any, ownProps: any) => ({
project: getComponent(state, ownProps.projectKey),
measures: getComponentMeasures(state, ownProps.projectKey)
-}))(ProjectCard);
+});
+
+export default connect(mapStateToProps)(ProjectCard);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.js
deleted file mode 100644
index 0b6cfab5cde..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import { sortBy } from 'lodash';
-import { connect } from 'react-redux';
-import Tooltip from '../../../components/controls/Tooltip';
-import { getLanguages } from '../../../store/rootReducer';
-import { translate } from '../../../helpers/l10n';
-
-class ProjectCardLanguages extends React.PureComponent {
- getLanguageName(key) {
- if (key === '<null>') {
- return translate('unknown');
- }
- const language = this.props.languages[key];
- return language != null ? language.name : key;
- }
-
- render() {
- const { distribution } = this.props;
-
- if (distribution == null) {
- return null;
- }
- const parsedLanguages = distribution.split(';').map(item => item.split('='));
- const finalLanguages = sortBy(parsedLanguages, l => -1 * Number(l[1])).map(l =>
- this.getLanguageName(l[0])
- );
-
- const tooltip = (
- <span>
- {finalLanguages.map(language => (
- <span key={language}>
- {language}
- <br />
- </span>
- ))}
- </span>
- );
-
- const languagesText =
- finalLanguages.slice(0, 2).join(', ') + (finalLanguages.length > 2 ? ', ...' : '');
-
- return (
- <div className="project-card-languages">
- <Tooltip placement="bottom" overlay={tooltip}>
- <span>{languagesText}</span>
- </Tooltip>
- </div>
- );
- }
-}
-
-const mapStateToProps = state => ({
- languages: getLanguages(state)
-});
-
-export default connect(mapStateToProps)(ProjectCardLanguages);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx
new file mode 100644
index 00000000000..e676d7378f0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx
@@ -0,0 +1,73 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { sortBy } from 'lodash';
+import Tooltip from '../../../components/controls/Tooltip';
+import { translate } from '../../../helpers/l10n';
+
+interface Languages {
+ [key: string]: { key: string; name: string };
+}
+
+interface Props {
+ distribution?: string;
+ languages: Languages;
+}
+
+export default function ProjectCardLanguages({ distribution, languages }: Props) {
+ if (distribution == undefined) {
+ return null;
+ }
+
+ const parsedLanguages = distribution.split(';').map(item => item.split('='));
+ const finalLanguages = sortBy(parsedLanguages, l => -1 * Number(l[1])).map(l =>
+ getLanguageName(languages, l[0])
+ );
+
+ const tooltip = (
+ <span>
+ {finalLanguages.map(language => (
+ <span key={language}>
+ {language}
+ <br />
+ </span>
+ ))}
+ </span>
+ );
+
+ const languagesText =
+ finalLanguages.slice(0, 2).join(', ') + (finalLanguages.length > 2 ? ', ...' : '');
+
+ return (
+ <div className="project-card-languages">
+ <Tooltip placement="bottom" overlay={tooltip}>
+ <span>{languagesText}</span>
+ </Tooltip>
+ </div>
+ );
+}
+
+function getLanguageName(languages: Languages, key: string): string {
+ if (key === '<null>') {
+ return translate('unknown');
+ }
+ const language = languages[key];
+ return language != null ? language.name : key;
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguagesContainer.ts b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguagesContainer.ts
new file mode 100644
index 00000000000..3d0429e6e3c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguagesContainer.ts
@@ -0,0 +1,28 @@
+/*
+ * 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 { connect } from 'react-redux';
+import ProjectCardLanguages from './ProjectCardLanguages';
+import { getLanguages } from '../../../store/rootReducer';
+
+const mapStateToProps = (state: any) => ({
+ languages: getLanguages(state)
+});
+
+export default connect<any, any, any>(mapStateToProps)(ProjectCardLanguages);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx
index 4c6867a3dd8..aefda0b2ac2 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx
@@ -17,12 +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.
*/
-// @flow
-import React from 'react';
-import classNames from 'classnames';
+import * as React from 'react';
+import * as classNames from 'classnames';
import { Link } from 'react-router';
import DateFromNow from '../../../components/intl/DateFromNow';
-import DateTimeFormatter from '../../../components/intl/DateTimeFormatter.tsx';
+import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import ProjectCardQualityGate from './ProjectCardQualityGate';
import ProjectCardLeakMeasures from './ProjectCardLeakMeasures';
import FavoriteContainer from '../../../components/controls/FavoriteContainer';
@@ -31,37 +30,35 @@ import TagsList from '../../../components/tags/TagsList';
import PrivateBadge from '../../../components/common/PrivateBadge';
import { translate, translateWithParameters } from '../../../helpers/l10n';
-/*::
-type Props = {
- measures: { [string]: string },
- organization?: { key: string },
+interface Props {
+ measures?: { [key: string]: string };
+ organization?: { key: string };
project?: {
- analysisDate?: string,
- key: string,
- leakPeriodDate?: string,
- name: string,
- tags: Array<string>,
- isFavorite?: boolean,
- organization?: string,
- visibility?: boolean
- }
-};
-*/
+ analysisDate?: string;
+ key: string;
+ leakPeriodDate?: string;
+ name: string;
+ tags: Array<string>;
+ isFavorite?: boolean;
+ organization?: string;
+ visibility?: string;
+ };
+}
-export default function ProjectCardLeak({ measures, organization, project } /*: Props */) {
- if (project == null) {
+export default function ProjectCardLeak({ measures, organization, project }: Props) {
+ if (project == undefined) {
return null;
}
const isProjectAnalyzed = project.analysisDate != null;
const isPrivate = project.visibility === 'private';
- const hasLeakPeriodStart = project.leakPeriodDate != null;
+ const hasLeakPeriodStart = project.leakPeriodDate != undefined;
const hasTags = project.tags.length > 0;
- const showOrganization = organization == null && project.organization != null;
+ const showOrganization = organization == undefined && project.organization != undefined;
// check for particular measures because only some measures can be loaded
// if coming from visualizations tab
- const areProjectMeasuresLoaded = measures != null && measures['new_bugs'];
+ const areProjectMeasuresLoaded = measures != undefined && measures['new_bugs'];
const displayQualityGate = areProjectMeasuresLoaded && isProjectAnalyzed;
const className = classNames('boxed-group', 'project-card', {
@@ -82,7 +79,7 @@ export default function ProjectCardLeak({ measures, organization, project } /*:
)}
<Link to={{ pathname: '/dashboard', query: { id: project.key } }}>{project.name}</Link>
</h2>
- {displayQualityGate && <ProjectCardQualityGate status={measures['alert_status']} />}
+ {displayQualityGate && <ProjectCardQualityGate status={measures!['alert_status']} />}
<div className="pull-right text-right">
{isPrivate && <PrivateBadge className="spacer-left" tooltipPlacement="left" />}
{hasTags && <TagsList tags={project.tags} customClass="spacer-left" />}
@@ -91,7 +88,7 @@ export default function ProjectCardLeak({ measures, organization, project } /*:
hasLeakPeriodStart && (
<div className="project-card-dates note text-right pull-right">
{hasLeakPeriodStart && (
- <DateFromNow date={project.leakPeriodDate}>
+ <DateFromNow date={project.leakPeriodDate!}>
{fromNow => (
<span className="project-card-leak-date pull-right">
{translateWithParameters('projects.leak_period_x', fromNow)}
@@ -100,7 +97,7 @@ export default function ProjectCardLeak({ measures, organization, project } /*:
</DateFromNow>
)}
{isProjectAnalyzed && (
- <DateTimeFormatter date={project.analysisDate}>
+ <DateTimeFormatter date={project.analysisDate!}>
{formattedDate => (
<span>
{translateWithParameters('projects.last_analysis_on_x', formattedDate)}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx
index bcd27888e68..353a0d6ccc9 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.tsx
@@ -17,8 +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.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
import Measure from '../../../components/measure/Measure';
import BugIcon from '../../../components/icons-components/BugIcon';
import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
@@ -26,14 +25,12 @@ import Rating from '../../../components/ui/Rating';
import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
import { translate } from '../../../helpers/l10n';
-/*::
-type Props = {
- measures?: { [string]: string }
-};
-*/
+interface Props {
+ measures?: { [key: string]: string };
+}
-export default function ProjectCardLeakMeasures({ measures } /*: Props */) {
- if (measures == null) {
+export default function ProjectCardLeakMeasures({ measures }: Props) {
+ if (measures == undefined) {
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx
index 05c35ab14b3..c52c09a3ff6 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx
@@ -17,9 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
-import classNames from 'classnames';
+import * as React from 'react';
+import * as classNames from 'classnames';
import { Link } from 'react-router';
import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
import ProjectCardQualityGate from './ProjectCardQualityGate';
@@ -30,36 +29,36 @@ import TagsList from '../../../components/tags/TagsList';
import PrivateBadge from '../../../components/common/PrivateBadge';
import { translate, translateWithParameters } from '../../../helpers/l10n';
-/*::
-type Props = {
- measures: { [string]: string },
- organization?: { key: string },
+interface Props {
+ measures?: { [key: string]: string };
+ organization?: { key: string };
project?: {
- analysisDate?: string,
- key: string,
- name: string,
- tags: Array<string>,
- isFavorite?: boolean,
- organization?: string,
- visibility?: boolean
- }
-};
-*/
+ analysisDate?: string;
+ key: string;
+ name: string;
+ tags: Array<string>;
+ isFavorite?: boolean;
+ organization?: string;
+ visibility?: string;
+ };
+}
-export default function ProjectCardOverall({ measures, organization, project } /*: Props */) {
- if (project == null) {
+export default function ProjectCardOverall({ measures, organization, project }: Props) {
+ if (project == undefined) {
return null;
}
- const isProjectAnalyzed = project.analysisDate != null;
+ const isProjectAnalyzed = project.analysisDate != undefined;
const isPrivate = project.visibility === 'private';
const hasTags = project.tags.length > 0;
- const showOrganization = organization == null && project.organization != null;
+ const showOrganization = organization == undefined && project.organization != undefined;
// check for particular measures because only some measures can be loaded
// if coming from visualizations tab
const areProjectMeasuresLoaded =
- measures != null && measures['reliability_rating'] != null && measures['sqale_rating'] != null;
+ measures != undefined &&
+ measures['reliability_rating'] != undefined &&
+ measures['sqale_rating'] != undefined;
const displayQualityGate = areProjectMeasuresLoaded && isProjectAnalyzed;
const className = classNames('boxed-group', 'project-card', {
@@ -69,7 +68,7 @@ export default function ProjectCardOverall({ measures, organization, project } /
return (
<div data-key={project.key} className={className}>
<div className="boxed-group-header clearfix">
- {project.isFavorite != null && (
+ {project.isFavorite != undefined && (
<FavoriteContainer className="spacer-right" componentKey={project.key} />
)}
<h2 className="project-card-name">
@@ -80,12 +79,12 @@ export default function ProjectCardOverall({ measures, organization, project } /
)}
<Link to={{ pathname: '/dashboard', query: { id: project.key } }}>{project.name}</Link>
</h2>
- {displayQualityGate && <ProjectCardQualityGate status={measures['alert_status']} />}
+ {displayQualityGate && <ProjectCardQualityGate status={measures!['alert_status']} />}
<div className="pull-right text-right">
{isPrivate && <PrivateBadge className="spacer-left" tooltipPlacement="left" />}
{hasTags && <TagsList tags={project.tags} customClass="spacer-left" />}
</div>
- {isProjectAnalyzed && (
+ {project.analysisDate && (
<div className="project-card-dates note text-right">
<DateTimeFormatter date={project.analysisDate}>
{formattedDate => (
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx
index 5fabe5635fe..4c5a242a638 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx
@@ -17,9 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
-import ProjectCardLanguages from './ProjectCardLanguages';
+import * as React from 'react';
+import ProjectCardLanguagesContainer from './ProjectCardLanguagesContainer';
import Measure from '../../../components/measure/Measure';
import Rating from '../../../components/ui/Rating';
import CoverageRating from '../../../components/ui/CoverageRating';
@@ -27,14 +26,12 @@ import DuplicationsRating from '../../../components/ui/DuplicationsRating';
import SizeRating from '../../../components/ui/SizeRating';
import { translate } from '../../../helpers/l10n';
-/*::
-type Props = {
- measures?: { [string]: string }
-};
-*/
+interface Props {
+ measures?: { [key: string]: string };
+}
-export default function ProjectCardOverallMeasures({ measures } /*: Props */) {
- if (measures == null) {
+export default function ProjectCardOverallMeasures({ measures }: Props) {
+ if (measures == undefined) {
return null;
}
@@ -128,7 +125,9 @@ export default function ProjectCardOverallMeasures({ measures } /*: Props */) {
/>
</div>
<div className="project-card-measure-label">
- <ProjectCardLanguages distribution={measures['ncloc_language_distribution']} />
+ <ProjectCardLanguagesContainer
+ distribution={measures['ncloc_language_distribution']}
+ />
</div>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.tsx
index d73a9223c2c..4843d090df9 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardQualityGate.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.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
import Level from '../../../components/ui/Level';
import Tooltip from '../../../components/controls/Tooltip';
import { formatMeasure } from '../../../helpers/measures';
import { translateWithParameters } from '../../../helpers/l10n';
-export default function ProjectCardQualityGate({ status } /*: { status?: string } */) {
+interface Props {
+ status?: string;
+}
+
+export default function ProjectCardQualityGate({ status }: Props) {
if (!status) {
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
index c44468f8555..08db1a07322 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
@@ -17,26 +17,21 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
import ProjectCardContainer from './ProjectCardContainer';
import NoFavoriteProjects from './NoFavoriteProjects';
import EmptyInstance from './EmptyInstance';
import EmptySearch from '../../../components/common/EmptySearch';
-/*::
-type Props = {
- projects?: Array<string>,
- isFavorite: boolean,
- isFiltered: boolean,
- organization?: { key: string },
- cardType?: string
-};
-*/
-
-export default class ProjectsList extends React.PureComponent {
- /*:: props: Props; */
+interface Props {
+ cardType?: string;
+ isFavorite: boolean;
+ isFiltered: boolean;
+ organization?: { key: string };
+ projects?: string[];
+}
+export default class ProjectsList extends React.PureComponent<Props> {
renderNoProjects() {
if (this.props.isFavorite && !this.props.isFiltered) {
return <NoFavoriteProjects />;
@@ -50,7 +45,7 @@ export default class ProjectsList extends React.PureComponent {
render() {
const { projects } = this.props;
- if (projects == null) {
+ if (projects == undefined) {
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsListContainer.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsListContainer.ts
index f326a8f02c7..76c02ecfc10 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsListContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsListContainer.ts
@@ -21,7 +21,9 @@ import { connect } from 'react-redux';
import ProjectsList from './ProjectsList';
import { getProjects, getProjectsAppState } from '../../../store/rootReducer';
-export default connect(state => ({
+const mapStateToProps = (state: any) => ({
projects: getProjects(state),
total: getProjectsAppState(state).total
-}))(ProjectsList);
+});
+
+export default connect<any, any, any>(mapStateToProps)(ProjectsList);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooter.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooter.js
deleted file mode 100644
index bf478ed0a6d..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooter.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import ListFooter from '../../../components/controls/ListFooter';
-
-export default class ProjectsListFooter extends React.PureComponent {
- static propTypes = {
- total: PropTypes.number.isRequired
- };
-
- render() {
- if (!this.props.total) {
- return null;
- }
-
- return <ListFooter {...this.props} />;
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooterContainer.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooterContainer.ts
index 542be063a7d..fb296456692 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooterContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsListFooterContainer.ts
@@ -20,9 +20,9 @@
import { connect } from 'react-redux';
import { getProjects, getProjectsAppState } from '../../../store/rootReducer';
import { fetchMoreProjects } from '../store/actions';
-import ProjectsListFooter from './ProjectsListFooter';
+import ListFooter from '../../../components/controls/ListFooter';
-const mapStateToProps = state => {
+const mapStateToProps = (state: any) => {
const projects = getProjects(state);
const appState = getProjectsAppState(state);
return {
@@ -32,9 +32,9 @@ const mapStateToProps = state => {
};
};
-const mapDispatchToProps = (dispatch, ownProps) => ({
+const mapDispatchToProps = (dispatch: any, ownProps: any) => ({
loadMore: () =>
dispatch(fetchMoreProjects(ownProps.query, ownProps.isFavorite, ownProps.organization))
});
-export default connect(mapStateToProps, mapDispatchToProps)(ProjectsListFooter);
+export default connect(mapStateToProps, mapDispatchToProps)(ListFooter);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.tsx
index 1465efaa30c..eb84d21caed 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.tsx
@@ -17,75 +17,51 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { sortBy } from 'lodash';
-import Select from 'react-select';
-import ProjectsSortingSelectOption from './ProjectsSortingSelectOption';
+import * as Select from 'react-select';
+import ProjectsSortingSelectOption, { Option } from './ProjectsSortingSelectOption';
import SortAscIcon from '../../../components/icons-components/SortAscIcon';
import SortDescIcon from '../../../components/icons-components/SortDescIcon';
import Tooltip from '../../../components/controls/Tooltip';
import { translate } from '../../../helpers/l10n';
import { SORTING_METRICS, SORTING_LEAK_METRICS, parseSorting } from '../utils';
-/*::
-export type Option = { label: string, value: string, class?: string, short?: string };
-*/
-
-/*::
-type Props = {
- className?: string,
- onChange: (sort: string, desc: boolean) => void,
- selectedSort: string,
- view: string,
- defaultOption: string
-};
-*/
-
-/*::
-type State = {
- sortValue: string,
- sortDesc: boolean
-};
-*/
-
-export default class ProjectsSortingSelect extends React.PureComponent {
- /*:: props: Props; */
- /*:: state: State; */
-
- constructor(props /*: Props */) {
- super(props);
- this.state = parseSorting(props.selectedSort);
- }
+interface Props {
+ className?: string;
+ defaultOption: string;
+ onChange: (sort: string, desc: boolean) => void;
+ selectedSort: string;
+ view: string;
+}
- componentDidUpdate(prevProps /*: Props */) {
- if (prevProps.selectedSort !== this.props.selectedSort) {
- this.setState(parseSorting(this.props.selectedSort));
- }
- }
+export default class ProjectsSortingSelect extends React.PureComponent<Props> {
+ getSorting = () => parseSorting(this.props.selectedSort);
getOptions = () => {
const sortMetrics = this.props.view === 'leak' ? SORTING_LEAK_METRICS : SORTING_METRICS;
- return sortBy(sortMetrics, opt => (opt.value === this.props.defaultOption ? 0 : 1)).map((
- opt /*: { value: string, class?: string } */
- ) => ({
- value: opt.value,
- label: translate('projects.sorting', opt.value),
- class: opt.class
+ return sortBy(
+ sortMetrics,
+ option => (option.value === this.props.defaultOption ? 0 : 1)
+ ).map(option => ({
+ value: option.value,
+ label: translate('projects.sorting', option.value),
+ class: option.class
}));
};
- handleDescToggle = (evt /*: Event & { currentTarget: HTMLElement } */) => {
- evt.preventDefault();
- evt.currentTarget.blur();
- this.props.onChange(this.state.sortValue, !this.state.sortDesc);
+ handleDescToggle = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ const sorting = this.getSorting();
+ this.props.onChange(sorting.sortValue, !sorting.sortDesc);
};
- handleSortChange = (option /*: Option */) =>
- this.props.onChange(option.value, this.state.sortDesc);
+ handleSortChange = (option: Option) =>
+ this.props.onChange(option.value, this.getSorting().sortDesc);
render() {
- const { sortDesc } = this.state;
+ const { sortDesc, sortValue } = this.getSorting();
return (
<div className={this.props.className}>
@@ -97,7 +73,7 @@ export default class ProjectsSortingSelect extends React.PureComponent {
optionComponent={ProjectsSortingSelectOption}
options={this.getOptions()}
searchable={false}
- value={this.state.sortValue}
+ value={sortValue}
/>
<Tooltip
overlay={
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelectOption.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelectOption.tsx
index aebb6bb478b..91553705be1 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelectOption.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelectOption.tsx
@@ -17,36 +17,37 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
-import classNames from 'classnames';
-/*:: import type { Option } from './ProjectsSortingSelect'; */
+import * as React from 'react';
+import * as classNames from 'classnames';
-/*::
-type Props = {
- option: Option,
- children?: Element | Text,
- className?: string,
- isFocused?: boolean,
- onFocus: (Option, MouseEvent) => void,
- onSelect: (Option, MouseEvent) => void
-};
-*/
+export interface Option {
+ label: string;
+ value: string;
+ class?: string;
+ short?: string;
+}
-export default class ProjectsSortingSelectOption extends React.PureComponent {
- /*:: props: Props; */
+interface Props {
+ option: Option;
+ children?: React.ReactNode;
+ className?: string;
+ isFocused?: boolean;
+ onFocus: (option: Option, event: React.SyntheticEvent<HTMLElement>) => void;
+ onSelect: (option: Option, event: React.SyntheticEvent<HTMLElement>) => void;
+}
- handleMouseDown = (event /*: MouseEvent */) => {
+export default class ProjectsSortingSelectOption extends React.PureComponent<Props> {
+ handleMouseDown = (event: React.SyntheticEvent<HTMLElement>) => {
event.preventDefault();
event.stopPropagation();
this.props.onSelect(this.props.option, event);
};
- handleMouseEnter = (event /*: MouseEvent */) => {
+ handleMouseEnter = (event: React.SyntheticEvent<HTMLElement>) => {
this.props.onFocus(this.props.option, event);
};
- handleMouseMove = (event /*: MouseEvent */) => {
+ handleMouseMove = (event: React.SyntheticEvent<HTMLElement>) => {
if (this.props.isFocused) {
return;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
new file mode 100644
index 00000000000..fd16bdac37d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
@@ -0,0 +1,180 @@
+/*
+ * 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.
+ */
+jest.mock('../ProjectsListContainer', () => ({
+ default: function ProjectsListContainer() {
+ return null;
+ }
+}));
+
+jest.mock('../ProjectsListFooterContainer', () => ({
+ default: function ProjectsListFooterContainer() {
+ return null;
+ }
+}));
+jest.mock('../PageHeaderContainer', () => ({
+ default: function PageHeaderContainer() {
+ return null;
+ }
+}));
+
+jest.mock('../PageSidebar', () => ({
+ default: function PageSidebar() {
+ return null;
+ }
+}));
+
+jest.mock('../../../../helpers/storage', () => ({
+ getSort: () => null,
+ getView: jest.fn(() => null),
+ getVisualization: () => null,
+ saveSort: jest.fn(),
+ saveView: jest.fn(),
+ 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';
+
+beforeEach(() => {
+ (getView as jest.Mock<any>).mockImplementation(() => null);
+ (saveSort as jest.Mock<any>).mockClear();
+ (saveView as jest.Mock<any>).mockClear();
+ (saveVisualization as jest.Mock<any>).mockClear();
+});
+
+it('renders', () => {
+ const wrapper = shallow(
+ <AllProjects
+ fetchProjects={jest.fn()}
+ isFavorite={false}
+ location={{ pathname: '/projects', query: {} }}
+ />,
+ { context: { router: {} } }
+ );
+ expect(wrapper).toMatchSnapshot();
+ wrapper.setState({ query: { view: 'visualizations' } });
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('fetches projects', () => {
+ const fetchProjects = jest.fn();
+ mountRender({ fetchProjects });
+ expect(fetchProjects).lastCalledWith(
+ {
+ coverage: null,
+ duplications: null,
+ gate: null,
+ languages: null,
+ maintainability: null,
+ new_coverage: null,
+ new_duplications: null,
+ new_lines: null,
+ new_maintainability: null,
+ new_reliability: null,
+ new_security: null,
+ reliability: null,
+ search: null,
+ security: null,
+ size: null,
+ sort: null,
+ tags: null,
+ view: undefined,
+ visualization: null
+ },
+ false,
+ undefined
+ );
+});
+
+it('redirects to the saved search', () => {
+ (getView as jest.Mock<any>).mockImplementation(() => 'leak');
+ const replace = jest.fn();
+ mountRender({}, jest.fn(), replace);
+ expect(replace).lastCalledWith({ pathname: '/projects', query: { view: 'leak' } });
+});
+
+it('changes sort', () => {
+ const push = jest.fn();
+ const wrapper = mountRender({}, push);
+ wrapper.find('PageHeaderContainer').prop<Function>('onSortChange')('size', false);
+ expect(push).lastCalledWith({ pathname: '/projects', query: { sort: 'size' } });
+ expect(saveSort).lastCalledWith('size');
+});
+
+it('changes perspective to leak', () => {
+ const push = jest.fn();
+ const wrapper = mountRender({}, push);
+ wrapper.find('PageHeaderContainer').prop<Function>('onPerspectiveChange')({ view: 'leak' });
+ expect(push).lastCalledWith({
+ pathname: '/projects',
+ query: { view: 'leak', visualization: undefined }
+ });
+ expect(saveSort).lastCalledWith(undefined);
+ expect(saveView).lastCalledWith('leak');
+ expect(saveVisualization).lastCalledWith(undefined);
+});
+
+it('updates sorting when changing perspective from leak', () => {
+ const push = jest.fn();
+ const wrapper = mountRender(
+ { location: { pathname: '/projects', query: { sort: 'new_coverage', view: 'leak' } } },
+ push
+ );
+ wrapper.find('PageHeaderContainer').prop<Function>('onPerspectiveChange')({
+ view: undefined
+ });
+ expect(push).lastCalledWith({
+ pathname: '/projects',
+ query: { sort: 'coverage', view: undefined, visualization: undefined }
+ });
+ expect(saveSort).lastCalledWith('coverage');
+ expect(saveView).lastCalledWith(undefined);
+ expect(saveVisualization).lastCalledWith(undefined);
+});
+
+it('changes perspective to risk visualization', () => {
+ const push = jest.fn();
+ const wrapper = mountRender({}, push);
+ wrapper.find('PageHeaderContainer').prop<Function>('onPerspectiveChange')({
+ view: 'visualizations',
+ visualization: 'risk'
+ });
+ expect(push).lastCalledWith({
+ pathname: '/projects',
+ query: { view: 'visualizations', visualization: 'risk' }
+ });
+ expect(saveSort).lastCalledWith(undefined);
+ expect(saveView).lastCalledWith('visualizations');
+ expect(saveVisualization).lastCalledWith('risk');
+});
+
+function mountRender(props: any = {}, push: Function = jest.fn(), replace: Function = jest.fn()) {
+ return mount(
+ <AllProjects
+ fetchProjects={jest.fn()}
+ isFavorite={false}
+ location={{ pathname: '/projects', query: {} }}
+ {...props}
+ />,
+ { context: { router: { push, replace } } }
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/DefaultPageSelector-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/DefaultPageSelector-test.tsx
new file mode 100644
index 00000000000..a7af9fce1e6
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/DefaultPageSelector-test.tsx
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+jest.mock('../AllProjectsContainer', () => ({
+ default: function AllProjectsContainer() {
+ return null;
+ }
+}));
+
+jest.mock('../../../../helpers/storage', () => ({
+ isFavoriteSet: jest.fn(),
+ isAllSet: jest.fn()
+}));
+
+jest.mock('../../../../api/components', () => ({
+ searchProjects: jest.fn()
+}));
+
+import * as React from 'react';
+import { mount } from 'enzyme';
+import { UnconnectedDefaultPageSelector } from '../DefaultPageSelector';
+import { doAsync } from '../../../../helpers/testUtils';
+
+const isFavoriteSet = require('../../../../helpers/storage').isFavoriteSet as jest.Mock<any>;
+const isAllSet = require('../../../../helpers/storage').isAllSet as jest.Mock<any>;
+const searchProjects = require('../../../../api/components').searchProjects as jest.Mock<any>;
+
+beforeEach(() => {
+ isFavoriteSet.mockImplementation(() => false).mockClear();
+ isAllSet.mockImplementation(() => false).mockClear();
+});
+
+it('shows all projects with existing filter', () => {
+ const replace = jest.fn();
+ mountRender(undefined, { size: '1' }, replace);
+ expect(replace).not.toBeCalled();
+});
+
+it('shows all projects sorted by analysis date for anonymous', () => {
+ const replace = jest.fn();
+ mountRender({ isLoggedIn: false }, undefined, replace);
+ expect(replace).lastCalledWith({ query: { sort: '-analysis_date' } });
+});
+
+it('shows favorite projects', () => {
+ isFavoriteSet.mockImplementation(() => true);
+ const replace = jest.fn();
+ mountRender(undefined, undefined, replace);
+ expect(replace).lastCalledWith({ pathname: '/projects/favorite', query: {} });
+});
+
+it('shows all projects', () => {
+ isAllSet.mockImplementation(() => true);
+ const replace = jest.fn();
+ mountRender(undefined, undefined, replace);
+ expect(replace).not.toBeCalled();
+});
+
+it('fetches favorites', () => {
+ searchProjects.mockImplementation(() => Promise.resolve({ paging: { total: 3 } }));
+ const replace = jest.fn();
+ mountRender(undefined, undefined, replace);
+ return doAsync().then(() => {
+ expect(searchProjects).toHaveBeenLastCalledWith({ filter: 'isFavorite', ps: 1 });
+ expect(replace).toBeCalledWith({ pathname: '/projects/favorite', query: {} });
+ });
+});
+
+function mountRender(user: any = { isLoggedIn: true }, query: any = {}, replace: any = jest.fn()) {
+ return mount(<UnconnectedDefaultPageSelector currentUser={user} location={{ query }} />, {
+ context: { router: { replace } }
+ });
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx
new file mode 100644
index 00000000000..2f9fe7f31f8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/EmptyInstance-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import EmptyInstance from '../EmptyInstance';
+
+it('renders', () => {
+ expect(shallow(<EmptyInstance />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/FavoriteFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/FavoriteFilter-test.tsx
new file mode 100644
index 00000000000..49e81f03b90
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/FavoriteFilter-test.tsx
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+jest.mock('../../../../helpers/storage', () => ({
+ saveAll: jest.fn(),
+ saveFavorite: jest.fn()
+}));
+
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import FavoriteFilter from '../FavoriteFilter';
+import { saveAll, saveFavorite } from '../../../../helpers/storage';
+import { click } from '../../../../helpers/testUtils';
+
+const user = { isLoggedIn: true };
+const query = { size: 1 };
+
+beforeEach(() => {
+ (saveAll as jest.Mock<any>).mockClear();
+ (saveFavorite as jest.Mock<any>).mockClear();
+});
+
+it('renders for logged in user', () => {
+ expect(shallow(<FavoriteFilter query={query} user={user} />)).toMatchSnapshot();
+});
+
+it('saves last selection', () => {
+ const wrapper = shallow(<FavoriteFilter query={query} user={user} />);
+ click(wrapper.find('#favorite-projects'));
+ expect(saveFavorite).toBeCalled();
+ click(wrapper.find('#all-projects'));
+ expect(saveAll).toBeCalled();
+});
+
+it('handles organization', () => {
+ expect(
+ shallow(<FavoriteFilter organization={{ key: 'org' }} query={query} user={user} />)
+ ).toMatchSnapshot();
+});
+
+it('does not save last selection with organization', () => {
+ const wrapper = shallow(
+ <FavoriteFilter organization={{ key: 'org' }} query={query} user={user} />
+ );
+ click(wrapper.find('#favorite-projects'));
+ expect(saveFavorite).not.toBeCalled();
+ click(wrapper.find('#all-projects'));
+ expect(saveAll).not.toBeCalled();
+});
+
+it('does not render for anonymous', () => {
+ expect(shallow(<FavoriteFilter query={query} user={{ isLoggedIn: false }} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx
new file mode 100644
index 00000000000..a11013b31d7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import NoFavoriteProjects from '../NoFavoriteProjects';
+
+it('renders', () => {
+ expect(shallow(<NoFavoriteProjects />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx
index 0a9b1b2aca0..48e9a86b3b2 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx
@@ -17,31 +17,21 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import PageHeader from '../PageHeader';
it('should render correctly', () => {
- expect(
- shallow(<PageHeader query={{ search: 'test' }} projectsAppState={{ total: 12 }} />)
- ).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
});
it('should render correctly while loading', () => {
- expect(
- shallow(
- <PageHeader
- query={{ search: '' }}
- isFavorite={true}
- projectsAppState={{ loading: true, total: 2 }}
- />
- )
- ).toMatchSnapshot();
+ expect(shallowRender({ projectsAppState: { loading: true, total: 2 } })).toMatchSnapshot();
});
it('should not render projects total', () => {
expect(
- shallow(<PageHeader projectsAppState={{}} query={{ search: '' }} />)
+ shallowRender({ projectsAppState: {} })
.find('#projects-total')
.exists()
).toBeFalsy();
@@ -49,38 +39,47 @@ it('should not render projects total', () => {
it('should render disabled sorting options for visualizations', () => {
expect(
- shallow(
- <PageHeader
- open={true}
- projectsAppState={{}}
- view="visualizations"
- visualization="coverage"
- />
- )
+ shallowRender({
+ open: true,
+ projectsAppState: {},
+ view: 'visualizations',
+ visualization: 'coverage'
+ })
).toMatchSnapshot();
});
it('should render switch the default sorting option for anonymous users', () => {
expect(
- shallow(
- <PageHeader
- currentUser={{ isLoggedIn: true }}
- open={true}
- projectsAppState={{}}
- view="overall"
- visualization="risk"
- />
- ).find('ProjectsSortingSelect')
+ shallowRender({
+ currentUser: { isLoggedIn: true },
+ open: true,
+ projectsAppState: {},
+ visualization: 'risk'
+ }).find('ProjectsSortingSelect')
).toMatchSnapshot();
+
expect(
- shallow(
- <PageHeader
- currentUser={{ isLoggedIn: false }}
- open={true}
- projectsAppState={{}}
- view="leak"
- visualization="risk"
- />
- ).find('ProjectsSortingSelect')
+ shallowRender({
+ currentUser: { isLoggedIn: false },
+ open: true,
+ projectsAppState: {},
+ view: 'leak',
+ visualization: 'risk'
+ }).find('ProjectsSortingSelect')
).toMatchSnapshot();
});
+
+function shallowRender(props?: any) {
+ return shallow(
+ <PageHeader
+ onPerspectiveChange={jest.fn()}
+ onSortChange={jest.fn()}
+ projects={[]}
+ projectsAppState={{ loading: false, total: 12 }}
+ query={{ search: 'test' }}
+ selectedSort="size"
+ view="overall"
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx
index fd396c25efe..6d071b47da5 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx
@@ -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 React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import PageSidebar from '../PageSidebar';
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.tsx
index a1a49c3ea84..8cb10d0fe2e 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelect-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.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import PerspectiveSelect from '../PerspectiveSelect';
it('should render correctly', () => {
- expect(shallow(<PerspectiveSelect view="overall" />)).toMatchSnapshot();
+ expect(shallow(<PerspectiveSelect onChange={jest.fn()} view="overall" />)).toMatchSnapshot();
});
it('should render with coverage selected', () => {
expect(
- shallow(<PerspectiveSelect view="visualizations" visualization="coverage" />)
+ shallow(
+ <PerspectiveSelect onChange={jest.fn()} view="visualizations" visualization="coverage" />
+ )
).toMatchSnapshot();
});
@@ -35,9 +37,9 @@ it('should handle perspective change correctly', () => {
const onChange = jest.fn();
const instance = shallow(
<PerspectiveSelect view="visualizations" visualization="coverage" onChange={onChange} />
- ).instance();
- instance.handleChange({ value: 'overall', type: 'view' });
- instance.handleChange({ value: 'leak', type: 'view' });
- instance.handleChange({ value: 'coverage', type: 'visualization' });
+ ).instance() as PerspectiveSelect;
+ instance.handleChange({ label: 'overall', value: 'overall', type: 'view' });
+ instance.handleChange({ label: 'leak', value: 'leak', type: 'view' });
+ instance.handleChange({ label: 'coverage', value: 'coverage', type: 'visualization' });
expect(onChange.mock.calls).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelectOption-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelectOption-test.tsx
new file mode 100644
index 00000000000..f0aaeeffbbf
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelectOption-test.tsx
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import PerspectiveSelectOption from '../PerspectiveSelectOption';
+
+it('should render correctly for a view', () => {
+ expect(
+ shallow(
+ <PerspectiveSelectOption
+ onFocus={jest.fn()}
+ onSelect={jest.fn()}
+ option={{ value: 'overall', type: 'view', label: 'Overall' }}>
+ Overall
+ </PerspectiveSelectOption>
+ )
+ ).toMatchSnapshot();
+});
+
+it('should render correctly for a visualization', () => {
+ expect(
+ shallow(
+ <PerspectiveSelectOption
+ onFocus={jest.fn()}
+ onSelect={jest.fn()}
+ option={{ value: 'coverage', type: 'visualization', label: 'Coverage' }}>
+ Coverage
+ </PerspectiveSelectOption>
+ )
+ ).toMatchSnapshot();
+});
+
+it('selects option', () => {
+ const onSelect = jest.fn();
+ const option = { value: 'coverage', type: 'visualization', label: 'Coverage' };
+ const wrapper = shallow(
+ <PerspectiveSelectOption onFocus={jest.fn()} onSelect={onSelect} option={option} />
+ );
+ const event = { stopPropagation() {}, preventDefault() {} };
+ wrapper.simulate('mousedown', event);
+ expect(onSelect).toBeCalledWith(option, event);
+});
+
+it('focuses option', () => {
+ const onFocus = jest.fn();
+ const option = { value: 'coverage', type: 'visualization', label: 'Coverage' };
+ const wrapper = shallow(
+ <PerspectiveSelectOption onFocus={onFocus} onSelect={jest.fn()} option={option} />
+ );
+ const event = { stopPropagation() {}, preventDefault() {} };
+
+ wrapper.simulate('mouseenter', event);
+ expect(onFocus).toBeCalledWith(option, event);
+
+ onFocus.mockClear();
+ wrapper.simulate('mousemove', event);
+ expect(onFocus).toBeCalledWith(option, event);
+
+ onFocus.mockClear();
+ wrapper.setProps({ isFocused: true });
+ wrapper.simulate('mousemove', event);
+ expect(onFocus).not.toBeCalled();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLanguages-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLanguages-test.tsx
new file mode 100644
index 00000000000..76b724f3bce
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLanguages-test.tsx
@@ -0,0 +1,53 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import ProjectCardLanguages from '../ProjectCardLanguages';
+
+const languages = {
+ java: { key: 'java', name: 'Java' },
+ js: { key: 'js', name: 'JavaScript' }
+};
+
+it('renders', () => {
+ expect(
+ shallow(<ProjectCardLanguages distribution="java=137;js=15" languages={languages} />)
+ ).toMatchSnapshot();
+});
+
+it('sorts languages', () => {
+ expect(
+ shallow(<ProjectCardLanguages distribution="java=13;js=152" languages={languages} />)
+ ).toMatchSnapshot();
+});
+
+it('handles unknown languages', () => {
+ expect(
+ shallow(<ProjectCardLanguages distribution="java=13;cpp=18" languages={languages} />)
+ ).toMatchSnapshot();
+
+ expect(
+ shallow(<ProjectCardLanguages distribution="java=13;<null>=18" languages={languages} />)
+ ).toMatchSnapshot();
+});
+
+it('does not render', () => {
+ expect(shallow(<ProjectCardLanguages languages={languages} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx
index 7be476e780e..41b383216dc 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx
@@ -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 React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import ProjectCardLeak from '../ProjectCardLeak';
@@ -32,11 +32,11 @@ const MEASURES = {
alert_status: 'OK',
reliability_rating: '1.0',
sqale_rating: '1.0',
- new_bugs: 12
+ new_bugs: '12'
};
it('should display analysis date and leak start date', () => {
- const card = shallow(<ProjectCardLeak type="leak" measures={MEASURES} project={PROJECT} />);
+ const card = shallow(<ProjectCardLeak measures={MEASURES} project={PROJECT} />);
expect(card.find('.project-card-dates').exists()).toBeTruthy();
expect(card.find('.project-card-dates').find('DateFromNow')).toHaveLength(1);
expect(card.find('.project-card-dates').find('DateTimeFormatter')).toHaveLength(1);
@@ -44,14 +44,14 @@ it('should display analysis date and leak start date', () => {
it('should not display analysis date or leak start date', () => {
const project = { ...PROJECT, analysisDate: undefined };
- const card = shallow(<ProjectCardLeak type="leak" measures={MEASURES} project={project} />);
+ const card = shallow(<ProjectCardLeak measures={MEASURES} project={project} />);
expect(card.find('.project-card-dates').exists()).toBeFalsy();
});
it('should display loading', () => {
- const measures = { ...MEASURES, new_bugs: undefined };
+ const measures = { alert_status: 'OK', reliability_rating: '1.0', sqale_rating: '1.0' };
expect(
- shallow(<ProjectCardLeak type="leak" measures={measures} project={PROJECT} />)
+ shallow(<ProjectCardLeak measures={measures} project={PROJECT} />)
.find('.boxed-group')
.hasClass('boxed-group-loading')
).toBeTruthy();
@@ -60,7 +60,7 @@ it('should display loading', () => {
it('should display tags', () => {
const project = { ...PROJECT, tags: ['foo', 'bar'] };
expect(
- shallow(<ProjectCardLeak type="leak" project={project} />)
+ shallow(<ProjectCardLeak project={project} />)
.find('TagsList')
.exists()
).toBeTruthy();
@@ -69,14 +69,12 @@ it('should display tags', () => {
it('should private badge', () => {
const project = { ...PROJECT, visibility: 'private' };
expect(
- shallow(<ProjectCardLeak type="leak" project={project} />)
+ shallow(<ProjectCardLeak project={project} />)
.find('PrivateBadge')
.exists()
).toBeTruthy();
});
it('should display the leak measures and quality gate', () => {
- expect(
- shallow(<ProjectCardLeak type="leak" measures={MEASURES} project={PROJECT} />)
- ).toMatchSnapshot();
+ expect(shallow(<ProjectCardLeak measures={MEASURES} project={PROJECT} />)).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.tsx
index 5003e421267..5284ac031d1 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeakMeasures-test.tsx
@@ -17,38 +17,37 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import ProjectCardLeakMeasures from '../ProjectCardLeakMeasures';
-const measures = {
- alert_status: 'ERROR',
- new_reliability_rating: '1.0',
- new_bugs: '8',
- new_security_rating: '2.0',
- new_vulnerabilities: '2',
- new_maintainability_rating: '1.0',
- new_code_smells: '0',
- new_coverage: '26.55',
- new_duplicated_lines_density: '0.55',
- new_lines: '87'
-};
-
it('should render correctly with all data', () => {
+ const measures = {
+ alert_status: 'ERROR',
+ new_reliability_rating: '1.0',
+ new_bugs: '8',
+ new_security_rating: '2.0',
+ new_vulnerabilities: '2',
+ new_maintainability_rating: '1.0',
+ new_code_smells: '0',
+ new_coverage: '26.55',
+ new_duplicated_lines_density: '0.55',
+ new_lines: '87'
+ };
const wrapper = shallow(<ProjectCardLeakMeasures measures={measures} />);
expect(wrapper).toMatchSnapshot();
});
it('should render no data style new coverage, new duplications and new lines', () => {
- const wrapper = shallow(
- <ProjectCardLeakMeasures
- measures={{
- ...measures,
- new_coverage: undefined,
- new_duplicated_lines_density: undefined,
- new_lines: undefined
- }}
- />
- );
+ const measures = {
+ alert_status: 'ERROR',
+ new_reliability_rating: '1.0',
+ new_bugs: '8',
+ new_security_rating: '2.0',
+ new_vulnerabilities: '2',
+ new_maintainability_rating: '1.0',
+ new_code_smells: '0'
+ };
+ const wrapper = shallow(<ProjectCardLeakMeasures measures={measures} />);
expect(wrapper).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.tsx
index e51b333a35e..a1dcfb897b8 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.tsx
@@ -17,13 +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.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import ProjectCardOverall from '../ProjectCardOverall';
const PROJECT = {
analysisDate: '2017-01-01',
- leakPeriodDate: '2016-12-01',
key: 'foo',
name: 'Foo',
tags: []
@@ -32,7 +31,7 @@ const MEASURES = {
alert_status: 'OK',
reliability_rating: '1.0',
sqale_rating: '1.0',
- new_bugs: 12
+ new_bugs: '12'
};
it('should display analysis date (and not leak period) when defined', () => {
@@ -49,14 +48,13 @@ it('should display analysis date (and not leak period) when defined', () => {
});
it('should display loading', () => {
- const measures = { ...MEASURES, sqale_rating: undefined };
expect(
shallow(<ProjectCardOverall project={PROJECT} />)
.find('.boxed-group')
.hasClass('boxed-group-loading')
).toBeTruthy();
expect(
- shallow(<ProjectCardOverall measures={measures} project={PROJECT} />)
+ shallow(<ProjectCardOverall measures={{ sqale_rating: '1.0' }} project={PROJECT} />)
.find('.boxed-group')
.hasClass('boxed-group-loading')
).toBeTruthy();
@@ -83,7 +81,7 @@ it('should display tags', () => {
it('should private badge', () => {
const project = { ...PROJECT, visibility: 'private' };
expect(
- shallow(<ProjectCardOverall type="overall" project={project} />)
+ shallow(<ProjectCardOverall project={project} />)
.find('PrivateBadge')
.exists()
).toBeTruthy();
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx
index 7ee12ea5dfa..ef032d7568f 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx
@@ -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 React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import ProjectCardOverallMeasures from '../ProjectCardOverallMeasures';
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardQualityGate-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardQualityGate-test.tsx
new file mode 100644
index 00000000000..1a0f549c763
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardQualityGate-test.tsx
@@ -0,0 +1,30 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import ProjectCardQualityGate from '../ProjectCardQualityGate';
+
+it('renders', () => {
+ expect(shallow(<ProjectCardQualityGate status="ERROR" />)).toMatchSnapshot();
+});
+
+it('does not render', () => {
+ expect(shallow(<ProjectCardQualityGate />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsList-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsList-test.tsx
new file mode 100644
index 00000000000..d15a866c1cb
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsList-test.tsx
@@ -0,0 +1,48 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import ProjectsList from '../ProjectsList';
+
+it('renders', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+it('does not render without projects', () => {
+ expect(shallow(<ProjectsList isFavorite={false} isFiltered={false} />)).toMatchSnapshot();
+});
+
+it('renders different types of "no projects"', () => {
+ expect(shallowRender({ projects: [] })).toMatchSnapshot();
+ expect(shallowRender({ projects: [], isFiltered: true })).toMatchSnapshot();
+ expect(shallowRender({ projects: [], isFavorite: true })).toMatchSnapshot();
+});
+
+function shallowRender(props?: any) {
+ return shallow(
+ <ProjectsList
+ cardType="overall"
+ isFavorite={false}
+ isFiltered={false}
+ projects={['foo', 'bar']}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelect-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelect-test.tsx
index 4906bf180b2..649d9bec8e3 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelect-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelect-test.tsx
@@ -17,13 +17,21 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import ProjectsSortingSelect from '../ProjectsSortingSelect';
+import { click } from '../../../../helpers/testUtils';
it('should render correctly for overall view', () => {
expect(
- shallow(<ProjectsSortingSelect selectedSort="name" view="overall" defaultOption="name" />)
+ shallow(
+ <ProjectsSortingSelect
+ defaultOption="name"
+ onChange={jest.fn()}
+ selectedSort="name"
+ view="overall"
+ />
+ )
).toMatchSnapshot();
});
@@ -31,9 +39,10 @@ it('should render correctly for leak view', () => {
expect(
shallow(
<ProjectsSortingSelect
+ defaultOption="analysis_date"
+ onChange={jest.fn()}
selectedSort="new_coverage"
view="leak"
- defaultOption="analysis_date"
/>
)
).toMatchSnapshot();
@@ -42,7 +51,40 @@ it('should render correctly for leak view', () => {
it('should handle the descending sort direction', () => {
expect(
shallow(
- <ProjectsSortingSelect selectedSort="-vulnerability" view="overall" defaultOption="name" />
+ <ProjectsSortingSelect
+ defaultOption="name"
+ onChange={jest.fn()}
+ selectedSort="-vulnerability"
+ view="overall"
+ />
)
).toMatchSnapshot();
});
+
+it('changes sorting', () => {
+ const onChange = jest.fn();
+ const instance = shallow(
+ <ProjectsSortingSelect
+ defaultOption="name"
+ onChange={onChange}
+ selectedSort="-vulnerabilities"
+ view="overall"
+ />
+ ).instance() as ProjectsSortingSelect;
+ instance.handleSortChange({ label: 'size', value: 'size' });
+ expect(onChange).toBeCalledWith('size', true);
+});
+
+it('reverses sorting', () => {
+ const onChange = jest.fn();
+ const wrapper = shallow(
+ <ProjectsSortingSelect
+ defaultOption="name"
+ onChange={onChange}
+ selectedSort="-size"
+ view="overall"
+ />
+ );
+ click(wrapper.find('a'));
+ expect(onChange).toBeCalledWith('size', false);
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelectOption-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelectOption-test.tsx
new file mode 100644
index 00000000000..22d8d49653b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectsSortingSelectOption-test.tsx
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import ProjectsSortingSelectOption from '../ProjectsSortingSelectOption';
+
+it('renders', () => {
+ expect(
+ shallow(
+ <ProjectsSortingSelectOption
+ onFocus={jest.fn()}
+ onSelect={jest.fn()}
+ option={{ label: 'Size', value: 'size' }}>
+ Size
+ </ProjectsSortingSelectOption>
+ )
+ ).toMatchSnapshot();
+});
+
+it('renders short', () => {
+ expect(
+ shallow(
+ <ProjectsSortingSelectOption
+ onFocus={jest.fn()}
+ onSelect={jest.fn()}
+ option={{ label: 'Size', short: 'Short', value: 'size' }}>
+ Size
+ </ProjectsSortingSelectOption>
+ )
+ ).toMatchSnapshot();
+});
+
+it('selects option', () => {
+ const onSelect = jest.fn();
+ const option = { value: 'coverage', type: 'visualization', label: 'Coverage' };
+ const wrapper = shallow(
+ <ProjectsSortingSelectOption onFocus={jest.fn()} onSelect={onSelect} option={option} />
+ );
+ const event = { stopPropagation() {}, preventDefault() {} };
+ wrapper.simulate('mousedown', event);
+ expect(onSelect).toBeCalledWith(option, event);
+});
+
+it('focuses option', () => {
+ const onFocus = jest.fn();
+ const option = { value: 'coverage', type: 'visualization', label: 'Coverage' };
+ const wrapper = shallow(
+ <ProjectsSortingSelectOption onFocus={onFocus} onSelect={jest.fn()} option={option} />
+ );
+ const event = { stopPropagation() {}, preventDefault() {} };
+
+ wrapper.simulate('mouseenter', event);
+ expect(onFocus).toBeCalledWith(option, event);
+
+ onFocus.mockClear();
+ wrapper.simulate('mousemove', event);
+ expect(onFocus).toBeCalledWith(option, event);
+
+ onFocus.mockClear();
+ wrapper.setProps({ isFocused: true });
+ wrapper.simulate('mousemove', event);
+ expect(onFocus).not.toBeCalled();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
new file mode 100644
index 00000000000..70ce07e9dab
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
@@ -0,0 +1,155 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="layout-page projects-page"
+>
+ <HelmetWrapper
+ encodeSpecialCharacters={true}
+ title="projects.page"
+ />
+ <div
+ className="layout-page-side-outer"
+ >
+ <div
+ className="layout-page-side projects-page-side"
+ style={
+ Object {
+ "top": 30,
+ }
+ }
+ >
+ <div
+ className="layout-page-side-inner"
+ >
+ <div
+ className="layout-page-filters"
+ >
+ <PageSidebar
+ isFavorite={false}
+ query={Object {}}
+ view="overall"
+ visualization="risk"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div
+ className="layout-page-main projects-page-content"
+ >
+ <div
+ className="layout-page-header-panel layout-page-main-header"
+ >
+ <div
+ className="layout-page-header-panel-inner layout-page-main-header-inner"
+ >
+ <div
+ className="layout-page-main-inner"
+ >
+ <PageHeaderContainer
+ isFavorite={false}
+ onPerspectiveChange={[Function]}
+ onSortChange={[Function]}
+ query={Object {}}
+ selectedSort="name"
+ view="overall"
+ visualization="risk"
+ />
+ </div>
+ </div>
+ </div>
+ <div
+ className="layout-page-main-inner"
+ >
+ <ProjectsListContainer
+ cardType="overall"
+ isFavorite={false}
+ isFiltered={false}
+ />
+ <ProjectsListFooterContainer
+ isFavorite={false}
+ query={Object {}}
+ />
+ </div>
+ </div>
+</div>
+`;
+
+exports[`renders 2`] = `
+<div
+ className="layout-page projects-page"
+>
+ <HelmetWrapper
+ encodeSpecialCharacters={true}
+ title="projects.page"
+ />
+ <div
+ className="layout-page-side-outer"
+ >
+ <div
+ className="layout-page-side projects-page-side"
+ style={
+ Object {
+ "top": 30,
+ }
+ }
+ >
+ <div
+ className="layout-page-side-inner"
+ >
+ <div
+ className="layout-page-filters"
+ >
+ <PageSidebar
+ isFavorite={false}
+ query={
+ Object {
+ "view": "visualizations",
+ }
+ }
+ view="visualizations"
+ visualization="risk"
+ />
+ </div>
+ </div>
+ </div>
+ </div>
+ <div
+ className="layout-page-main projects-page-content"
+ >
+ <div
+ className="layout-page-header-panel layout-page-main-header"
+ >
+ <div
+ className="layout-page-header-panel-inner layout-page-main-header-inner"
+ >
+ <div
+ className="layout-page-main-inner"
+ >
+ <PageHeaderContainer
+ isFavorite={false}
+ onPerspectiveChange={[Function]}
+ onSortChange={[Function]}
+ query={
+ Object {
+ "view": "visualizations",
+ }
+ }
+ selectedSort="name"
+ view="visualizations"
+ visualization="risk"
+ />
+ </div>
+ </div>
+ </div>
+ <div
+ className="layout-page-main-inner"
+ >
+ <Connect(Visualizations)
+ visualization="risk"
+ />
+ </div>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/EmptyInstance-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/EmptyInstance-test.tsx.snap
new file mode 100644
index 00000000000..df541164173
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/EmptyInstance-test.tsx.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="projects-empty-list"
+>
+ <h3>
+ projects.no_projects.empty_instance
+ </h3>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/FavoriteFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/FavoriteFilter-test.tsx.snap
new file mode 100644
index 00000000000..411ec45c0b0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/FavoriteFilter-test.tsx.snap
@@ -0,0 +1,93 @@
+// 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"
+>
+ <div
+ className="button-group"
+ >
+ <Link
+ activeClassName="button-active"
+ className="button"
+ id="favorite-projects"
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/projects/favorite",
+ "query": Object {
+ "size": 1,
+ },
+ }
+ }
+ >
+ my_favorites
+ </Link>
+ <IndexLink
+ activeClassName="button-active"
+ className="button"
+ id="all-projects"
+ onClick={[Function]}
+ to={
+ Object {
+ "pathname": "/organizations/org/projects",
+ "query": Object {
+ "size": 1,
+ },
+ }
+ }
+ >
+ all
+ </IndexLink>
+ </div>
+</header>
+`;
+
+exports[`renders for logged in user 1`] = `
+<header
+ className="page-header text-center"
+>
+ <div
+ className="button-group"
+ >
+ <Link
+ activeClassName="button-active"
+ className="button"
+ id="favorite-projects"
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects/favorite",
+ "query": Object {
+ "size": 1,
+ },
+ }
+ }
+ >
+ my_favorites
+ </Link>
+ <IndexLink
+ activeClassName="button-active"
+ className="button"
+ id="all-projects"
+ onClick={[Function]}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "size": 1,
+ },
+ }
+ }
+ >
+ all
+ </IndexLink>
+ </div>
+</header>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap
new file mode 100644
index 00000000000..3b6652f10f8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/NoFavoriteProjects-test.tsx.snap
@@ -0,0 +1,28 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="projects-empty-list"
+>
+ <h3>
+ projects.no_favorite_projects
+ </h3>
+ <p
+ className="big-spacer-top"
+ >
+ projects.no_favorite_projects.engagement
+ </p>
+ <p
+ className="big-spacer-top"
+ >
+ <Link
+ className="button"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="/projects/all"
+ >
+ projects.explore_projects
+ </Link>
+ </p>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
index 17c8eddd16c..f5561258330 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
@@ -6,12 +6,17 @@ exports[`should render correctly 1`] = `
>
<PerspectiveSelect
className="projects-topbar-item js-projects-perspective-select"
+ onChange={[Function]}
+ view="overall"
/>
<ProjectsSortingSelect
className="projects-topbar-item js-projects-sorting-select"
defaultOption="analysis_date"
+ onChange={[Function]}
+ selectedSort="size"
+ view="overall"
/>
- <withRouter(SearchFilterContainer)
+ <SearchFilterContainer
className="projects-topbar-item projects-topbar-item-search"
query={
Object {
@@ -41,17 +46,21 @@ exports[`should render correctly while loading 1`] = `
>
<PerspectiveSelect
className="projects-topbar-item js-projects-perspective-select"
+ onChange={[Function]}
+ view="overall"
/>
<ProjectsSortingSelect
className="projects-topbar-item js-projects-sorting-select"
defaultOption="analysis_date"
+ onChange={[Function]}
+ selectedSort="size"
+ view="overall"
/>
- <withRouter(SearchFilterContainer)
+ <SearchFilterContainer
className="projects-topbar-item projects-topbar-item-search"
- isFavorite={true}
query={
Object {
- "search": "",
+ "search": "test",
}
}
/>
@@ -80,6 +89,7 @@ exports[`should render disabled sorting options for visualizations 1`] = `
>
<PerspectiveSelect
className="projects-topbar-item js-projects-perspective-select"
+ onChange={[Function]}
view="visualizations"
visualization="coverage"
/>
@@ -93,12 +103,19 @@ exports[`should render disabled sorting options for visualizations 1`] = `
<ProjectsSortingSelect
className="js-projects-sorting-select"
defaultOption="analysis_date"
+ onChange={[Function]}
+ selectedSort="size"
view="visualizations"
/>
</div>
</Tooltip>
- <withRouter(SearchFilterContainer)
+ <SearchFilterContainer
className="projects-topbar-item projects-topbar-item-search"
+ query={
+ Object {
+ "search": "test",
+ }
+ }
/>
<div
className="projects-topbar-item is-last"
@@ -110,6 +127,8 @@ exports[`should render switch the default sorting option for anonymous users 1`]
<ProjectsSortingSelect
className="projects-topbar-item js-projects-sorting-select"
defaultOption="name"
+ onChange={[Function]}
+ selectedSort="size"
view="overall"
/>
`;
@@ -118,6 +137,8 @@ exports[`should render switch the default sorting option for anonymous users 2`]
<ProjectsSortingSelect
className="projects-topbar-item js-projects-sorting-select"
defaultOption="analysis_date"
+ onChange={[Function]}
+ selectedSort="size"
view="leak"
/>
`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.tsx.snap
index d48dbb01e6d..aa067968a43 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.tsx.snap
@@ -91,14 +91,13 @@ exports[`should render \`leak\` view correctly 1`] = `
/>
<NewLinesFilter
isFavorite={false}
- property="new_lines"
query={
Object {
"view": "leak",
}
}
/>
- <Connect(withRouter(LanguagesFilter))
+ <Connect(LanguagesFilter)
isFavorite={false}
query={
Object {
@@ -106,7 +105,7 @@ exports[`should render \`leak\` view correctly 1`] = `
}
}
/>
- <Connect(withRouter(TagsFilter))
+ <Connect(TagsFilter)
isFavorite={false}
query={
Object {
@@ -178,7 +177,6 @@ exports[`should render correctly 1`] = `
/>
<CoverageFilter
isFavorite={true}
- property="coverage"
query={
Object {
"size": "3",
@@ -187,7 +185,6 @@ exports[`should render correctly 1`] = `
/>
<DuplicationsFilter
isFavorite={true}
- property="duplications"
query={
Object {
"size": "3",
@@ -196,14 +193,13 @@ exports[`should render correctly 1`] = `
/>
<SizeFilter
isFavorite={true}
- property="size"
query={
Object {
"size": "3",
}
}
/>
- <Connect(withRouter(LanguagesFilter))
+ <Connect(LanguagesFilter)
isFavorite={true}
query={
Object {
@@ -211,7 +207,7 @@ exports[`should render correctly 1`] = `
}
}
/>
- <Connect(withRouter(TagsFilter))
+ <Connect(TagsFilter)
isFavorite={true}
query={
Object {
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.tsx.snap
index 7671abced29..7671abced29 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelect-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelectOption-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelectOption-test.tsx.snap
index 794349a21ca..794349a21ca 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelectOption-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PerspectiveSelectOption-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap
new file mode 100644
index 00000000000..bf50df65a84
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap
@@ -0,0 +1,107 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`does not render 1`] = `null`;
+
+exports[`handles unknown languages 1`] = `
+<div
+ className="project-card-languages"
+>
+ <Tooltip
+ overlay={
+ <span>
+ <span>
+ cpp
+ <br />
+ </span>
+ <span>
+ Java
+ <br />
+ </span>
+ </span>
+ }
+ placement="bottom"
+ >
+ <span>
+ cpp, Java
+ </span>
+ </Tooltip>
+</div>
+`;
+
+exports[`handles unknown languages 2`] = `
+<div
+ className="project-card-languages"
+>
+ <Tooltip
+ overlay={
+ <span>
+ <span>
+ unknown
+ <br />
+ </span>
+ <span>
+ Java
+ <br />
+ </span>
+ </span>
+ }
+ placement="bottom"
+ >
+ <span>
+ unknown, Java
+ </span>
+ </Tooltip>
+</div>
+`;
+
+exports[`renders 1`] = `
+<div
+ className="project-card-languages"
+>
+ <Tooltip
+ overlay={
+ <span>
+ <span>
+ Java
+ <br />
+ </span>
+ <span>
+ JavaScript
+ <br />
+ </span>
+ </span>
+ }
+ placement="bottom"
+ >
+ <span>
+ Java, JavaScript
+ </span>
+ </Tooltip>
+</div>
+`;
+
+exports[`sorts languages 1`] = `
+<div
+ className="project-card-languages"
+>
+ <Tooltip
+ overlay={
+ <span>
+ <span>
+ JavaScript
+ <br />
+ </span>
+ <span>
+ Java
+ <br />
+ </span>
+ </span>
+ }
+ placement="bottom"
+ >
+ <span>
+ JavaScript, Java
+ </span>
+ </Tooltip>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.tsx.snap
index 91275b64048..cc6ae61aca6 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.tsx.snap
@@ -50,7 +50,7 @@ exports[`should display the leak measures and quality gate 1`] = `
measures={
Object {
"alert_status": "OK",
- "new_bugs": 12,
+ "new_bugs": "12",
"reliability_rating": "1.0",
"sqale_rating": "1.0",
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap
index bbce939030e..1c98e6df82e 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.tsx.snap
@@ -28,8 +28,6 @@ exports[`should render correctly with all data 1`] = `
}
/>
<Rating
- muted={false}
- small={false}
value="1.0"
/>
</div>
@@ -67,8 +65,6 @@ exports[`should render correctly with all data 1`] = `
}
/>
<Rating
- muted={false}
- small={false}
value="2.0"
/>
</div>
@@ -106,8 +102,6 @@ exports[`should render correctly with all data 1`] = `
}
/>
<Rating
- muted={false}
- small={false}
value="1.0"
/>
</div>
@@ -242,8 +236,6 @@ exports[`should render no data style new coverage, new duplications and new line
}
/>
<Rating
- muted={false}
- small={false}
value="1.0"
/>
</div>
@@ -281,8 +273,6 @@ exports[`should render no data style new coverage, new duplications and new line
}
/>
<Rating
- muted={false}
- small={false}
value="2.0"
/>
</div>
@@ -320,8 +310,6 @@ exports[`should render no data style new coverage, new duplications and new line
}
/>
<Rating
- muted={false}
- small={false}
value="1.0"
/>
</div>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.tsx.snap
index 720f0f20129..243bf08788d 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.tsx.snap
@@ -47,7 +47,7 @@ exports[`should display the overall measures and quality gate 1`] = `
measures={
Object {
"alert_status": "OK",
- "new_bugs": 12,
+ "new_bugs": "12",
"reliability_rating": "1.0",
"sqale_rating": "1.0",
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap
index 2cf247a1451..d16171e7345 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap
@@ -81,8 +81,6 @@ exports[`should render correctly with all data 1`] = `
className="project-card-measure-number"
>
<Rating
- muted={false}
- small={false}
value="1.0"
/>
</div>
@@ -104,8 +102,6 @@ exports[`should render correctly with all data 1`] = `
className="project-card-measure-number"
>
<Rating
- muted={false}
- small={false}
value="1.0"
/>
</div>
@@ -127,8 +123,6 @@ exports[`should render correctly with all data 1`] = `
className="project-card-measure-number"
>
<Rating
- muted={false}
- small={false}
value="1.0"
/>
</div>
@@ -153,8 +147,6 @@ exports[`should render correctly with all data 1`] = `
className="spacer-right"
>
<CoverageRating
- muted={false}
- size="normal"
value="88.3"
/>
</span>
@@ -192,8 +184,6 @@ exports[`should render correctly with all data 1`] = `
className="spacer-right"
>
<DuplicationsRating
- muted={false}
- small={false}
value={9.8}
/>
</span>
@@ -231,8 +221,6 @@ exports[`should render correctly with all data 1`] = `
className="spacer-right"
>
<SizeRating
- muted={false}
- small={false}
value={2053}
/>
</span>
@@ -274,8 +262,6 @@ exports[`should render ncloc correctly 1`] = `
className="spacer-right"
>
<SizeRating
- muted={false}
- small={false}
value={16549887}
/>
</span>
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap
new file mode 100644
index 00000000000..54acf190a57
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap
@@ -0,0 +1,23 @@
+// 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"
+>
+ <Tooltip
+ overlay="overview.quality_gate_x.ERROR"
+ placement="bottom"
+ >
+ <div
+ className="project-card-measure-inner"
+ >
+ <Level
+ level="ERROR"
+ small={true}
+ />
+ </div>
+ </Tooltip>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap
new file mode 100644
index 00000000000..64edfbdf4da
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsList-test.tsx.snap
@@ -0,0 +1,42 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`does not render without projects 1`] = `null`;
+
+exports[`renders 1`] = `
+<div
+ className="projects-list"
+>
+ <Connect(ProjectCard)
+ projectKey="foo"
+ type="overall"
+ />
+ <Connect(ProjectCard)
+ projectKey="bar"
+ type="overall"
+ />
+</div>
+`;
+
+exports[`renders different types of "no projects" 1`] = `
+<div
+ className="projects-list"
+>
+ <EmptyInstance />
+</div>
+`;
+
+exports[`renders different types of "no projects" 2`] = `
+<div
+ className="projects-list"
+>
+ <EmptySearch />
+</div>
+`;
+
+exports[`renders different types of "no projects" 3`] = `
+<div
+ className="projects-list"
+>
+ <NoFavoriteProjects />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.tsx.snap
index dfa78f606fc..dfa78f606fc 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelectOption-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelectOption-test.tsx.snap
new file mode 100644
index 00000000000..473dc3902ed
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelectOption-test.tsx.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className=""
+ onMouseDown={[Function]}
+ onMouseEnter={[Function]}
+ onMouseMove={[Function]}
+ title="Size"
+>
+ Size
+</div>
+`;
+
+exports[`renders short 1`] = `
+<div
+ className=""
+ onMouseDown={[Function]}
+ onMouseEnter={[Function]}
+ onMouseMove={[Function]}
+ title="Size"
+>
+ Short
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js
deleted file mode 100644
index 701b9439eca..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import FilterContainer from './FilterContainer';
-import FilterHeader from './FilterHeader';
-import CoverageRating from '../../../components/ui/CoverageRating';
-import { getCoverageRatingLabel, getCoverageRatingAverageValue } from '../../../helpers/ratings';
-import { translate } from '../../../helpers/l10n';
-
-export default class CoverageFilter extends React.PureComponent {
- static propTypes = {
- className: PropTypes.string,
- query: PropTypes.object.isRequired,
- isFavorite: PropTypes.bool,
- organization: PropTypes.object,
- property: PropTypes.string
- };
-
- static defaultProps = {
- property: 'coverage'
- };
-
- getFacetValueForOption(facet, option) {
- const map = ['80.0-*', '70.0-80.0', '50.0-70.0', '30.0-50.0', '*-30.0', 'NO_DATA'];
- return facet[map[option - 1]];
- }
-
- renderOption(option, selected) {
- return (
- <span>
- {option < 6 && (
- <CoverageRating
- value={getCoverageRatingAverageValue(option)}
- size="small"
- muted={!selected}
- />
- )}
- <span className="spacer-left">
- {option < 6 ? (
- getCoverageRatingLabel(option)
- ) : (
- <span className="big-spacer-left">{translate('no_data')}</span>
- )}
- </span>
- </span>
- );
- }
-
- render() {
- return (
- <FilterContainer
- property={this.props.property}
- className={this.props.className}
- options={[1, 2, 3, 4, 5, 6]}
- query={this.props.query}
- renderOption={this.renderOption}
- isFavorite={this.props.isFavorite}
- organization={this.props.organization}
- getFacetValueForOption={this.getFacetValueForOption}
- highlightUnder={1}
- highlightUnderMax={5}
- header={<FilterHeader name={translate('metric_domain.Coverage')} />}
- />
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.tsx b/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.tsx
new file mode 100644
index 00000000000..b8784492319
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.tsx
@@ -0,0 +1,80 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import FilterContainer from './FilterContainer';
+import FilterHeader from './FilterHeader';
+import { Facet } from './Filter';
+import CoverageRating from '../../../components/ui/CoverageRating';
+import { getCoverageRatingLabel, getCoverageRatingAverageValue } from '../../../helpers/ratings';
+import { translate } from '../../../helpers/l10n';
+
+export interface Props {
+ className?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ property?: string;
+ query: { [x: string]: any };
+}
+
+export default function CoverageFilter(props: Props) {
+ const { property = 'coverage' } = props;
+
+ return (
+ <FilterContainer
+ property={property}
+ className={props.className}
+ options={[1, 2, 3, 4, 5, 6]}
+ query={props.query}
+ renderOption={renderOption}
+ isFavorite={props.isFavorite}
+ organization={props.organization}
+ getFacetValueForOption={getFacetValueForOption}
+ highlightUnder={1}
+ highlightUnderMax={5}
+ header={<FilterHeader name={translate('metric_domain.Coverage')} />}
+ />
+ );
+}
+
+function getFacetValueForOption(facet: Facet, option: number): number {
+ const map = ['80.0-*', '70.0-80.0', '50.0-70.0', '30.0-50.0', '*-30.0', 'NO_DATA'];
+ return facet[map[option - 1]];
+}
+
+function renderOption(option: number, selected: boolean) {
+ return (
+ <span>
+ {option < 6 && (
+ <CoverageRating
+ value={getCoverageRatingAverageValue(option)}
+ size="small"
+ muted={!selected}
+ />
+ )}
+ <span className="spacer-left">
+ {option < 6 ? (
+ getCoverageRatingLabel(option)
+ ) : (
+ <span className="big-spacer-left">{translate('no_data')}</span>
+ )}
+ </span>
+ </span>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js
deleted file mode 100644
index 3758cb3518d..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import FilterContainer from './FilterContainer';
-import FilterHeader from './FilterHeader';
-import DuplicationsRating from '../../../components/ui/DuplicationsRating';
-import {
- getDuplicationsRatingLabel,
- getDuplicationsRatingAverageValue
-} from '../../../helpers/ratings';
-import { translate } from '../../../helpers/l10n';
-
-export default class DuplicationsFilter extends React.PureComponent {
- static propTypes = {
- className: PropTypes.string,
- query: PropTypes.object.isRequired,
- isFavorite: PropTypes.bool,
- organization: PropTypes.object,
- property: PropTypes.string
- };
-
- static defaultProps = {
- property: 'duplications'
- };
-
- getFacetValueForOption(facet, option) {
- const map = ['*-3.0', '3.0-5.0', '5.0-10.0', '10.0-20.0', '20.0-*', 'NO_DATA'];
- return facet[map[option - 1]];
- }
-
- renderOption(option, selected) {
- return (
- <span>
- {option < 6 && (
- <DuplicationsRating
- value={getDuplicationsRatingAverageValue(option)}
- size="small"
- muted={!selected}
- />
- )}
- <span className="spacer-left">
- {option < 6 ? (
- getDuplicationsRatingLabel(option)
- ) : (
- <span className="big-spacer-left">{translate('no_data')}</span>
- )}
- </span>
- </span>
- );
- }
-
- render() {
- return (
- <FilterContainer
- property={this.props.property}
- className={this.props.className}
- options={[1, 2, 3, 4, 5, 6]}
- query={this.props.query}
- renderOption={this.renderOption}
- isFavorite={this.props.isFavorite}
- organization={this.props.organization}
- getFacetValueForOption={this.getFacetValueForOption}
- highlightUnder={1}
- highlightUnderMax={5}
- header={<FilterHeader name={translate('metric_domain.Duplications')} />}
- />
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.tsx b/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.tsx
new file mode 100644
index 00000000000..6526aced1b8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.tsx
@@ -0,0 +1,82 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import FilterContainer from './FilterContainer';
+import FilterHeader from './FilterHeader';
+import DuplicationsRating from '../../../components/ui/DuplicationsRating';
+import {
+ getDuplicationsRatingLabel,
+ getDuplicationsRatingAverageValue
+} from '../../../helpers/ratings';
+import { translate } from '../../../helpers/l10n';
+import { Facet } from './Filter';
+
+export interface Props {
+ className?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ property?: string;
+ query: { [x: string]: any };
+}
+
+export default function DuplicationsFilter(props: Props) {
+ const { property = 'duplications' } = props;
+ return (
+ <FilterContainer
+ property={property}
+ className={props.className}
+ options={[1, 2, 3, 4, 5, 6]}
+ query={props.query}
+ renderOption={renderOption}
+ isFavorite={props.isFavorite}
+ organization={props.organization}
+ getFacetValueForOption={getFacetValueForOption}
+ highlightUnder={1}
+ highlightUnderMax={5}
+ header={<FilterHeader name={translate('metric_domain.Duplications')} />}
+ />
+ );
+}
+
+function getFacetValueForOption(facet: Facet, option: number) {
+ const map = ['*-3.0', '3.0-5.0', '5.0-10.0', '10.0-20.0', '20.0-*', 'NO_DATA'];
+ return facet[map[option - 1]];
+}
+
+function renderOption(option: number, selected: boolean) {
+ return (
+ <span>
+ {option < 6 && (
+ <DuplicationsRating
+ value={getDuplicationsRatingAverageValue(option)}
+ size="small"
+ muted={!selected}
+ />
+ )}
+ <span className="spacer-left">
+ {option < 6 ? (
+ getDuplicationsRatingLabel(option)
+ ) : (
+ <span className="big-spacer-left">{translate('no_data')}</span>
+ )}
+ </span>
+ </span>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/Filter.js b/server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx
index 01e7051c7cd..04ac288c5a6 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/Filter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx
@@ -17,60 +17,58 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
-import PropTypes from 'prop-types';
-import classNames from 'classnames';
+import * as React from 'react';
+import * as classNames from 'classnames';
import { Link } from 'react-router';
import { getFilterUrl } from './utils';
import { formatMeasure } from '../../../helpers/measures';
import { translate } from '../../../helpers/l10n';
-export default class Filter extends React.PureComponent {
- static propTypes = {
- property: PropTypes.string.isRequired,
- className: PropTypes.string,
- options: PropTypes.array.isRequired,
- query: PropTypes.object.isRequired,
- renderOption: PropTypes.func.isRequired,
-
- value: PropTypes.any,
- facet: PropTypes.object,
- maxFacetValue: PropTypes.number,
- optionClassName: PropTypes.string,
- isFavorite: PropTypes.bool,
- organization: PropTypes.object,
-
- getFacetValueForOption: PropTypes.func,
-
- halfWidth: PropTypes.bool,
- highlightUnder: PropTypes.number,
- highlightUnderMax: PropTypes.number,
-
- header: PropTypes.object,
- footer: PropTypes.object
- };
+export type Option = string | number;
+export type Facet = { [x: string]: number };
- static defaultProps = {
- halfWidth: false
- };
+interface Props {
+ property: string;
+ className?: string;
+ options: Option[];
+ query: { [x: string]: any };
+ renderOption: (option: Option, isSelected: boolean) => React.ReactNode;
+
+ value?: Option | Option[];
+ facet?: Facet;
+ maxFacetValue?: number;
+ optionClassName?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+
+ getFacetValueForOption?: (facet: Facet, option: Option) => void;
+
+ halfWidth?: boolean;
+ highlightUnder?: number;
+ highlightUnderMax?: number;
- isSelected(option) {
+ header?: React.ReactNode;
+ footer?: React.ReactNode;
+}
+
+export default class Filter extends React.PureComponent<Props> {
+ isSelected(option: Option): boolean {
const { value } = this.props;
return Array.isArray(value) ? value.includes(option) : option === value;
}
- highlightUnder(option) {
+ highlightUnder(option?: Option): boolean {
return (
this.props.highlightUnder != null &&
- option !== null &&
+ option != null &&
option > this.props.highlightUnder &&
(this.props.highlightUnderMax == null || option < this.props.highlightUnderMax)
);
}
- blurOnClick = (evt /*: Event & { currentTarget: HTMLElement } */) => evt.currentTarget.blur();
+ blurOnClick = (event: React.SyntheticEvent<HTMLElement>) => event.currentTarget.blur();
- getPath(option) {
+ getPath(option: Option) {
const { property, value } = this.props;
let urlOption;
@@ -86,8 +84,8 @@ export default class Filter extends React.PureComponent {
return getFilterUrl(this.props, { [property]: urlOption });
}
- renderOptionBar(facetValue) {
- if (facetValue == null || !this.props.maxFacetValue) {
+ renderOptionBar(facetValue: number | undefined) {
+ if (facetValue == undefined || !this.props.maxFacetValue) {
return null;
}
return (
@@ -100,7 +98,7 @@ export default class Filter extends React.PureComponent {
);
}
- renderOption(option) {
+ renderOption(option: Option) {
const { facet, getFacetValueForOption, value } = this.props;
const className = classNames(
'facet',
@@ -115,9 +113,13 @@ export default class Filter extends React.PureComponent {
const path = this.getPath(option);
const facetValue =
- facet && getFacetValueForOption ? getFacetValueForOption(facet, option) : null;
+ facet && getFacetValueForOption ? getFacetValueForOption(facet, option) : undefined;
- const isUnderSelectedOption = this.highlightUnder(value) && option > value;
+ const isUnderSelectedOption =
+ typeof value === 'number' &&
+ typeof option === 'number' &&
+ this.highlightUnder(value) &&
+ option > value;
return (
<Link
@@ -139,7 +141,7 @@ export default class Filter extends React.PureComponent {
);
}
- renderOptions() {
+ renderOptions = () => {
const { options, highlightUnder } = this.props;
if (options && options.length > 0) {
if (highlightUnder != null) {
@@ -166,7 +168,7 @@ export default class Filter extends React.PureComponent {
} else {
return <div className="search-navigator-facet-empty">{translate('no_results')}</div>;
}
- }
+ };
render() {
return (
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.js b/server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.ts
index e70b9b5904e..eee1640b8ad 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/FilterContainer.ts
@@ -24,9 +24,10 @@ import {
getProjectsAppMaxFacetValue
} from '../../../store/rootReducer';
-const mapStateToProps = (state, ownProps) => ({
+const mapStateToProps = (state: any, ownProps: any) => ({
value: ownProps.query[ownProps.property],
facet: getProjectsAppFacetByProperty(state, ownProps.property),
maxFacetValue: getProjectsAppMaxFacetValue(state)
});
-export default connect(mapStateToProps)(Filter);
+
+export default connect<any, any, any>(mapStateToProps)(Filter);
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/FilterHeader.tsx b/server/sonar-web/src/main/js/apps/projects/filters/FilterHeader.tsx
new file mode 100644
index 00000000000..74ee5757733
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/FilterHeader.tsx
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+
+interface Props {
+ children?: React.ReactNode;
+ name: string;
+}
+
+export default function FilterHeader(props: Props) {
+ return (
+ <div className="search-navigator-facet-header projects-facet-header">
+ {props.name}
+ {props.children}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js
deleted file mode 100644
index 88ff9bc42f8..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import FilterContainer from './FilterContainer';
-import FilterHeader from './FilterHeader';
-import Rating from '../../../components/ui/Rating';
-import { translate } from '../../../helpers/l10n';
-
-export default class IssuesFilter extends React.PureComponent {
- static propTypes = {
- className: PropTypes.string,
- headerDetail: PropTypes.element,
- isFavorite: PropTypes.bool,
- organization: PropTypes.object,
- name: PropTypes.string.isRequired,
- property: PropTypes.string.isRequired,
- query: PropTypes.object.isRequired
- };
-
- getFacetValueForOption(facet, option) {
- return facet[option];
- }
-
- renderOption(option, selected) {
- return (
- <span>
- <Rating value={option} small={true} muted={!selected} />
- {option > 1 &&
- option < 5 && <span className="note spacer-left">{translate('and_worse')}</span>}
- </span>
- );
- }
-
- render() {
- return (
- <FilterContainer
- property={this.props.property}
- className={this.props.className}
- options={[1, 2, 3, 4, 5]}
- query={this.props.query}
- renderOption={this.renderOption}
- isFavorite={this.props.isFavorite}
- organization={this.props.organization}
- getFacetValueForOption={this.getFacetValueForOption}
- highlightUnder={1}
- header={
- <FilterHeader name={translate('metric_domain', this.props.name)}>
- {this.props.headerDetail}
- </FilterHeader>
- }
- />
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.tsx b/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.tsx
new file mode 100644
index 00000000000..2a139d6cb00
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.tsx
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import FilterContainer from './FilterContainer';
+import FilterHeader from './FilterHeader';
+import Rating from '../../../components/ui/Rating';
+import { translate } from '../../../helpers/l10n';
+import { Facet } from './Filter';
+
+interface Props {
+ className?: string;
+ headerDetail?: React.ReactNode;
+ isFavorite?: boolean;
+ name: string;
+ organization?: { key: string };
+ property?: string;
+ query: { [x: string]: any };
+}
+
+export default function IssuesFilter(props: Props) {
+ return (
+ <FilterContainer
+ property={props.property}
+ className={props.className}
+ options={[1, 2, 3, 4, 5]}
+ query={props.query}
+ renderOption={renderOption}
+ isFavorite={props.isFavorite}
+ organization={props.organization}
+ getFacetValueForOption={getFacetValueForOption}
+ highlightUnder={1}
+ header={
+ <FilterHeader name={translate('metric_domain', props.name)}>
+ {props.headerDetail}
+ </FilterHeader>
+ }
+ />
+ );
+}
+
+function getFacetValueForOption(facet: Facet, option: number) {
+ return facet[option];
+}
+
+function renderOption(option: number, selected: boolean) {
+ return (
+ <span>
+ <Rating value={option} small={true} muted={!selected} />
+ {option > 1 &&
+ option < 5 && <span className="note spacer-left">{translate('and_worse')}</span>}
+ </span>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.tsx
index 822b80d30f2..b4c7fb562e8 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.tsx
@@ -17,55 +17,49 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
import { difference, sortBy } from 'lodash';
-import Filter from './Filter';
+import Filter, { Facet } from './Filter';
import FilterHeader from './FilterHeader';
import SearchableFilterFooter from './SearchableFilterFooter';
import SearchableFilterOption from './SearchableFilterOption';
import { getLanguageByKey } from '../../../store/languages/reducer';
import { translate } from '../../../helpers/l10n';
-/*::
-type Props = {
- query: {},
- languages: {},
- router: { push: ({ pathname: string, query?: {} }) => void },
- value?: Array<string>,
- facet?: {},
- isFavorite?: boolean,
- organization?: {},
- maxFacetValue?: number
-};
-*/
+interface Languages {
+ [key: string]: { key: string; name: string };
+}
-const LIST_SIZE = 10;
+interface Props {
+ facet?: Facet;
+ isFavorite?: boolean;
+ languages: Languages;
+ maxFacetValue?: number;
+ organization?: { key: string };
+ property?: string;
+ query: { [x: string]: any };
+ value?: Array<string>;
+}
-export default class LanguagesFilter extends React.PureComponent {
- /*:: props: Props; */
- property = 'languages';
+const LIST_SIZE = 10;
- getSearchOptions(
- facet /*: ?{} */,
- languages /*: {} */
- ) /*: Array<{ label: string, value: string }> */ {
- let languageKeys = Object.keys(languages);
- if (facet) {
- languageKeys = difference(languageKeys, Object.keys(facet));
+export default class LanguagesFilter extends React.Component<Props> {
+ getSearchOptions = () => {
+ let languageKeys = Object.keys(this.props.languages);
+ if (this.props.facet) {
+ languageKeys = difference(languageKeys, Object.keys(this.props.facet));
}
return languageKeys
.slice(0, LIST_SIZE)
- .map(key => ({ label: languages[key].name, value: key }));
- }
+ .map(key => ({ label: this.props.languages[key].name, value: key }));
+ };
- getSortedOptions(facet /*: {} */ = {}) {
- return sortBy(Object.keys(facet), [option => -facet[option], option => option]);
- }
+ getSortedOptions = (facet: Facet = {}) =>
+ sortBy(Object.keys(facet), [(option: string) => -facet[option], (option: string) => option]);
- getFacetValueForOption = (facet /*: {} */ = {}, option /*: string */) => facet[option];
+ getFacetValueForOption = (facet: Facet = {}, option: string) => facet[option];
- renderOption = (option /*: string */) => (
+ renderOption = (option: string) => (
<SearchableFilterOption
optionKey={option}
option={getLanguageByKey(this.props.languages, option)}
@@ -73,9 +67,11 @@ export default class LanguagesFilter extends React.PureComponent {
);
render() {
+ const { property = 'languages' } = this.props;
+
return (
<Filter
- property={this.property}
+ property={property}
options={this.getSortedOptions(this.props.facet)}
query={this.props.query}
renderOption={this.renderOption}
@@ -88,12 +84,11 @@ export default class LanguagesFilter extends React.PureComponent {
header={<FilterHeader name={translate('projects.facets.languages')} />}
footer={
<SearchableFilterFooter
- property={this.property}
- query={this.props.query}
- options={this.getSearchOptions(this.props.facet, this.props.languages)}
isFavorite={this.props.isFavorite}
organization={this.props.organization}
- router={this.props.router}
+ options={this.getSearchOptions()}
+ property={property}
+ query={this.props.query}
/>
}
/>
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilterContainer.js b/server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilterContainer.ts
index 8143103d283..8b45c11c36a 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilterContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilterContainer.ts
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { connect } from 'react-redux';
-import { withRouter } from 'react-router';
import LanguagesFilter from './LanguagesFilter';
import {
getProjectsAppFacetByProperty,
@@ -26,10 +25,10 @@ import {
getLanguages
} from '../../../store/rootReducer';
-const mapStateToProps = (state, ownProps) => ({
+const mapStateToProps = (state: any, ownProps: any) => ({
languages: getLanguages(state),
value: ownProps.query['languages'],
facet: getProjectsAppFacetByProperty(state, 'languages'),
maxFacetValue: getProjectsAppMaxFacetValue(state)
});
-export default connect(mapStateToProps)(withRouter(LanguagesFilter));
+export default connect<any, any, any>(mapStateToProps)(LanguagesFilter);
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/MaintainabilityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/MaintainabilityFilter.tsx
index a02a5f70c57..424e3f21ce1 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/MaintainabilityFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/MaintainabilityFilter.tsx
@@ -17,9 +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 React from 'react';
+import * as React from 'react';
import IssuesFilter from './IssuesFilter';
-export default function MaintainabilityFilter(props) {
+interface Props {
+ className?: string;
+ headerDetail?: React.ReactNode;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ query: { [x: string]: any };
+}
+
+export default function MaintainabilityFilter(props: Props) {
return <IssuesFilter {...props} name="Maintainability" property="maintainability" />;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.tsx
index 24f24ce2706..9d344bf5141 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.tsx
@@ -17,9 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
-import CoverageFilter from './CoverageFilter';
+import * as React from 'react';
+import CoverageFilter, { Props } from './CoverageFilter';
-export default function NewCoverageFilter(props) {
+export default function NewCoverageFilter(props: Props) {
return <CoverageFilter {...props} property="new_coverage" className="leak-facet-box" />;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.tsx
index 98754c93080..770b7219d0e 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.tsx
@@ -17,9 +17,9 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
-import DuplicationsFilter from './DuplicationsFilter';
+import * as React from 'react';
+import DuplicationsFilter, { Props } from './DuplicationsFilter';
-export default function NewDuplicationsFilter(props) {
+export default function NewDuplicationsFilter(props: Props) {
return <DuplicationsFilter {...props} property="new_duplications" className="leak-facet-box" />;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.js
deleted file mode 100644
index 2593f06a3a0..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import FilterContainer from './FilterContainer';
-import FilterHeader from './FilterHeader';
-import { translate } from '../../../helpers/l10n';
-import { getSizeRatingLabel } from '../../../helpers/ratings';
-
-export default class NewLinesFilter extends React.PureComponent {
- static propTypes = {
- className: PropTypes.string,
- query: PropTypes.object.isRequired,
- isFavorite: PropTypes.bool,
- organization: PropTypes.object,
- property: PropTypes.string
- };
-
- static defaultProps = {
- property: 'new_lines'
- };
-
- getFacetValueForOption(facet, option) {
- const map = [
- '*-1000.0',
- '1000.0-10000.0',
- '10000.0-100000.0',
- '100000.0-500000.0',
- '500000.0-*'
- ];
- return facet[map[option - 1]];
- }
-
- renderOption(option) {
- return <span>{getSizeRatingLabel(option)}</span>;
- }
-
- render() {
- return (
- <FilterContainer
- property={this.props.property}
- className="leak-facet-box"
- options={[1, 2, 3, 4, 5]}
- query={this.props.query}
- renderOption={this.renderOption}
- isFavorite={this.props.isFavorite}
- organization={this.props.organization}
- getFacetValueForOption={this.getFacetValueForOption}
- highlightUnder={1}
- header={<FilterHeader name={translate('projects.facets.new_lines')} />}
- />
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.tsx b/server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.tsx
new file mode 100644
index 00000000000..29374492c73
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.tsx
@@ -0,0 +1,61 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import FilterContainer from './FilterContainer';
+import FilterHeader from './FilterHeader';
+import { translate } from '../../../helpers/l10n';
+import { getSizeRatingLabel } from '../../../helpers/ratings';
+import { Facet } from './Filter';
+
+export interface Props {
+ className?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ property?: string;
+ query: { [x: string]: any };
+}
+
+export default function NewLinesFilter(props: Props) {
+ const { property = 'new_lines' } = props;
+
+ return (
+ <FilterContainer
+ property={property}
+ className="leak-facet-box"
+ options={[1, 2, 3, 4, 5]}
+ query={props.query}
+ renderOption={renderOption}
+ isFavorite={props.isFavorite}
+ organization={props.organization}
+ getFacetValueForOption={getFacetValueForOption}
+ highlightUnder={1}
+ header={<FilterHeader name={translate('projects.facets.new_lines')} />}
+ />
+ );
+}
+
+function getFacetValueForOption(facet: Facet, option: number) {
+ const map = ['*-1000.0', '1000.0-10000.0', '10000.0-100000.0', '100000.0-500000.0', '500000.0-*'];
+ return facet[map[option - 1]];
+}
+
+function renderOption(option: number) {
+ return <span>{getSizeRatingLabel(option)}</span>;
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.tsx
index b501476a25f..590645121b2 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.tsx
@@ -17,12 +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.
*/
-import React from 'react';
+import * as React from 'react';
import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
-export default function NewMaintainabilityFilter(props) {
+interface Props {
+ className?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ query: { [x: string]: any };
+}
+
+export default function NewMaintainabilityFilter(props: Props) {
return (
<IssuesFilter
{...props}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.tsx
index 9e42ab27e69..c2b6ff0e08d 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.tsx
@@ -17,12 +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.
*/
-import React from 'react';
+import * as React from 'react';
import BugIcon from '../../../components/icons-components/BugIcon';
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
-export default function NewReliabilityFilter(props) {
+interface Props {
+ className?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ query: { [x: string]: any };
+}
+
+export default function NewReliabilityFilter(props: Props) {
return (
<IssuesFilter
{...props}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.tsx
index 7d2e64e74c7..66d5bf07c5f 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.tsx
@@ -17,12 +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.
*/
-import React from 'react';
+import * as React from 'react';
import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
-export default function NewSecurityFilter(props) {
+interface Props {
+ className?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ query: { [x: string]: any };
+}
+
+export default function NewSecurityFilter(props: Props) {
return (
<IssuesFilter
{...props}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx
index 825ccb8625d..56ce79e0db6 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx
@@ -17,40 +17,39 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
-import PropTypes from 'prop-types';
+import * as React from 'react';
import FilterContainer from './FilterContainer';
import FilterHeader from './FilterHeader';
import Level from '../../../components/ui/Level';
import { translate } from '../../../helpers/l10n';
+import { Facet } from './Filter';
-export default class QualityGateFilter extends React.PureComponent {
- static propTypes = {
- query: PropTypes.object.isRequired,
- isFavorite: PropTypes.bool,
- organization: PropTypes.object
- };
+export interface Props {
+ className?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ query: { [x: string]: any };
+}
- getFacetValueForOption(facet, option) {
- return facet[option];
- }
+export default function QualityGateFilter(props: Props) {
+ return (
+ <FilterContainer
+ property="gate"
+ options={['OK', 'WARN', 'ERROR']}
+ query={props.query}
+ renderOption={renderOption}
+ isFavorite={props.isFavorite}
+ organization={props.organization}
+ getFacetValueForOption={getFacetValueForOption}
+ header={<FilterHeader name={translate('projects.facets.quality_gate')} />}
+ />
+ );
+}
- renderOption(option, selected) {
- return <Level level={option} small={true} muted={!selected} />;
- }
+function getFacetValueForOption(facet: Facet, option: string) {
+ return facet[option];
+}
- render() {
- return (
- <FilterContainer
- property="gate"
- options={['OK', 'WARN', 'ERROR']}
- query={this.props.query}
- renderOption={this.renderOption}
- isFavorite={this.props.isFavorite}
- organization={this.props.organization}
- getFacetValueForOption={this.getFacetValueForOption}
- header={<FilterHeader name={translate('projects.facets.quality_gate')} />}
- />
- );
- }
+function renderOption(option: string, selected: boolean) {
+ return <Level level={option} small={true} muted={!selected} />;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/ReliabilityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/ReliabilityFilter.tsx
index 07852afe335..99e3ce19bd0 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/ReliabilityFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/ReliabilityFilter.tsx
@@ -17,9 +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 React from 'react';
+import * as React from 'react';
import IssuesFilter from './IssuesFilter';
-export default function ReliabilityFilter(props) {
+interface Props {
+ className?: string;
+ headerDetail?: React.ReactNode;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ query: { [x: string]: any };
+}
+
+export default function ReliabilityFilter(props: Props) {
return <IssuesFilter {...props} name="Reliability" property="reliability" />;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.tsx
index 76cbdb067ca..5bc0764acdc 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.tsx
@@ -17,50 +17,39 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import { translate, translateWithParameters } from '../../../helpers/l10n';
-/*::
-type Props = {
- className?: string,
- handleSearch: (userString?: string) => void,
- query: { search?: string }
-};
-*/
-
-/*::
-type State = {
- userQuery?: string
-};
-*/
+interface Props {
+ className?: string;
+ handleSearch: (userString?: string) => void;
+ query: { search?: string | undefined };
+}
-export default class SearchFilter extends React.PureComponent {
- /*:: props: Props; */
- /*:: state: State; */
+interface State {
+ userQuery?: string;
+}
- constructor(props /*: Props */) {
+export default class SearchFilter extends React.PureComponent<Props, State> {
+ constructor(props: Props) {
super(props);
- this.state = {
- userQuery: props.query.search
- };
+ this.state = { userQuery: props.query.search };
}
- componentWillReceiveProps(nextProps /*: Props */) {
+ componentWillReceiveProps(nextProps: Props) {
if (
this.props.query.search === this.state.userQuery &&
nextProps.query.search !== this.props.query.search
) {
- this.setState({
- userQuery: nextProps.query.search || ''
- });
+ this.setState({ userQuery: nextProps.query.search || '' });
}
}
- handleQueryChange = ({ target } /*: { target: HTMLInputElement } */) => {
- this.setState({ userQuery: target.value });
- if (!target.value || target.value.length >= 2) {
- this.props.handleSearch(target.value);
+ handleQueryChange = (event: React.SyntheticEvent<HTMLInputElement>) => {
+ const { value } = event.currentTarget;
+ this.setState({ userQuery: value });
+ if (!value || value.length >= 2) {
+ this.props.handleSearch(value);
}
};
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.js b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.tsx
index 6e64dbf9bec..b17ca9a1a21 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.tsx
@@ -17,36 +17,33 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
-import { withRouter } from 'react-router';
+import * as React from 'react';
+import * as PropTypes from 'prop-types';
import { debounce } from 'lodash';
import { getFilterUrl } from './utils';
import SearchFilter from './SearchFilter';
-/*::
-type Props = {|
- className?: string,
- query: { search?: string },
- router: { push: ({ pathname: string }) => void },
- isFavorite?: boolean,
- organization?: {}
-|};
-*/
+interface Props {
+ className?: string;
+ query: { search?: string };
+ isFavorite?: boolean;
+ organization?: { key: string };
+}
-class SearchFilterContainer extends React.PureComponent {
- /*:: handleSearch: (userQuery?: string) => void; */
- /*:: props: Props; */
+export default class SearchFilterContainer extends React.PureComponent<Props> {
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
- constructor(props /*: Props */) {
+ constructor(props: Props) {
super(props);
- this.handleSearch = debounce(this.handleSearch.bind(this), 250);
+ this.handleSearch = debounce(this.handleSearch, 250);
}
- handleSearch(userQuery /*: ?string */) {
- const path = getFilterUrl(this.props, { search: userQuery || null });
- this.props.router.push(path);
- }
+ handleSearch = (userQuery?: string) => {
+ const path = getFilterUrl(this.props, { search: userQuery });
+ this.context.router.push(path);
+ };
render() {
return (
@@ -58,5 +55,3 @@ class SearchFilterContainer extends React.PureComponent {
);
}
}
-
-export default withRouter(SearchFilterContainer);
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterFooter.js b/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterFooter.tsx
index 25b7ff076d7..b81830abbb7 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterFooter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterFooter.tsx
@@ -17,48 +17,47 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
-import Select from 'react-select';
+import * as React from 'react';
+import * as Select from 'react-select';
+import * as PropTypes from 'prop-types';
import { getFilterUrl } from './utils';
import { translate } from '../../../helpers/l10n';
-/*::
-type Props = {
- property: string,
- query: {},
- options: Array<{ label: string, value: string }>,
- router: { push: ({ pathname: string, query?: {} }) => void },
- onInputChange?: string => void,
- onOpen?: void => void,
- isLoading?: boolean,
- isFavorite?: boolean,
- organization?: {}
-};
-*/
+interface Props {
+ property: string;
+ query: { [x: string]: any };
+ options: Array<{ label: string; value: string }>;
+ onInputChange?: (query: string) => void;
+ onOpen?: () => void;
+ isLoading?: boolean;
+ isFavorite?: boolean;
+ organization?: { key: string };
+}
-export default class SearchableFilterFooter extends React.PureComponent {
- /*:: props: Props; */
+export default class SearchableFilterFooter extends React.PureComponent<Props> {
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
- handleOptionChange /*: ({ value: string }) => void */ = ({ value }) => {
+ handleOptionChange = ({ value }: { value: string }) => {
const urlOptions = (this.props.query[this.props.property] || []).concat(value).join(',');
const path = getFilterUrl(this.props, { [this.props.property]: urlOptions });
- this.props.router.push(path);
+ this.context.router.push(path);
};
render() {
return (
<div className="search-navigator-facet-footer projects-facet-footer">
<Select
- onChange={this.handleOptionChange}
className="input-super-large"
- placeholder={translate('search_verb')}
clearable={false}
- searchable={true}
+ isLoading={this.props.isLoading}
+ onChange={this.handleOptionChange}
onInputChange={this.props.onInputChange}
onOpen={this.props.onOpen}
- isLoading={this.props.isLoading}
options={this.props.options}
+ placeholder={translate('search_verb')}
+ searchable={true}
/>
</div>
);
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterOption.js b/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterOption.js
deleted file mode 100644
index 16831ee169e..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterOption.js
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import { translate } from '../../../helpers/l10n';
-
-export default class SearchableFilterOption extends React.PureComponent {
- static propTypes = {
- optionKey: PropTypes.string.isRequired,
- option: PropTypes.object
- };
-
- render() {
- const optionName = this.props.option ? this.props.option.name : this.props.optionKey;
- return <span>{this.props.optionKey !== '<null>' ? optionName : translate('unknown')}</span>;
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/FilterHeader.js b/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterOption.tsx
index 51b3512ca0e..06bf39a8289 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/FilterHeader.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterOption.tsx
@@ -17,25 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
+import { translate } from '../../../helpers/l10n';
-/*::
-type Props = {
- name: string,
- children?: React.Element<*>
-};
-*/
-
-export default class FilterHeader extends React.PureComponent {
- /*:: props: Props; */
+interface Props {
+ option?: { name: string };
+ optionKey: string;
+}
- render() {
- return (
- <div className="search-navigator-facet-header projects-facet-header">
- {this.props.name}
- {this.props.children}
- </div>
- );
- }
+export default function SearchableFilterOption(props: Props) {
+ const optionName = props.option ? props.option.name : props.optionKey;
+ return <span>{props.optionKey !== '<null>' ? optionName : translate('unknown')}</span>;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SecurityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/SecurityFilter.tsx
index 9f7fa7aa997..27684ca9d93 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/SecurityFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/SecurityFilter.tsx
@@ -17,9 +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 React from 'react';
+import * as React from 'react';
import IssuesFilter from './IssuesFilter';
-export default function SecurityFilter(props) {
+interface Props {
+ className?: string;
+ headerDetail?: React.ReactNode;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ query: { [x: string]: any };
+}
+
+export default function SecurityFilter(props: Props) {
return <IssuesFilter {...props} name="Security" property="security" />;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js
deleted file mode 100644
index 566f799ef8d..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import FilterContainer from './FilterContainer';
-import FilterHeader from './FilterHeader';
-import SizeRating from '../../../components/ui/SizeRating';
-import { translate } from '../../../helpers/l10n';
-import { getSizeRatingLabel, getSizeRatingAverageValue } from '../../../helpers/ratings';
-
-export default class SizeFilter extends React.PureComponent {
- static propTypes = {
- className: PropTypes.string,
- query: PropTypes.object.isRequired,
- isFavorite: PropTypes.bool,
- organization: PropTypes.object,
- property: PropTypes.string
- };
-
- static defaultProps = {
- property: 'size'
- };
-
- getFacetValueForOption(facet, option) {
- const map = [
- '*-1000.0',
- '1000.0-10000.0',
- '10000.0-100000.0',
- '100000.0-500000.0',
- '500000.0-*'
- ];
- return facet[map[option - 1]];
- }
-
- renderOption(option, selected) {
- return (
- <span>
- <SizeRating value={getSizeRatingAverageValue(option)} small={true} muted={!selected} />
- <span className="spacer-left">{getSizeRatingLabel(option)}</span>
- </span>
- );
- }
-
- render() {
- return (
- <FilterContainer
- property={this.props.property}
- className={this.props.className}
- options={[1, 2, 3, 4, 5]}
- query={this.props.query}
- renderOption={this.renderOption}
- isFavorite={this.props.isFavorite}
- organization={this.props.organization}
- getFacetValueForOption={this.getFacetValueForOption}
- highlightUnder={1}
- header={<FilterHeader name={translate('metric_domain.Size')} />}
- />
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.tsx b/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.tsx
new file mode 100644
index 00000000000..36479e486b7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.tsx
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import FilterContainer from './FilterContainer';
+import FilterHeader from './FilterHeader';
+import SizeRating from '../../../components/ui/SizeRating';
+import { translate } from '../../../helpers/l10n';
+import { getSizeRatingLabel, getSizeRatingAverageValue } from '../../../helpers/ratings';
+import { Facet } from './Filter';
+
+export interface Props {
+ className?: string;
+ isFavorite?: boolean;
+ organization?: { key: string };
+ property?: string;
+ query: { [x: string]: any };
+}
+
+export default function SizeFilter(props: Props) {
+ const { property = 'size' } = props;
+
+ return (
+ <FilterContainer
+ property={property}
+ className={props.className}
+ options={[1, 2, 3, 4, 5]}
+ query={props.query}
+ renderOption={renderOption}
+ isFavorite={props.isFavorite}
+ organization={props.organization}
+ getFacetValueForOption={getFacetValueForOption}
+ highlightUnder={1}
+ header={<FilterHeader name={translate('metric_domain.Size')} />}
+ />
+ );
+}
+
+function getFacetValueForOption(facet: Facet, option: number) {
+ const map = ['*-1000.0', '1000.0-10000.0', '10000.0-100000.0', '100000.0-500000.0', '500000.0-*'];
+ return facet[map[option - 1]];
+}
+
+function renderOption(option: number, selected: boolean) {
+ return (
+ <span>
+ <SizeRating value={getSizeRatingAverageValue(option)} small={true} muted={!selected} />
+ <span className="spacer-left">{getSizeRatingLabel(option)}</span>
+ </span>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/TagsFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/TagsFilter.tsx
index 22c5bfcb377..e163fed3b49 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/TagsFilter.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/TagsFilter.tsx
@@ -17,66 +17,63 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-//@flow
-import React from 'react';
+import * as React from 'react';
import { debounce, difference, sortBy, size } from 'lodash';
-import Filter from './Filter';
+import Filter, { Facet } from './Filter';
import FilterHeader from './FilterHeader';
import SearchableFilterFooter from './SearchableFilterFooter';
import SearchableFilterOption from './SearchableFilterOption';
import { searchProjectTags } from '../../../api/components';
import { translate } from '../../../helpers/l10n';
-/*::
-type Props = {
- query: {},
- router: { push: ({ pathname: string, query?: {} }) => void },
- value?: Array<string>,
- facet?: {},
- isFavorite?: boolean,
- organization?: {},
- maxFacetValue?: number
-};
-*/
+interface Props {
+ facet?: Facet;
+ isFavorite?: boolean;
+ maxFacetValue?: number;
+ organization?: { key: string };
+ property?: string;
+ query: { [x: string]: any };
+ value?: string[];
+}
-/*::
-type State = {
- isLoading: boolean,
- search: string,
- tags: Array<string>
-};
-*/
+interface State {
+ isLoading: boolean;
+ search: string;
+ tags: string[];
+}
const LIST_SIZE = 10;
-export default class TagsFilter extends React.PureComponent {
- /*:: props: Props; */
- /*:: state: State; */
- /*:: property: string; */
+export default class TagsFilter extends React.PureComponent<Props, State> {
+ mounted: boolean;
- constructor(props /*: Props */) {
+ constructor(props: Props) {
super(props);
this.state = {
isLoading: false,
search: '',
tags: []
};
- this.property = 'tags';
- this.handleSearch = debounce(this.handleSearch.bind(this), 250);
+ this.handleSearch = debounce(this.handleSearch, 250);
}
- getSearchOptions(
- facet /*: ?{} */,
- tags /*: Array<string> */
- ) /*: Array<{ label: string, value: string }> */ {
- let tagsCopy = [...tags];
- if (facet) {
- tagsCopy = difference(tagsCopy, Object.keys(facet));
+ componentDidMount() {
+ this.mounted = true;
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ getSearchOptions = () => {
+ let tagsCopy = [...this.state.tags];
+ if (this.props.facet) {
+ tagsCopy = difference(tagsCopy, Object.keys(this.props.facet));
}
return tagsCopy.slice(0, LIST_SIZE).map(tag => ({ label: tag, value: tag }));
- }
+ };
- handleSearch = (search /*: ?string */) => {
+ handleSearch = (search?: string) => {
if (search !== this.state.search) {
search = search || '';
this.setState({ search, isLoading: true });
@@ -84,23 +81,26 @@ export default class TagsFilter extends React.PureComponent {
q: search,
ps: size(this.props.facet || {}) + LIST_SIZE
}).then(result => {
- this.setState({ isLoading: false, tags: result.tags });
+ if (this.mounted) {
+ this.setState({ isLoading: false, tags: result.tags });
+ }
});
}
};
- getSortedOptions(facet /*: {} */ = {}) {
- return sortBy(Object.keys(facet), [option => -facet[option], option => option]);
- }
+ getSortedOptions = (facet: Facet = {}) =>
+ sortBy(Object.keys(facet), [(option: string) => -facet[option], (option: string) => option]);
- getFacetValueForOption = (facet /*: {} */, option /*: string */) => facet[option];
+ getFacetValueForOption = (facet: Facet = {}, option: string) => facet[option];
- renderOption = (option /*: string */) => <SearchableFilterOption optionKey={option} />;
+ renderOption = (option: string) => <SearchableFilterOption optionKey={option} />;
render() {
+ const { property = 'tags' } = this.props;
+
return (
<Filter
- property={this.property}
+ property={property}
options={this.getSortedOptions(this.props.facet)}
query={this.props.query}
renderOption={this.renderOption}
@@ -113,15 +113,14 @@ export default class TagsFilter extends React.PureComponent {
header={<FilterHeader name={translate('projects.facets.tags')} />}
footer={
<SearchableFilterFooter
- property={this.property}
- query={this.props.query}
- options={this.getSearchOptions(this.props.facet, this.state.tags)}
+ isFavorite={this.props.isFavorite}
isLoading={this.state.isLoading}
- onOpen={this.handleSearch}
onInputChange={this.handleSearch}
- isFavorite={this.props.isFavorite}
+ onOpen={this.handleSearch}
organization={this.props.organization}
- router={this.props.router}
+ options={this.getSearchOptions()}
+ property={property}
+ query={this.props.query}
/>
}
/>
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/TagsFilterContainer.js b/server/sonar-web/src/main/js/apps/projects/filters/TagsFilterContainer.ts
index 8c841346a42..47d25834e5c 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/TagsFilterContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/TagsFilterContainer.ts
@@ -18,16 +18,15 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { connect } from 'react-redux';
-import { withRouter } from 'react-router';
import TagsFilter from './TagsFilter';
import {
getProjectsAppFacetByProperty,
getProjectsAppMaxFacetValue
} from '../../../store/rootReducer';
-const mapStateToProps = (state, ownProps) => ({
+const mapStateToProps = (state: any, ownProps: any) => ({
value: ownProps.query['tags'],
facet: getProjectsAppFacetByProperty(state, 'tags'),
maxFacetValue: getProjectsAppMaxFacetValue(state)
});
-export default connect(mapStateToProps)(withRouter(TagsFilter));
+export default connect<any, any, any>(mapStateToProps)(TagsFilter);
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/CoverageFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/CoverageFilter-test.tsx
new file mode 100644
index 00000000000..29f2121767d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/CoverageFilter-test.tsx
@@ -0,0 +1,38 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import CoverageFilter from '../CoverageFilter';
+
+it('renders', () => {
+ const wrapper = shallow(<CoverageFilter query={{}} />);
+ expect(wrapper).toMatchSnapshot();
+
+ const renderOption = wrapper.prop('renderOption');
+ expect(renderOption(2, false)).toMatchSnapshot();
+
+ const getFacetValueForOption = wrapper.prop('getFacetValueForOption');
+ expect(
+ getFacetValueForOption(
+ { '80.0-*': 1, '70.0-80.0': 42, '50.0-70.0': 14, '30.0-50.0': 13, '*-30.0': 8, NO_DATA: 3 },
+ 2
+ )
+ ).toBe(42);
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/DuplicationsFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/DuplicationsFilter-test.tsx
new file mode 100644
index 00000000000..2a4588325dc
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/DuplicationsFilter-test.tsx
@@ -0,0 +1,39 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import DuplicationsFilter from '../DuplicationsFilter';
+
+it('renders', () => {
+ const wrapper = shallow(<DuplicationsFilter query={{}} />);
+ expect(wrapper).toMatchSnapshot();
+
+ const renderOption = wrapper.prop('renderOption');
+ expect(renderOption(2, false)).toMatchSnapshot();
+ expect(renderOption(6, true)).toMatchSnapshot();
+
+ const getFacetValueForOption = wrapper.prop('getFacetValueForOption');
+ expect(
+ getFacetValueForOption(
+ { '*-3.0': 1, '3.0-5.0': 42, '5.0-10.0': 14, '10.0-20.0': 13, '20.0-*': 8, NO_DATA: 3 },
+ 2
+ )
+ ).toBe(42);
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/Filter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/Filter-test.tsx
new file mode 100644
index 00000000000..51c9f5b2b52
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/Filter-test.tsx
@@ -0,0 +1,73 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import Filter from '../Filter';
+
+it('renders', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+it('renders header and footer', () => {
+ expect(shallowRender({ header: <header />, footer: <footer /> })).toMatchSnapshot();
+});
+
+it('renders no results', () => {
+ expect(shallowRender({ options: [] })).toMatchSnapshot();
+});
+
+it('highlights under', () => {
+ expect(shallowRender({ highlightUnder: 1 })).toMatchSnapshot();
+});
+
+it('renders selected', () => {
+ expect(shallowRender({ value: 2 })).toMatchSnapshot();
+});
+
+it('hightlights under selected', () => {
+ expect(shallowRender({ highlightUnder: 1, value: 2 })).toMatchSnapshot();
+});
+
+it('renders multiple selected', () => {
+ expect(shallowRender({ value: [1, 2] })).toMatchSnapshot();
+});
+
+it('renders facet bar chart', () => {
+ expect(
+ shallowRender({
+ getFacetValueForOption: (facet: any, option: any) => facet[option],
+ facet: { a: 17, b: 15, c: 24 },
+ maxFacetValue: 24,
+ options: ['a', 'b', 'c']
+ })
+ ).toMatchSnapshot();
+});
+
+function shallowRender(props?: any) {
+ return shallow(
+ <Filter
+ options={[1, 2, 3]}
+ property="foo"
+ query={{}}
+ renderOption={option => option}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/FilterHeader-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/FilterHeader-test.tsx
new file mode 100644
index 00000000000..cb0301a76e5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/FilterHeader-test.tsx
@@ -0,0 +1,36 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import FilterHeader from '../FilterHeader';
+
+it('renders', () => {
+ expect(shallow(<FilterHeader name="foo" />)).toMatchSnapshot();
+});
+
+it('renders with children', () => {
+ expect(
+ shallow(
+ <FilterHeader name="foo">
+ <div />
+ </FilterHeader>
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/IssuesFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/IssuesFilter-test.tsx
new file mode 100644
index 00000000000..6f839f171dc
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/IssuesFilter-test.tsx
@@ -0,0 +1,33 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import IssuesFilter from '../IssuesFilter';
+
+it('renders', () => {
+ const wrapper = shallow(<IssuesFilter name="bugs" query={{}} />);
+ expect(wrapper).toMatchSnapshot();
+
+ const renderOption = wrapper.prop('renderOption');
+ expect(renderOption(2, false)).toMatchSnapshot();
+
+ const getFacetValueForOption = wrapper.prop('getFacetValueForOption');
+ expect(getFacetValueForOption({ 1: 1, 2: 2, 3: 3, 4: 4, 5: 5 }, 2)).toBe(2);
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/LanguagesFilter-test.js b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/LanguagesFilter-test.tsx
index b5a84155f69..2669fd4f1ae 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/LanguagesFilter-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/LanguagesFilter-test.tsx
@@ -17,47 +17,24 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import LanguagesFilter from '../LanguagesFilter';
const languages = {
- java: {
- key: 'java',
- name: 'Java'
- },
- cs: {
- key: 'cs',
- name: 'C#'
- },
- js: {
- key: 'js',
- name: 'JavaScript'
- },
- flex: {
- key: 'flex',
- name: 'Flex'
- },
- php: {
- key: 'php',
- name: 'PHP'
- },
- py: {
- key: 'py',
- name: 'Python'
- }
+ java: { key: 'java', name: 'Java' },
+ cs: { key: 'cs', name: 'C#' },
+ js: { key: 'js', name: 'JavaScript' },
+ flex: { key: 'flex', name: 'Flex' },
+ php: { key: 'php', name: 'PHP' },
+ py: { key: 'py', name: 'Python' }
};
+
const languagesFacet = { java: 39, cs: 4, js: 1 };
-const fakeRouter = { push: () => {} };
it('should render the languages without the ones in the facet', () => {
const wrapper = shallow(
- <LanguagesFilter
- query={{ languages: null }}
- languages={languages}
- router={fakeRouter}
- facet={languagesFacet}
- />
+ <LanguagesFilter query={{ languages: null }} languages={languages} facet={languagesFacet} />
);
expect(wrapper).toMatchSnapshot();
});
@@ -68,7 +45,6 @@ it('should render the languages facet with the selected languages', () => {
query={{ languages: ['java', 'cs'] }}
value={['java', 'cs']}
languages={languages}
- router={fakeRouter}
facet={languagesFacet}
isFavorite={true}
/>
@@ -94,7 +70,6 @@ it('should render maximum 10 languages in the searchbox results', () => {
k: { key: 'k', name: 'k' },
l: { key: 'l', name: 'l' }
}}
- router={fakeRouter}
facet={{ ...languagesFacet, g: 1 }}
isFavorite={true}
/>
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/MaintainabilityFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/MaintainabilityFilter-test.tsx
new file mode 100644
index 00000000000..59add14532b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/MaintainabilityFilter-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import MaintainabilityFilter from '../MaintainabilityFilter';
+
+it('renders', () => {
+ expect(shallow(<MaintainabilityFilter query={{}} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewCoverageFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewCoverageFilter-test.tsx
new file mode 100644
index 00000000000..69f0855f75e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewCoverageFilter-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import NewCoverageFilter from '../NewCoverageFilter';
+
+it('renders', () => {
+ expect(shallow(<NewCoverageFilter query={{}} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewDuplicationsFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewDuplicationsFilter-test.tsx
new file mode 100644
index 00000000000..9420d8257be
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewDuplicationsFilter-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import NewDuplicationsFilter from '../NewDuplicationsFilter';
+
+it('renders', () => {
+ expect(shallow(<NewDuplicationsFilter query={{}} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewLinesFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewLinesFilter-test.tsx
new file mode 100644
index 00000000000..5760a50b122
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewLinesFilter-test.tsx
@@ -0,0 +1,44 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import NewLinesFilter from '../NewLinesFilter';
+
+it('renders', () => {
+ const wrapper = shallow(<NewLinesFilter query={{}} />);
+ expect(wrapper).toMatchSnapshot();
+
+ const renderOption = wrapper.prop('renderOption');
+ expect(renderOption(2, false)).toMatchSnapshot();
+
+ const getFacetValueForOption = wrapper.prop('getFacetValueForOption');
+ expect(
+ getFacetValueForOption(
+ {
+ '*-1000.0': 1,
+ '1000.0-10000.0': 2,
+ '10000.0-100000.0': 3,
+ '100000.0-500000.0': 4,
+ '500000.0-*': 5
+ },
+ 2
+ )
+ ).toBe(2);
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewMaintainabilityFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewMaintainabilityFilter-test.tsx
new file mode 100644
index 00000000000..9dc78541994
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewMaintainabilityFilter-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import NewMaintainabilityFilter from '../NewMaintainabilityFilter';
+
+it('renders', () => {
+ expect(shallow(<NewMaintainabilityFilter query={{}} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewReliabilityFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewReliabilityFilter-test.tsx
new file mode 100644
index 00000000000..2f97df9e72e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewReliabilityFilter-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import NewReliabilityFilter from '../NewReliabilityFilter';
+
+it('renders', () => {
+ expect(shallow(<NewReliabilityFilter query={{}} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewSecurityFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewSecurityFilter-test.tsx
new file mode 100644
index 00000000000..a3771637e92
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewSecurityFilter-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import NewSecurityFilter from '../NewSecurityFilter';
+
+it('renders', () => {
+ expect(shallow(<NewSecurityFilter query={{}} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx
new file mode 100644
index 00000000000..02316f2426b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx
@@ -0,0 +1,33 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import QualityGateFilter from '../QualityGateFilter';
+
+it('renders', () => {
+ const wrapper = shallow(<QualityGateFilter query={{}} />);
+ expect(wrapper).toMatchSnapshot();
+
+ const renderOption = wrapper.prop('renderOption');
+ expect(renderOption(2, false)).toMatchSnapshot();
+
+ const getFacetValueForOption = wrapper.prop('getFacetValueForOption');
+ expect(getFacetValueForOption({ ERROR: 1, WARN: 2, OK: 3 }, 'WARN')).toBe(2);
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/ReliabilityFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/ReliabilityFilter-test.tsx
new file mode 100644
index 00000000000..8ca23ed4f50
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/ReliabilityFilter-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import ReliabilityFilter from '../ReliabilityFilter';
+
+it('renders', () => {
+ expect(shallow(<ReliabilityFilter query={{}} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilter-test.js b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilter-test.tsx
index 9e6594db965..2724f834a36 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilter-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilter-test.tsx
@@ -17,23 +17,42 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import SearchFilter from '../SearchFilter';
+import { change } from '../../../../helpers/testUtils';
it('should render correctly without any search query', () => {
- const wrapper = shallow(<SearchFilter handleSearch={() => {}} query={{ search: null }} />);
+ const wrapper = shallow(<SearchFilter handleSearch={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();
});
it('should render with a search query', () => {
- const wrapper = shallow(<SearchFilter handleSearch={() => {}} query={{ search: 'foo' }} />);
+ const wrapper = shallow(<SearchFilter handleSearch={jest.fn()} query={{ search: 'foo' }} />);
expect(wrapper).toMatchSnapshot();
});
it('should display a help message when there is less than 2 characters', () => {
- const wrapper = shallow(<SearchFilter handleSearch={() => {}} query={{ search: 'a' }} />);
+ const wrapper = shallow(<SearchFilter handleSearch={jest.fn()} query={{ search: 'a' }} />);
expect(wrapper).toMatchSnapshot();
wrapper.setState({ userQuery: 'foo' });
expect(wrapper).toMatchSnapshot();
});
+
+it('searches', () => {
+ const handleSearch = jest.fn();
+ const wrapper = shallow(<SearchFilter handleSearch={handleSearch} query={{}} />);
+
+ change(wrapper.find('input'), 'a');
+ expect(handleSearch).not.toBeCalled();
+
+ change(wrapper.find('input'), 'abc');
+ expect(handleSearch).toBeCalledWith('abc');
+});
+
+it('updates state to new props', () => {
+ const wrapper = shallow(<SearchFilter handleSearch={jest.fn()} query={{ search: 'abc' }} />);
+ expect(wrapper.state()).toEqual({ userQuery: 'abc' });
+ wrapper.setProps({ query: { search: 'def' } });
+ expect(wrapper.state()).toEqual({ userQuery: 'def' });
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx
new file mode 100644
index 00000000000..7825caf1ca2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx
@@ -0,0 +1,37 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import SearchFilterContainer from '../SearchFilterContainer';
+
+// mocking lodash, because mocking timers is now working for some reason :'(
+jest.mock('lodash', () => {
+ const lodash = require.requireActual('lodash');
+ lodash.debounce = (fn: Function) => (...args: any[]) => fn(args);
+ return lodash;
+});
+
+it('searches', () => {
+ const push = jest.fn();
+ const wrapper = shallow(<SearchFilterContainer query={{}} />, { context: { router: { push } } });
+ expect(wrapper).toMatchSnapshot();
+ wrapper.prop('handleSearch')('foo');
+ expect(push).toBeCalledWith({ pathname: '/projects', query: { search: 'foo' } });
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.js b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx
index c8f5a59ea74..2d12621ec21 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx
@@ -17,44 +17,41 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import SearchableFilterFooter from '../SearchableFilterFooter';
-const languageOptions = [
- { label: 'Flex', value: 'flex' },
- { label: 'PHP', value: 'php' },
- { label: 'Python', value: 'py' }
-];
-const tagOptions = [
- { label: 'lang', value: 'lang' },
- { label: 'sonar', value: 'sonar' },
+const options = [
+ { label: 'java', value: 'java' },
+ { label: 'js', value: 'js' },
{ label: 'csharp', value: 'csharp' }
];
-const fakeRouter = { push: () => {} };
-it('should render the languages without the ones in the facet', () => {
+it('should render items without the ones in the facet', () => {
const wrapper = shallow(
<SearchableFilterFooter
property="languages"
- query={{ languages: null }}
- options={languageOptions}
- router={fakeRouter}
- />
+ query={{ languages: ['java'] }}
+ options={options}
+ />,
+ { context: { router: { push: jest.fn() } } }
);
- expect(wrapper).toMatchSnapshot();
- expect(wrapper.find('Select').props().options.length).toBe(3);
+ expect(wrapper.find('Select').prop('options')).toMatchSnapshot();
});
-it('should render the tags without the ones in the facet', () => {
+it('should render items without the ones in the facet', () => {
+ const push = jest.fn();
const wrapper = shallow(
<SearchableFilterFooter
- property="tags"
- query={{ tags: ['java'] }}
- options={tagOptions}
- isFavorite={true}
- />
+ property="languages"
+ query={{ languages: ['java'] }}
+ options={options}
+ />,
+ { context: { router: { push } } }
);
- expect(wrapper).toMatchSnapshot();
- expect(wrapper.find('Select').props().options.length).toBe(3);
+ (wrapper.find('Select').prop('onChange') as Function)({ value: 'js' });
+ expect(push).toBeCalledWith({
+ pathname: '/projects',
+ query: { languages: 'java,js' }
+ });
});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterOption-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterOption-test.tsx
new file mode 100644
index 00000000000..c5af6e76c6e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterOption-test.tsx
@@ -0,0 +1,29 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import SearchableFilterOption from '../SearchableFilterOption';
+
+it('renders', () => {
+ expect(shallow(<SearchableFilterOption optionKey="foo" />)).toMatchSnapshot();
+ expect(
+ shallow(<SearchableFilterOption option={{ name: 'bar' }} optionKey="foo" />)
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SecurityFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SecurityFilter-test.tsx
new file mode 100644
index 00000000000..05c83d65c41
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SecurityFilter-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import SecurityFilter from '../SecurityFilter';
+
+it('renders', () => {
+ expect(shallow(<SecurityFilter query={{}} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SizeFilter-test.tsx b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SizeFilter-test.tsx
new file mode 100644
index 00000000000..486594f766d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/SizeFilter-test.tsx
@@ -0,0 +1,44 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import SizeFilter from '../SizeFilter';
+
+it('renders', () => {
+ const wrapper = shallow(<SizeFilter query={{}} />);
+ expect(wrapper).toMatchSnapshot();
+
+ const renderOption = wrapper.prop('renderOption');
+ expect(renderOption(2, false)).toMatchSnapshot();
+
+ const getFacetValueForOption = wrapper.prop('getFacetValueForOption');
+ expect(
+ getFacetValueForOption(
+ {
+ '*-1000.0': 1,
+ '1000.0-10000.0': 42,
+ '10000.0-100000.0': 14,
+ '100000.0-500000.0': 13,
+ '500000.0-*': 8
+ },
+ 2
+ )
+ ).toBe(42);
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/TagsFilter-test.js b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/TagsFilter-test.tsx
index ef6d8518c8d..bfc0790d00c 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/TagsFilter-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/TagsFilter-test.tsx
@@ -17,18 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
import TagsFilter from '../TagsFilter';
const tags = ['lang', 'sonar', 'csharp', 'dotnet', 'it', 'net'];
const tagsFacet = { lang: 4, sonar: 3, csharp: 1 };
-const fakeRouter = { push: () => {} };
it('should render the tags without the ones in the facet', () => {
- const wrapper = shallow(
- <TagsFilter query={{ tags: null }} router={fakeRouter} facet={tagsFacet} />
- );
+ const wrapper = shallow(<TagsFilter query={{ tags: null }} facet={tagsFacet} />);
expect(wrapper).toMatchSnapshot();
wrapper.setState({ tags });
expect(wrapper).toMatchSnapshot();
@@ -39,7 +36,6 @@ it('should render the tags facet with the selected tags', () => {
<TagsFilter
query={{ tags: ['lang', 'sonar'] }}
value={['lang', 'sonar']}
- router={fakeRouter}
facet={tagsFacet}
isFavorite={true}
/>
@@ -53,7 +49,6 @@ it('should render maximum 10 tags in the searchbox results', () => {
<TagsFilter
query={{ languages: ['java', 'ad'] }}
value={['java', 'ad']}
- router={fakeRouter}
facet={{ ...tagsFacet, ad: 1 }}
isFavorite={true}
/>
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/CoverageFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/CoverageFilter-test.tsx.snap
new file mode 100644
index 00000000000..d475b329ae7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/CoverageFilter-test.tsx.snap
@@ -0,0 +1,42 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Connect(Filter)
+ getFacetValueForOption={[Function]}
+ header={
+ <FilterHeader
+ name="metric_domain.Coverage"
+ />
+ }
+ highlightUnder={1}
+ highlightUnderMax={5}
+ options={
+ Array [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ ]
+ }
+ property="coverage"
+ query={Object {}}
+ renderOption={[Function]}
+/>
+`;
+
+exports[`renders 2`] = `
+<span>
+ <CoverageRating
+ muted={true}
+ size="small"
+ value={75}
+ />
+ <span
+ className="spacer-left"
+ >
+ &lt; 80%
+ </span>
+</span>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/DuplicationsFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/DuplicationsFilter-test.tsx.snap
new file mode 100644
index 00000000000..ff87f83526b
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/DuplicationsFilter-test.tsx.snap
@@ -0,0 +1,56 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Connect(Filter)
+ getFacetValueForOption={[Function]}
+ header={
+ <FilterHeader
+ name="metric_domain.Duplications"
+ />
+ }
+ highlightUnder={1}
+ highlightUnderMax={5}
+ options={
+ Array [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ ]
+ }
+ property="duplications"
+ query={Object {}}
+ renderOption={[Function]}
+/>
+`;
+
+exports[`renders 2`] = `
+<span>
+ <DuplicationsRating
+ muted={true}
+ size="small"
+ value={4}
+ />
+ <span
+ className="spacer-left"
+ >
+ ≥ 3%
+ </span>
+</span>
+`;
+
+exports[`renders 3`] = `
+<span>
+ <span
+ className="spacer-left"
+ >
+ <span
+ className="big-spacer-left"
+ >
+ no_data
+ </span>
+ </span>
+</span>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap
new file mode 100644
index 00000000000..684a65f0ce8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap
@@ -0,0 +1,596 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`highlights under 1`] = `
+<div
+ className="search-navigator-facet-box"
+ data-key="foo"
+>
+ <div
+ className="search-navigator-facet-list projects-facet-list"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={1}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 1,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </Link>
+ <div
+ className="search-navigator-facet-highlight-under-container"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={2}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 2,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={3}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 3,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </Link>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`hightlights under selected 1`] = `
+<div
+ className="search-navigator-facet-box"
+ data-key="foo"
+>
+ <div
+ className="search-navigator-facet-list projects-facet-list"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={1}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 1,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </Link>
+ <div
+ className="search-navigator-facet-highlight-under-container"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet active"
+ data-key={2}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {},
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={3}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 3,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </Link>
+ </div>
+ </div>
+</div>
+`;
+
+exports[`renders 1`] = `
+<div
+ className="search-navigator-facet-box"
+ data-key="foo"
+>
+ <div
+ className="search-navigator-facet-list projects-facet-list"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={1}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 1,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={2}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 2,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={3}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 3,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </Link>
+ </div>
+</div>
+`;
+
+exports[`renders facet bar chart 1`] = `
+<div
+ className="search-navigator-facet-box"
+ data-key="foo"
+>
+ <div
+ className="search-navigator-facet-list projects-facet-list"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key="a"
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": "a",
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ a
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 17
+ <div
+ className="projects-facet-bar"
+ >
+ <div
+ className="projects-facet-bar-inner"
+ style={
+ Object {
+ "width": 42.5,
+ }
+ }
+ />
+ </div>
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key="b"
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": "b",
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ b
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 15
+ <div
+ className="projects-facet-bar"
+ >
+ <div
+ className="projects-facet-bar-inner"
+ style={
+ Object {
+ "width": 37.5,
+ }
+ }
+ />
+ </div>
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key="c"
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": "c",
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ c
+ </span>
+ <span
+ className="facet-stat"
+ >
+ 24
+ <div
+ className="projects-facet-bar"
+ >
+ <div
+ className="projects-facet-bar-inner"
+ style={
+ Object {
+ "width": 60,
+ }
+ }
+ />
+ </div>
+ </span>
+ </Link>
+ </div>
+</div>
+`;
+
+exports[`renders header and footer 1`] = `
+<div
+ className="search-navigator-facet-box"
+ data-key="foo"
+>
+ <header />
+ <div
+ className="search-navigator-facet-list projects-facet-list"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={1}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 1,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={2}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 2,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={3}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 3,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </Link>
+ </div>
+ <footer />
+</div>
+`;
+
+exports[`renders multiple selected 1`] = `
+<div
+ className="search-navigator-facet-box"
+ data-key="foo"
+>
+ <div
+ className="search-navigator-facet-list projects-facet-list"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet active"
+ data-key={1}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": "2",
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet active"
+ data-key={2}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": "1",
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={3}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": "1,2,3",
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </Link>
+ </div>
+</div>
+`;
+
+exports[`renders no results 1`] = `
+<div
+ className="search-navigator-facet-box"
+ data-key="foo"
+>
+ <div
+ className="search-navigator-facet-empty"
+ >
+ no_results
+ </div>
+</div>
+`;
+
+exports[`renders selected 1`] = `
+<div
+ className="search-navigator-facet-box"
+ data-key="foo"
+>
+ <div
+ className="search-navigator-facet-list projects-facet-list"
+ >
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={1}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 1,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 1
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet active"
+ data-key={2}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {},
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 2
+ </span>
+ </Link>
+ <Link
+ className="facet search-navigator-facet projects-facet"
+ data-key={3}
+ onClick={[Function]}
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/projects",
+ "query": Object {
+ "foo": 3,
+ },
+ }
+ }
+ >
+ <span
+ className="facet-name"
+ >
+ 3
+ </span>
+ </Link>
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/FilterHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/FilterHeader-test.tsx.snap
new file mode 100644
index 00000000000..b1f4611eae9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/FilterHeader-test.tsx.snap
@@ -0,0 +1,18 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="search-navigator-facet-header projects-facet-header"
+>
+ foo
+</div>
+`;
+
+exports[`renders with children 1`] = `
+<div
+ className="search-navigator-facet-header projects-facet-header"
+>
+ foo
+ <div />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/IssuesFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/IssuesFilter-test.tsx.snap
new file mode 100644
index 00000000000..2177f4c16e9
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/IssuesFilter-test.tsx.snap
@@ -0,0 +1,39 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Connect(Filter)
+ getFacetValueForOption={[Function]}
+ header={
+ <FilterHeader
+ name="metric_domain.bugs"
+ />
+ }
+ highlightUnder={1}
+ options={
+ Array [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ ]
+ }
+ query={Object {}}
+ renderOption={[Function]}
+/>
+`;
+
+exports[`renders 2`] = `
+<span>
+ <Rating
+ muted={true}
+ small={true}
+ value={2}
+ />
+ <span
+ className="note spacer-left"
+ >
+ and_worse
+ </span>
+</span>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.js.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap
index 78b7575ab4e..38b72391839 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap
@@ -66,15 +66,9 @@ exports[`should render maximum 10 languages in the searchbox results 1`] = `
],
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
}
getFacetValueForOption={[Function]}
- halfWidth={false}
header={
<FilterHeader
name="projects.facets.languages"
@@ -145,15 +139,9 @@ exports[`should render the languages facet with the selected languages 1`] = `
],
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
}
getFacetValueForOption={[Function]}
- halfWidth={false}
header={
<FilterHeader
name="projects.facets.languages"
@@ -327,11 +315,6 @@ exports[`should render the languages facet with the selected languages 2`] = `
],
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
</div>
`;
@@ -369,15 +352,9 @@ exports[`should render the languages without the ones in the facet 1`] = `
"languages": null,
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
}
getFacetValueForOption={[Function]}
- halfWidth={false}
header={
<FilterHeader
name="projects.facets.languages"
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/MaintainabilityFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/MaintainabilityFilter-test.tsx.snap
new file mode 100644
index 00000000000..ef3d9057b14
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/MaintainabilityFilter-test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<IssuesFilter
+ name="Maintainability"
+ property="maintainability"
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewCoverageFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewCoverageFilter-test.tsx.snap
new file mode 100644
index 00000000000..08f6a0bcf1f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewCoverageFilter-test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<CoverageFilter
+ className="leak-facet-box"
+ property="new_coverage"
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewDuplicationsFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewDuplicationsFilter-test.tsx.snap
new file mode 100644
index 00000000000..a75df7d187c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewDuplicationsFilter-test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<DuplicationsFilter
+ className="leak-facet-box"
+ property="new_duplications"
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewLinesFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewLinesFilter-test.tsx.snap
new file mode 100644
index 00000000000..4fde9336313
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewLinesFilter-test.tsx.snap
@@ -0,0 +1,32 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Connect(Filter)
+ className="leak-facet-box"
+ getFacetValueForOption={[Function]}
+ header={
+ <FilterHeader
+ name="projects.facets.new_lines"
+ />
+ }
+ highlightUnder={1}
+ options={
+ Array [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ ]
+ }
+ property="new_lines"
+ query={Object {}}
+ renderOption={[Function]}
+/>
+`;
+
+exports[`renders 2`] = `
+<span>
+ ≥ 1k
+</span>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewMaintainabilityFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewMaintainabilityFilter-test.tsx.snap
new file mode 100644
index 00000000000..dc9057aa022
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewMaintainabilityFilter-test.tsx.snap
@@ -0,0 +1,22 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<IssuesFilter
+ className="leak-facet-box"
+ headerDetail={
+ <span
+ className="note little-spacer-left"
+ >
+ (
+ <CodeSmellIcon
+ className="little-spacer-right"
+ />
+ metric.code_smells.name
+ )
+ </span>
+ }
+ name="Maintainability"
+ property="new_maintainability"
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewReliabilityFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewReliabilityFilter-test.tsx.snap
new file mode 100644
index 00000000000..b93c2e876b5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewReliabilityFilter-test.tsx.snap
@@ -0,0 +1,22 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<IssuesFilter
+ className="leak-facet-box"
+ headerDetail={
+ <span
+ className="note little-spacer-left"
+ >
+ (
+ <BugIcon
+ className="little-spacer-right"
+ />
+ metric.bugs.name
+ )
+ </span>
+ }
+ name="Reliability"
+ property="new_reliability"
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewSecurityFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewSecurityFilter-test.tsx.snap
new file mode 100644
index 00000000000..f0005022a9d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewSecurityFilter-test.tsx.snap
@@ -0,0 +1,22 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<IssuesFilter
+ className="leak-facet-box"
+ headerDetail={
+ <span
+ className="note little-spacer-left"
+ >
+ (
+ <VulnerabilityIcon
+ className="little-spacer-right"
+ />
+ metric.vulnerabilities.name
+ )
+ </span>
+ }
+ name="Security"
+ property="new_security"
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap
new file mode 100644
index 00000000000..c20cd46404a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap
@@ -0,0 +1,30 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Connect(Filter)
+ getFacetValueForOption={[Function]}
+ header={
+ <FilterHeader
+ name="projects.facets.quality_gate"
+ />
+ }
+ options={
+ Array [
+ "OK",
+ "WARN",
+ "ERROR",
+ ]
+ }
+ property="gate"
+ query={Object {}}
+ renderOption={[Function]}
+/>
+`;
+
+exports[`renders 2`] = `
+<Level
+ level={2}
+ muted={true}
+ small={true}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/ReliabilityFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/ReliabilityFilter-test.tsx.snap
new file mode 100644
index 00000000000..4494914b160
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/ReliabilityFilter-test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<IssuesFilter
+ name="Reliability"
+ property="reliability"
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.js.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.tsx.snap
index 86e0c761aa1..86e0c761aa1 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.tsx.snap
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilterContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilterContainer-test.tsx.snap
new file mode 100644
index 00000000000..d3d2ac3eb74
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilterContainer-test.tsx.snap
@@ -0,0 +1,8 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`searches 1`] = `
+<SearchFilter
+ handleSearch={[Function]}
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterFooter-test.js.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterFooter-test.js.snap
deleted file mode 100644
index b0346a32181..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterFooter-test.js.snap
+++ /dev/null
@@ -1,131 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render the languages without the ones in the facet 1`] = `
-<div
- className="search-navigator-facet-footer projects-facet-footer"
->
- <Select
- addLabelText="Add \\"{label}\\"?"
- arrowRenderer={[Function]}
- autosize={true}
- backspaceRemoves={true}
- backspaceToRemoveMessage="Press backspace to remove {label}"
- className="input-super-large"
- clearAllText="Clear all"
- clearRenderer={[Function]}
- clearValueText="Clear value"
- clearable={false}
- deleteRemoves={true}
- delimiter=","
- disabled={false}
- escapeClearsValue={true}
- filterOptions={[Function]}
- ignoreAccents={true}
- ignoreCase={true}
- inputProps={Object {}}
- isLoading={false}
- joinValues={false}
- labelKey="label"
- matchPos="any"
- matchProp="any"
- menuBuffer={0}
- menuRenderer={[Function]}
- multi={false}
- noResultsText="No results found"
- onBlurResetsInput={true}
- onChange={[Function]}
- onCloseResetsInput={true}
- optionComponent={[Function]}
- options={
- Array [
- Object {
- "label": "Flex",
- "value": "flex",
- },
- Object {
- "label": "PHP",
- "value": "php",
- },
- Object {
- "label": "Python",
- "value": "py",
- },
- ]
- }
- pageSize={5}
- placeholder="search_verb"
- required={false}
- scrollMenuIntoView={true}
- searchable={true}
- simpleValue={false}
- tabSelectsValue={true}
- valueComponent={[Function]}
- valueKey="value"
- />
-</div>
-`;
-
-exports[`should render the tags without the ones in the facet 1`] = `
-<div
- className="search-navigator-facet-footer projects-facet-footer"
->
- <Select
- addLabelText="Add \\"{label}\\"?"
- arrowRenderer={[Function]}
- autosize={true}
- backspaceRemoves={true}
- backspaceToRemoveMessage="Press backspace to remove {label}"
- className="input-super-large"
- clearAllText="Clear all"
- clearRenderer={[Function]}
- clearValueText="Clear value"
- clearable={false}
- deleteRemoves={true}
- delimiter=","
- disabled={false}
- escapeClearsValue={true}
- filterOptions={[Function]}
- ignoreAccents={true}
- ignoreCase={true}
- inputProps={Object {}}
- isLoading={false}
- joinValues={false}
- labelKey="label"
- matchPos="any"
- matchProp="any"
- menuBuffer={0}
- menuRenderer={[Function]}
- multi={false}
- noResultsText="No results found"
- onBlurResetsInput={true}
- onChange={[Function]}
- onCloseResetsInput={true}
- optionComponent={[Function]}
- options={
- Array [
- Object {
- "label": "lang",
- "value": "lang",
- },
- Object {
- "label": "sonar",
- "value": "sonar",
- },
- Object {
- "label": "csharp",
- "value": "csharp",
- },
- ]
- }
- pageSize={5}
- placeholder="search_verb"
- required={false}
- scrollMenuIntoView={true}
- searchable={true}
- simpleValue={false}
- tabSelectsValue={true}
- valueComponent={[Function]}
- valueKey="value"
- />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterFooter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterFooter-test.tsx.snap
new file mode 100644
index 00000000000..6cf27012dfd
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterFooter-test.tsx.snap
@@ -0,0 +1,18 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render items without the ones in the facet 1`] = `
+Array [
+ Object {
+ "label": "java",
+ "value": "java",
+ },
+ Object {
+ "label": "js",
+ "value": "js",
+ },
+ Object {
+ "label": "csharp",
+ "value": "csharp",
+ },
+]
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterOption-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterOption-test.tsx.snap
new file mode 100644
index 00000000000..43960a58f7e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchableFilterOption-test.tsx.snap
@@ -0,0 +1,13 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<span>
+ foo
+</span>
+`;
+
+exports[`renders 2`] = `
+<span>
+ bar
+</span>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SecurityFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SecurityFilter-test.tsx.snap
new file mode 100644
index 00000000000..7142354de29
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SecurityFilter-test.tsx.snap
@@ -0,0 +1,9 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<IssuesFilter
+ name="Security"
+ property="security"
+ query={Object {}}
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SizeFilter-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SizeFilter-test.tsx.snap
new file mode 100644
index 00000000000..e4ec3a9aa79
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SizeFilter-test.tsx.snap
@@ -0,0 +1,40 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Connect(Filter)
+ getFacetValueForOption={[Function]}
+ header={
+ <FilterHeader
+ name="metric_domain.Size"
+ />
+ }
+ highlightUnder={1}
+ options={
+ Array [
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ ]
+ }
+ property="size"
+ query={Object {}}
+ renderOption={[Function]}
+/>
+`;
+
+exports[`renders 2`] = `
+<span>
+ <SizeRating
+ muted={true}
+ small={true}
+ value={5000}
+ />
+ <span
+ className="spacer-left"
+ >
+ ≥ 1k
+ </span>
+</span>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.js.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap
index c184c5e901a..22f13a9b832 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap
@@ -69,15 +69,9 @@ exports[`should render maximum 10 tags in the searchbox results 1`] = `
],
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
}
getFacetValueForOption={[Function]}
- halfWidth={false}
header={
<FilterHeader
name="projects.facets.tags"
@@ -136,15 +130,9 @@ exports[`should render the tags facet with the selected tags 1`] = `
],
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
}
getFacetValueForOption={[Function]}
- halfWidth={false}
header={
<FilterHeader
name="projects.facets.tags"
@@ -288,11 +276,6 @@ exports[`should render the tags facet with the selected tags 2`] = `
],
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
</div>
`;
@@ -318,15 +301,9 @@ exports[`should render the tags without the ones in the facet 1`] = `
"tags": null,
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
}
getFacetValueForOption={[Function]}
- halfWidth={false}
header={
<FilterHeader
name="projects.facets.tags"
@@ -385,15 +362,9 @@ exports[`should render the tags without the ones in the facet 2`] = `
"tags": null,
}
}
- router={
- Object {
- "push": [Function],
- }
- }
/>
}
getFacetValueForOption={[Function]}
- halfWidth={false}
header={
<FilterHeader
name="projects.facets.tags"
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/utils-test.ts
new file mode 100644
index 00000000000..a888c165cc0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/utils-test.ts
@@ -0,0 +1,54 @@
+/*
+ * 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 { getFilterUrl } from '../utils';
+
+it('works in trivial cases', () => {
+ expect(getFilterUrl({ query: {} }, {})).toEqual({ pathname: '/projects', query: {} });
+ expect(getFilterUrl({ query: { foo: 'bar' } }, {})).toEqual({
+ pathname: '/projects',
+ query: { foo: 'bar' }
+ });
+ expect(getFilterUrl({ query: {} }, { foo: 'bar' })).toEqual({
+ pathname: '/projects',
+ query: { foo: 'bar' }
+ });
+ expect(getFilterUrl({ query: { foo: 'bar' } }, { foo: 'qux' })).toEqual({
+ pathname: '/projects',
+ query: { foo: 'qux' }
+ });
+ expect(getFilterUrl({ query: { foo: 'bar' } }, { baz: 'qux' })).toEqual({
+ pathname: '/projects',
+ query: { foo: 'bar', baz: 'qux' }
+ });
+});
+
+it('works for favorites', () => {
+ expect(getFilterUrl({ isFavorite: true, query: {} }, { foo: 'bar' })).toEqual({
+ pathname: '/projects/favorite',
+ query: { foo: 'bar' }
+ });
+});
+
+it('works with organization', () => {
+ expect(getFilterUrl({ organization: { key: 'org' }, query: {} }, { foo: 'bar' })).toEqual({
+ pathname: '/organizations/org/projects',
+ query: { foo: 'bar' }
+ });
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/filters/utils.js b/server/sonar-web/src/main/js/apps/projects/filters/utils.ts
index 5be12186542..18887a3cc38 100644
--- a/server/sonar-web/src/main/js/apps/projects/filters/utils.js
+++ b/server/sonar-web/src/main/js/apps/projects/filters/utils.ts
@@ -18,17 +18,24 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { each, isNil, omitBy } from 'lodash';
+import { RawQuery } from '../../../helpers/query';
-export const getFilterUrl = (ownProps, part) => {
+interface OwnProps {
+ isFavorite?: boolean;
+ organization?: { key: string };
+ query: RawQuery;
+}
+
+export function getFilterUrl(ownProps: OwnProps, part: RawQuery) {
const basePathName = ownProps.organization
? `/organizations/${ownProps.organization.key}/projects`
: '/projects';
const pathname = basePathName + (ownProps.isFavorite ? '/favorite' : '');
- const query = omitBy({ ...ownProps.query, ...part }, isNil);
+ const query: RawQuery = omitBy({ ...ownProps.query, ...part }, isNil);
each(query, (value, key) => {
if (Array.isArray(value)) {
query[key] = value.join(',');
}
});
return { pathname, query };
-};
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/types.ts b/server/sonar-web/src/main/js/apps/projects/types.ts
new file mode 100644
index 00000000000..a78e422460f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/types.ts
@@ -0,0 +1,25 @@
+/*
+ * 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.
+ */
+export interface Project {
+ key: string;
+ measures: { [key: string]: string };
+ name: string;
+ organization?: { name: string };
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/utils.js b/server/sonar-web/src/main/js/apps/projects/utils.ts
index b322061f662..6a03e7b57cf 100644
--- a/server/sonar-web/src/main/js/apps/projects/utils.js
+++ b/server/sonar-web/src/main/js/apps/projects/utils.ts
@@ -17,10 +17,14 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
import { translate } from '../../helpers/l10n';
-export const SORTING_METRICS = [
+interface SortingOption {
+ class?: string;
+ value: string;
+}
+
+export const SORTING_METRICS: SortingOption[] = [
{ value: 'name' },
{ value: 'analysis_date' },
{ value: 'reliability' },
@@ -31,7 +35,7 @@ export const SORTING_METRICS = [
{ value: 'size' }
];
-export const SORTING_LEAK_METRICS = [
+export const SORTING_LEAK_METRICS: SortingOption[] = [
{ value: 'name' },
{ value: 'analysis_date' },
{ value: 'new_reliability', class: 'projects-leak-sorting-option' },
@@ -42,7 +46,7 @@ export const SORTING_LEAK_METRICS = [
{ value: 'new_lines', class: 'projects-leak-sorting-option' }
];
-export const SORTING_SWITCH = {
+export const SORTING_SWITCH: { [x: string]: string } = {
analysis_date: 'analysis_date',
name: 'name',
reliability: 'new_reliability',
@@ -70,11 +74,11 @@ export const VISUALIZATIONS = [
'duplications'
];
-export const localizeSorting = (sort /*: ?string */) => {
+export function localizeSorting(sort?: string): string {
return translate('projects.sort', sort || 'name');
-};
+}
-export function parseSorting(sort /*: string */) /*: { sortValue: string, sortDesc: boolean } */ {
+export function parseSorting(sort: string): { sortValue: string; sortDesc: boolean } {
const desc = sort[0] === '-';
return { sortValue: desc ? sort.substr(1) : sort, sortDesc: desc };
}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.js b/server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.tsx
index 5df77506a7a..1feed7f3c75 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.tsx
@@ -17,19 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import SimpleBubbleChart from './SimpleBubbleChart';
+import { Project } from '../types';
-export default class Duplications extends React.PureComponent {
- render() {
- return (
- <SimpleBubbleChart
- {...this.props}
- xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
- yMetric={{ key: 'duplicated_lines', type: 'SHORT_INT' }}
- sizeMetric={{ key: 'duplicated_blocks', type: 'SHORT_INT' }}
- />
- );
- }
+interface Props {
+ displayOrganizations: boolean;
+ projects: Project[];
+}
+
+export default function Coverage(props: Props) {
+ return (
+ <SimpleBubbleChart
+ {...props}
+ xMetric={{ key: 'complexity', type: 'SHORT_INT' }}
+ yMetric={{ key: 'coverage', type: 'PERCENT' }}
+ yDomain={[100, 0]}
+ sizeMetric={{ key: 'uncovered_lines', type: 'SHORT_INT' }}
+ />
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.js b/server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.tsx
index 66ad04d16a2..2e6c05b0dc9 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/Coverage.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Duplications.tsx
@@ -17,20 +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.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import SimpleBubbleChart from './SimpleBubbleChart';
+import { Project } from '../types';
-export default class Coverage extends React.PureComponent {
- render() {
- return (
- <SimpleBubbleChart
- {...this.props}
- xMetric={{ key: 'complexity', type: 'SHORT_INT' }}
- yMetric={{ key: 'coverage', type: 'PERCENT' }}
- yDomain={[100, 0]}
- sizeMetric={{ key: 'uncovered_lines', type: 'SHORT_INT' }}
- />
- );
- }
+interface Props {
+ displayOrganizations: boolean;
+ projects: Project[];
+}
+
+export default function Duplications(props: Props) {
+ return (
+ <SimpleBubbleChart
+ {...props}
+ xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
+ yMetric={{ key: 'duplicated_lines', type: 'SHORT_INT' }}
+ sizeMetric={{ key: 'duplicated_blocks', type: 'SHORT_INT' }}
+ />
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.js b/server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.tsx
index 886e22f2f18..9d137efb5a4 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Maintainability.tsx
@@ -17,20 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import SimpleBubbleChart from './SimpleBubbleChart';
+import { Project } from '../types';
-export default class Maintainability extends React.PureComponent {
- render() {
- return (
- <SimpleBubbleChart
- {...this.props}
- xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
- yMetric={{ key: 'sqale_index', type: 'SHORT_WORK_DUR' }}
- sizeMetric={{ key: 'code_smells', type: 'SHORT_INT' }}
- colorMetric="sqale_rating"
- />
- );
- }
+interface Props {
+ displayOrganizations: boolean;
+ projects: Project[];
+}
+
+export default function Maintainability(props: Props) {
+ return (
+ <SimpleBubbleChart
+ {...props}
+ xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
+ yMetric={{ key: 'sqale_index', type: 'SHORT_WORK_DUR' }}
+ sizeMetric={{ key: 'code_smells', type: 'SHORT_INT' }}
+ colorMetric="sqale_rating"
+ />
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.js b/server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.tsx
index 18af93db288..15778724935 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Reliability.tsx
@@ -17,20 +17,23 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import SimpleBubbleChart from './SimpleBubbleChart';
+import { Project } from '../types';
-export default class Reliability extends React.PureComponent {
- render() {
- return (
- <SimpleBubbleChart
- {...this.props}
- xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
- yMetric={{ key: 'reliability_remediation_effort', type: 'SHORT_WORK_DUR' }}
- sizeMetric={{ key: 'bugs', type: 'SHORT_INT' }}
- colorMetric="reliability_rating"
- />
- );
- }
+interface Props {
+ displayOrganizations: boolean;
+ projects: Project[];
+}
+
+export default function Reliability(props: Props) {
+ return (
+ <SimpleBubbleChart
+ {...props}
+ xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
+ yMetric={{ key: 'reliability_remediation_effort', type: 'SHORT_WORK_DUR' }}
+ sizeMetric={{ key: 'bugs', type: 'SHORT_INT' }}
+ colorMetric="reliability_rating"
+ />
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.js b/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx
index 880b07fa10a..8f7d1aedbbc 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx
@@ -17,8 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
+import { Project } from '../types';
import ColorRatingsLegend from '../../../components/charts/ColorRatingsLegend';
import BubbleChart from '../../../components/charts/BubbleChart';
import { formatMeasure } from '../../../helpers/measures';
@@ -26,15 +26,6 @@ import { translate, translateWithParameters } from '../../../helpers/l10n';
import { RATING_COLORS } from '../../../helpers/constants';
import { getProjectUrl } from '../../../helpers/urls';
-/*::
-type Project = {
- key: string,
- measures: { [string]: string },
- name: string,
- organization?: { name: string }
-};
-*/
-
const X_METRIC = 'sqale_index';
const X_METRIC_TYPE = 'SHORT_WORK_DUR';
const Y_METRIC = 'coverage';
@@ -45,26 +36,25 @@ const COLOR_METRIC_1 = 'reliability_rating';
const COLOR_METRIC_2 = 'security_rating';
const COLOR_METRIC_TYPE = 'RATING';
-export default class Risk extends React.PureComponent {
- /*:: props: {
- displayOrganizations: boolean,
- projects: Array<Project>
- };
-*/
+interface Props {
+ displayOrganizations: boolean;
+ projects: Project[];
+}
- getMetricTooltip(metric /*: { key: string, type: string } */, value /*: ?number */) {
+export default class Risk extends React.PureComponent<Props> {
+ getMetricTooltip(metric: { key: string; type: string }, value?: number) {
const name = translate('metric', metric.key, 'name');
const formattedValue = value != null ? formatMeasure(value, metric.type) : '–';
return `<div>${name}: ${formattedValue}</div>`;
}
getTooltip(
- project /*: Project */,
- x /*: ?number */,
- y /*: ?number */,
- size /*: ?number */,
- color1 /*: ?number */,
- color2 /*: ?number */
+ project: Project,
+ x?: number,
+ y?: number,
+ size?: number,
+ color1?: number,
+ color2?: number
) {
const fullProjectName =
this.props.displayOrganizations && project.organization
@@ -83,14 +73,18 @@ export default class Risk extends React.PureComponent {
render() {
const items = this.props.projects.map(project => {
- const x = project.measures[X_METRIC] != null ? Number(project.measures[X_METRIC]) : null;
- const y = project.measures[Y_METRIC] != null ? Number(project.measures[Y_METRIC]) : null;
+ const x = project.measures[X_METRIC] != null ? Number(project.measures[X_METRIC]) : undefined;
+ const y = project.measures[Y_METRIC] != null ? Number(project.measures[Y_METRIC]) : undefined;
const size =
- project.measures[SIZE_METRIC] != null ? Number(project.measures[SIZE_METRIC]) : null;
+ project.measures[SIZE_METRIC] != null ? Number(project.measures[SIZE_METRIC]) : undefined;
const color1 =
- project.measures[COLOR_METRIC_1] != null ? Number(project.measures[COLOR_METRIC_1]) : null;
+ project.measures[COLOR_METRIC_1] != null
+ ? Number(project.measures[COLOR_METRIC_1])
+ : undefined;
const color2 =
- project.measures[COLOR_METRIC_2] != null ? Number(project.measures[COLOR_METRIC_2]) : null;
+ project.measures[COLOR_METRIC_2] != null
+ ? Number(project.measures[COLOR_METRIC_2])
+ : undefined;
return {
x: x || 0,
y: y || 0,
@@ -104,8 +98,10 @@ export default class Risk extends React.PureComponent {
link: getProjectUrl(project.key)
};
});
- const formatXTick = tick => formatMeasure(tick, X_METRIC_TYPE);
- const formatYTick = tick => formatMeasure(tick, Y_METRIC_TYPE);
+
+ const formatXTick = (tick: number) => formatMeasure(tick, X_METRIC_TYPE);
+ const formatYTick = (tick: number) => formatMeasure(tick, Y_METRIC_TYPE);
+
return (
<div>
<BubbleChart
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Security.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Security.tsx
new file mode 100644
index 00000000000..ca328013284
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Security.tsx
@@ -0,0 +1,39 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import SimpleBubbleChart from './SimpleBubbleChart';
+import { Project } from '../types';
+
+interface Props {
+ displayOrganizations: boolean;
+ projects: Project[];
+}
+
+export default function Security(props: Props) {
+ return (
+ <SimpleBubbleChart
+ {...props}
+ xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
+ yMetric={{ key: 'security_remediation_effort', type: 'SHORT_WORK_DUR' }}
+ sizeMetric={{ key: 'vulnerabilities', type: 'SHORT_INT' }}
+ colorMetric="security_rating"
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.js b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx
index 88ca5cafbef..0b3a866353e 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx
@@ -17,53 +17,38 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import ColorRatingsLegend from '../../../components/charts/ColorRatingsLegend';
import BubbleChart from '../../../components/charts/BubbleChart';
import { formatMeasure } from '../../../helpers/measures';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { RATING_COLORS } from '../../../helpers/constants';
import { getProjectUrl } from '../../../helpers/urls';
+import { Project } from '../types';
-/*::
-type Metric = { key: string, type: string };
-*/
-
-/*::
-type Project = {
- key: string,
- measures: { [string]: string },
- name: string,
- organization?: { name: string }
-};
-*/
+export interface Metric {
+ key: string;
+ type: string;
+}
-export default class SimpleBubbleChart extends React.PureComponent {
- /*:: props: {
- displayOrganizations: boolean,
- projects: Array<Project>,
- sizeMetric: Metric,
- xMetric: Metric,
- yDomain?: [number, number],
- yMetric: Metric,
- colorMetric?: string
- };
-*/
+interface Props {
+ colorMetric?: string;
+ displayOrganizations: boolean;
+ projects: Project[];
+ sizeMetric: Metric;
+ xMetric: Metric;
+ yDomain?: [number, number];
+ yMetric: Metric;
+}
- getMetricTooltip(metric /*: Metric */, value /*: ?number */) {
+export default class SimpleBubbleChart extends React.PureComponent<Props> {
+ getMetricTooltip(metric: Metric, value?: number) {
const name = translate('metric', metric.key, 'name');
const formattedValue = value != null ? formatMeasure(value, metric.type) : '–';
return `<div>${name}: ${formattedValue}</div>`;
}
- getTooltip(
- project /*: Project */,
- x /*: ?number */,
- y /*: ?number */,
- size /*: ?number */,
- color /*: ?number */
- ) {
+ getTooltip(project: Project, x?: number, y?: number, size?: number, color?: number) {
const fullProjectName =
this.props.displayOrganizations && project.organization
? `${project.organization.name} / <strong>${project.name}</strong>`
@@ -77,8 +62,8 @@ export default class SimpleBubbleChart extends React.PureComponent {
];
if (color) {
- // $FlowFixMe if `color` is defined then `this.props.colorMetric` is defined too
- this.getMetricTooltip({ key: this.props.colorMetric, type: 'RATING' }, color);
+ // if `color` is defined then `this.props.colorMetric` is defined too
+ this.getMetricTooltip({ key: this.props.colorMetric!, type: 'RATING' }, color);
}
return `<div class="text-left">${inner.join('')}</div>`;
@@ -91,13 +76,13 @@ export default class SimpleBubbleChart extends React.PureComponent {
.filter(project => colorMetric == null || project.measures[colorMetric] !== null)
.map(project => {
const x =
- project.measures[xMetric.key] != null ? Number(project.measures[xMetric.key]) : null;
+ project.measures[xMetric.key] != null ? Number(project.measures[xMetric.key]) : undefined;
const y =
- project.measures[yMetric.key] != null ? Number(project.measures[yMetric.key]) : null;
+ project.measures[yMetric.key] != null ? Number(project.measures[yMetric.key]) : undefined;
const size =
project.measures[sizeMetric.key] != null
? Number(project.measures[sizeMetric.key])
- : null;
+ : undefined;
const color = colorMetric ? Number(project.measures[colorMetric]) : undefined;
return {
x: x || 0,
@@ -110,8 +95,8 @@ export default class SimpleBubbleChart extends React.PureComponent {
};
});
- const formatXTick = tick => formatMeasure(tick, xMetric.type);
- const formatYTick = tick => formatMeasure(tick, yMetric.type);
+ const formatXTick = (tick: number) => formatMeasure(tick, xMetric.type);
+ const formatYTick = (tick: number) => formatMeasure(tick, yMetric.type);
return (
<div>
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.js b/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.tsx
index 65ce3c044d5..32070c159b5 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Visualizations.tsx
@@ -17,8 +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.
*/
-// @flow
-import React from 'react';
+import * as React from 'react';
import Risk from './Risk';
import Reliability from './Reliability';
import Security from './Security';
@@ -26,20 +25,20 @@ import Maintainability from './Maintainability';
import Coverage from './Coverage';
import Duplications from './Duplications';
import { localizeSorting } from '../utils';
+import { Project } from '../types';
import { translate, translateWithParameters } from '../../../helpers/l10n';
-export default class Visualizations extends React.PureComponent {
- /*:: props: {
- displayOrganizations: boolean,
- projects?: Array<*>,
- sort?: string,
- total?: number,
- visualization: string
- };
-*/
+interface Props {
+ displayOrganizations: boolean;
+ projects?: Project[];
+ sort?: string;
+ total?: number;
+ visualization: string;
+}
- renderVisualization(projects /*: Array<*> */) {
- const visualizationToComponent = {
+export default class Visualizations extends React.PureComponent<Props> {
+ renderVisualization(projects: Project[]) {
+ const visualizationToComponent: { [x: string]: any } = {
risk: Risk,
reliability: Reliability,
security: Security,
@@ -66,8 +65,7 @@ export default class Visualizations extends React.PureComponent {
<p className="note spacer-top">
{translateWithParameters(
'projects.limited_set_of_projects',
- // $FlowFixMe
- projects.length,
+ projects!.length,
localizeSorting(sort)
)}
</p>
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.js b/server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.ts
index 2de9e7bebb7..3b22d353e8e 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/VisualizationsContainer.ts
@@ -28,8 +28,8 @@ import {
areThereCustomOrganizations
} from '../../../store/rootReducer';
-const mapStateToProps = state => {
- const projectKeys = getProjects(state) || [];
+const mapStateToProps = (state: any) => {
+ const projectKeys: string[] = getProjects(state) || [];
const projects = projectKeys.map(key => {
const component = getComponent(state, key);
return {
@@ -46,4 +46,4 @@ const mapStateToProps = state => {
};
};
-export default connect(mapStateToProps)(Visualizations);
+export default connect<any, any, any>(mapStateToProps)(Visualizations);
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Coverage-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Coverage-test.tsx
new file mode 100644
index 00000000000..8e0859820c5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Coverage-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import Coverage from '../Coverage';
+
+it('renders', () => {
+ expect(shallow(<Coverage displayOrganizations={false} projects={[]} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Duplications-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Duplications-test.tsx
new file mode 100644
index 00000000000..d2aaa2cca20
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Duplications-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import Duplications from '../Duplications';
+
+it('renders', () => {
+ expect(shallow(<Duplications displayOrganizations={false} projects={[]} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Maintainability-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Maintainability-test.tsx
new file mode 100644
index 00000000000..1db7f9adafe
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Maintainability-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import Maintainability from '../Maintainability';
+
+it('renders', () => {
+ expect(shallow(<Maintainability displayOrganizations={false} projects={[]} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Reliability-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Reliability-test.tsx
new file mode 100644
index 00000000000..93946c8ce64
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Reliability-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import Reliability from '../Reliability';
+
+it('renders', () => {
+ expect(shallow(<Reliability displayOrganizations={false} projects={[]} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx
new file mode 100644
index 00000000000..dcfbc066e39
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx
@@ -0,0 +1,31 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import Risk from '../Risk';
+
+it('renders', () => {
+ const project1 = {
+ key: 'foo',
+ measures: { complexity: '17.2', coverage: '53.5', ncloc: '1734' },
+ name: 'Foo'
+ };
+ expect(shallow(<Risk displayOrganizations={false} projects={[project1]} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Security-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Security-test.tsx
new file mode 100644
index 00000000000..82d608e3e91
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Security-test.tsx
@@ -0,0 +1,26 @@
+/*
+ * 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import Security from '../Security';
+
+it('renders', () => {
+ expect(shallow(<Security displayOrganizations={false} projects={[]} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Security.js b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx
index 80f4d6de997..7eda7c9762f 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/Security.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx
@@ -1,7 +1,7 @@
/*
* SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
+ * 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
@@ -17,20 +17,26 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-// @flow
-import React from 'react';
-import SimpleBubbleChart from './SimpleBubbleChart';
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import SimpleBubbleChart from '../SimpleBubbleChart';
-export default class Security extends React.PureComponent {
- render() {
- return (
+it('renders', () => {
+ const project1 = {
+ key: 'foo',
+ measures: { complexity: '17.2', coverage: '53.5', ncloc: '1734' },
+ name: 'Foo'
+ };
+ expect(
+ shallow(
<SimpleBubbleChart
- {...this.props}
- xMetric={{ key: 'ncloc', type: 'SHORT_INT' }}
- yMetric={{ key: 'security_remediation_effort', type: 'SHORT_WORK_DUR' }}
- sizeMetric={{ key: 'vulnerabilities', type: 'SHORT_INT' }}
colorMetric="security_rating"
+ displayOrganizations={false}
+ projects={[project1]}
+ sizeMetric={{ key: 'ncloc', type: 'INT' }}
+ xMetric={{ key: 'complexity', type: 'INT' }}
+ yMetric={{ key: 'coverage', type: 'PERCENT' }}
/>
- );
- }
-}
+ )
+ ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelectOption-test.js b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Visualizations-test.tsx
index b19fabe98bc..963e0f0c259 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PerspectiveSelectOption-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Visualizations-test.tsx
@@ -1,7 +1,7 @@
/*
* SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
+ * 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
@@ -17,27 +17,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React from 'react';
+import * as React from 'react';
import { shallow } from 'enzyme';
-import PerspectiveSelectOption from '../PerspectiveSelectOption';
+import Visualizations from '../Visualizations';
-it('should render correctly for a view', () => {
+it('renders', () => {
expect(
- shallow(
- <PerspectiveSelectOption option={{ value: 'overall', type: 'view', label: 'Overall' }}>
- Overall
- </PerspectiveSelectOption>
- )
+ shallow(<Visualizations displayOrganizations={false} projects={[]} visualization="coverage" />)
).toMatchSnapshot();
});
-it('should render correctly for a visualization', () => {
+it('renders when limit is reached', () => {
expect(
shallow(
- <PerspectiveSelectOption
- option={{ value: 'coverage', type: 'visualization', label: 'Coverage' }}>
- Coverage
- </PerspectiveSelectOption>
+ <Visualizations
+ displayOrganizations={false}
+ projects={[]}
+ total={1000}
+ visualization="coverage"
+ />
)
).toMatchSnapshot();
});
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Coverage-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Coverage-test.tsx.snap
new file mode 100644
index 00000000000..e1207dff93f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Coverage-test.tsx.snap
@@ -0,0 +1,32 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<SimpleBubbleChart
+ displayOrganizations={false}
+ projects={Array []}
+ sizeMetric={
+ Object {
+ "key": "uncovered_lines",
+ "type": "SHORT_INT",
+ }
+ }
+ xMetric={
+ Object {
+ "key": "complexity",
+ "type": "SHORT_INT",
+ }
+ }
+ yDomain={
+ Array [
+ 100,
+ 0,
+ ]
+ }
+ yMetric={
+ Object {
+ "key": "coverage",
+ "type": "PERCENT",
+ }
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Duplications-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Duplications-test.tsx.snap
new file mode 100644
index 00000000000..dca1cdc0b05
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Duplications-test.tsx.snap
@@ -0,0 +1,26 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<SimpleBubbleChart
+ displayOrganizations={false}
+ projects={Array []}
+ sizeMetric={
+ Object {
+ "key": "duplicated_blocks",
+ "type": "SHORT_INT",
+ }
+ }
+ xMetric={
+ Object {
+ "key": "ncloc",
+ "type": "SHORT_INT",
+ }
+ }
+ yMetric={
+ Object {
+ "key": "duplicated_lines",
+ "type": "SHORT_INT",
+ }
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Maintainability-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Maintainability-test.tsx.snap
new file mode 100644
index 00000000000..aa9eb694998
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Maintainability-test.tsx.snap
@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<SimpleBubbleChart
+ colorMetric="sqale_rating"
+ displayOrganizations={false}
+ projects={Array []}
+ sizeMetric={
+ Object {
+ "key": "code_smells",
+ "type": "SHORT_INT",
+ }
+ }
+ xMetric={
+ Object {
+ "key": "ncloc",
+ "type": "SHORT_INT",
+ }
+ }
+ yMetric={
+ Object {
+ "key": "sqale_index",
+ "type": "SHORT_WORK_DUR",
+ }
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Reliability-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Reliability-test.tsx.snap
new file mode 100644
index 00000000000..74b8820dcd8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Reliability-test.tsx.snap
@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<SimpleBubbleChart
+ colorMetric="reliability_rating"
+ displayOrganizations={false}
+ projects={Array []}
+ sizeMetric={
+ Object {
+ "key": "bugs",
+ "type": "SHORT_INT",
+ }
+ }
+ xMetric={
+ Object {
+ "key": "ncloc",
+ "type": "SHORT_INT",
+ }
+ }
+ yMetric={
+ Object {
+ "key": "reliability_remediation_effort",
+ "type": "SHORT_WORK_DUR",
+ }
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
new file mode 100644
index 00000000000..d6d546c4931
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
@@ -0,0 +1,77 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div>
+ <BubbleChart
+ displayXGrid={true}
+ displayXTicks={true}
+ displayYGrid={true}
+ displayYTicks={true}
+ formatXTick={[Function]}
+ formatYTick={[Function]}
+ height={600}
+ items={
+ Array [
+ Object {
+ "color": undefined,
+ "key": "foo",
+ "link": Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "foo",
+ },
+ },
+ "size": 1734,
+ "tooltip": "<div class=\\"text-left\\"><div class=\\"little-spacer-bottom\\"><strong>Foo</strong></div><div>metric.reliability_rating.name: –</div><div>metric.security_rating.name: –</div><div>metric.coverage.name: 53.5%</div><div>metric.sqale_index.name: –</div><div>metric.ncloc.name: 1.7k</div></div>",
+ "x": 0,
+ "y": 53.5,
+ },
+ ]
+ }
+ padding={
+ Array [
+ 80,
+ 20,
+ 60,
+ 100,
+ ]
+ }
+ sizeRange={
+ Array [
+ 5,
+ 45,
+ ]
+ }
+ yDomain={
+ Array [
+ 100,
+ 0,
+ ]
+ }
+ />
+ <div
+ className="measure-details-bubble-chart-axis x"
+ >
+ metric.sqale_index.name
+ </div>
+ <div
+ className="measure-details-bubble-chart-axis y"
+ >
+ metric.coverage.name
+ </div>
+ <div
+ className="measure-details-bubble-chart-axis size"
+ >
+ <span
+ className="spacer-right"
+ >
+ component_measures.legend.color_x.projects.worse_of_reliablity_and_security
+ </span>
+ component_measures.legend.size_x.metric.ncloc.name
+ <ColorRatingsLegend
+ className="big-spacer-top"
+ />
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Security-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Security-test.tsx.snap
new file mode 100644
index 00000000000..1ff6a7d08a7
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Security-test.tsx.snap
@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<SimpleBubbleChart
+ colorMetric="security_rating"
+ displayOrganizations={false}
+ projects={Array []}
+ sizeMetric={
+ Object {
+ "key": "vulnerabilities",
+ "type": "SHORT_INT",
+ }
+ }
+ xMetric={
+ Object {
+ "key": "ncloc",
+ "type": "SHORT_INT",
+ }
+ }
+ yMetric={
+ Object {
+ "key": "security_remediation_effort",
+ "type": "SHORT_WORK_DUR",
+ }
+ }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
new file mode 100644
index 00000000000..afb4e501021
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
@@ -0,0 +1,71 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div>
+ <BubbleChart
+ displayXGrid={true}
+ displayXTicks={true}
+ displayYGrid={true}
+ displayYTicks={true}
+ formatXTick={[Function]}
+ formatYTick={[Function]}
+ height={600}
+ items={
+ Array [
+ Object {
+ "color": undefined,
+ "key": "foo",
+ "link": Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "foo",
+ },
+ },
+ "size": 1734,
+ "tooltip": "<div class=\\"text-left\\"><div class=\\"little-spacer-bottom\\"><strong>Foo</strong></div><div>metric.complexity.name: 17</div><div>metric.coverage.name: 53.5%</div><div>metric.ncloc.name: 1,734</div></div>",
+ "x": 17.2,
+ "y": 53.5,
+ },
+ ]
+ }
+ padding={
+ Array [
+ 80,
+ 20,
+ 60,
+ 100,
+ ]
+ }
+ sizeRange={
+ Array [
+ 5,
+ 45,
+ ]
+ }
+ />
+ <div
+ className="measure-details-bubble-chart-axis x"
+ >
+ metric.complexity.name
+ </div>
+ <div
+ className="measure-details-bubble-chart-axis y"
+ >
+ metric.coverage.name
+ </div>
+ <div
+ className="measure-details-bubble-chart-axis size"
+ >
+ <span
+ className="spacer-right"
+ >
+ component_measures.legend.color_x.metric.security_rating.name
+ </span>
+ component_measures.legend.size_x.metric.ncloc.name
+ <ColorRatingsLegend
+ className="big-spacer-top"
+ />
+ </div>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Visualizations-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Visualizations-test.tsx.snap
new file mode 100644
index 00000000000..6a28f9d5baa
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Visualizations-test.tsx.snap
@@ -0,0 +1,50 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="boxed-group projects-visualizations"
+>
+ <div
+ className="projects-visualization"
+ >
+ <Coverage
+ displayOrganizations={false}
+ projects={Array []}
+ />
+ </div>
+ <footer
+ className="projects-visualizations-footer"
+ >
+ <p>
+ projects.visualization.coverage.description
+ </p>
+ </footer>
+</div>
+`;
+
+exports[`renders when limit is reached 1`] = `
+<div
+ className="boxed-group projects-visualizations"
+>
+ <div
+ className="projects-visualization"
+ >
+ <Coverage
+ displayOrganizations={false}
+ projects={Array []}
+ />
+ </div>
+ <footer
+ className="projects-visualizations-footer"
+ >
+ <p>
+ projects.visualization.coverage.description
+ </p>
+ <p
+ className="note spacer-top"
+ >
+ projects.limited_set_of_projects.0.projects.sort.name
+ </p>
+ </footer>
+</div>
+`;