aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorphilippe-perrin-sonarsource <philippe.perrin@sonarsource.com>2019-09-26 10:14:57 +0200
committerSonarTech <sonartech@sonarsource.com>2019-09-30 20:21:06 +0200
commit5cb50b88707b28319bd457a2602b6bfc7445f8d9 (patch)
tree0531a1dc8247b4a4d7bc0df4e61277067af3c7be /server/sonar-web
parent882c11382e0314743269de2fb1b1fde68fed8a83 (diff)
downloadsonarqube-5cb50b88707b28319bd457a2602b6bfc7445f8d9.tar.gz
sonarqube-5cb50b88707b28319bd457a2602b6bfc7445f8d9.zip
SONAR-12413 Issue changelog contains confusing 'undefined' values
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/apps/custom-measures/components/Item.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/BulkChangeModal-test.tsx37
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap20
-rw-r--r--server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx19
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx9
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.tsx6
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap36
-rw-r--r--server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.tsx52
9 files changed, 151 insertions, 47 deletions
diff --git a/server/sonar-web/src/main/js/apps/custom-measures/components/Item.tsx b/server/sonar-web/src/main/js/apps/custom-measures/components/Item.tsx
index fd5379b7ded..40ef23bfec9 100644
--- a/server/sonar-web/src/main/js/apps/custom-measures/components/Item.tsx
+++ b/server/sonar-web/src/main/js/apps/custom-measures/components/Item.tsx
@@ -86,6 +86,7 @@ export default class Item extends React.PureComponent<Props, State> {
render() {
const { measure } = this.props;
+ const userName = measure.user.name || measure.user.login;
return (
<tr data-metric={measure.metric.key}>
@@ -117,8 +118,8 @@ export default class Item extends React.PureComponent<Props, State> {
<MeasureDate measure={measure} /> {translate('by_')}{' '}
<span className="js-custom-measure-user">
{isUserActive(measure.user)
- ? measure.user.name || measure.user.login
- : translateWithParameters('user.x_deleted', measure.user.login)}
+ ? userName
+ : translateWithParameters('user.x_deleted', userName)}
</span>
</td>
diff --git a/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx b/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
index 6ed30634422..7e16ba09c35 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/BulkChangeModal.tsx
@@ -161,11 +161,15 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
handleAssigneeSearch = (query: string) => {
return searchAssignees(query, this.state.organization).then(({ results }) =>
- results.map(r => ({
- avatar: r.avatar,
- label: isUserActive(r) ? r.name : translateWithParameters('user.x_deleted', r.login),
- value: r.login
- }))
+ results.map(r => {
+ const userInfo = r.name || r.login;
+
+ return {
+ avatar: r.avatar,
+ label: isUserActive(r) ? userInfo : translateWithParameters('user.x_deleted', userInfo),
+ value: r.login
+ };
+ })
);
};
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/BulkChangeModal-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/BulkChangeModal-test.tsx
index 3803de3e021..60f6e8472d5 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/BulkChangeModal-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/BulkChangeModal-test.tsx
@@ -33,6 +33,30 @@ jest.mock('../BulkChangeModal', () => {
return mock;
});
+jest.mock('../../utils', () => ({
+ searchAssignees: jest.fn().mockResolvedValue({
+ results: [
+ {
+ active: true,
+ avatar: '##toto',
+ login: 'toto@toto',
+ name: 'toto'
+ },
+ {
+ active: false,
+ avatar: '##toto',
+ login: 'login@login',
+ name: 'toto'
+ },
+ {
+ active: true,
+ avatar: '##toto',
+ login: 'login@login'
+ }
+ ]
+ })
+}));
+
it('should display error message when no issues available', async () => {
const wrapper = getWrapper([]);
await waitAndUpdate(wrapper);
@@ -57,8 +81,19 @@ it('should display warning when too many issues are passed', async () => {
expect(wrapper.find('Alert')).toMatchSnapshot();
});
+it('should properly handle the search for assignee', async () => {
+ const issues: T.Issue[] = [];
+ for (let i = MAX_PAGE_SIZE + 1; i > 0; i--) {
+ issues.push(mockIssue());
+ }
+
+ const wrapper = getWrapper(issues);
+ const result = await wrapper.instance().handleAssigneeSearch('toto');
+ expect(result).toMatchSnapshot();
+});
+
const getWrapper = (issues: T.Issue[]) => {
- return shallow(
+ return shallow<BulkChangeModal>(
<BulkChangeModal
component={undefined}
currentUser={{ isLoggedIn: true }}
diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap
index 9b093ed6143..e8fb769f862 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/BulkChangeModal-test.tsx.snap
@@ -122,3 +122,23 @@ exports[`should display warning when too many issues are passed 2`] = `
/>
</Alert>
`;
+
+exports[`should properly handle the search for assignee 1`] = `
+Array [
+ Object {
+ "avatar": "##toto",
+ "label": "toto",
+ "value": "toto@toto",
+ },
+ Object {
+ "avatar": "##toto",
+ "label": "user.x_deleted.toto",
+ "value": "login@login",
+ },
+ Object {
+ "avatar": "##toto",
+ "label": "user.x_deleted.login@login",
+ "value": "login@login",
+ },
+]
+`;
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx
index 76c93284a5f..3e7f242654e 100644
--- a/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/sidebar/AssigneeFacet.tsx
@@ -104,18 +104,17 @@ export default class AssigneeFacet extends React.PureComponent<Props> {
const user = this.props.referencedUsers[assignee];
- return user ? (
+ if (!user) {
+ return assignee;
+ }
+
+ const userName = user.name || user.login;
+
+ return (
<>
- <Avatar
- className="little-spacer-right"
- hash={user.avatar}
- name={user.name || user.login}
- size={16}
- />
- {isUserActive(user) ? user.name : translateWithParameters('user.x_deleted', user.login)}
+ <Avatar className="little-spacer-right" hash={user.avatar} name={userName} size={16} />
+ {isUserActive(user) ? userName : translateWithParameters('user.x_deleted', userName)}
</>
- ) : (
- assignee
);
};
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx
index a021c4bd7bc..e8f133b7484 100644
--- a/server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx
+++ b/server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx
@@ -47,10 +47,9 @@ export default class IssueAssign extends React.PureComponent<Props> {
renderAssignee() {
const { issue } = this.props;
- const assignee =
- issue.assigneeActive !== false ? issue.assigneeName || issue.assignee : issue.assignee;
+ const assigneeName = issue.assigneeName || issue.assignee;
- if (assignee) {
+ if (assigneeName) {
return (
<>
<span className="text-top">
@@ -63,8 +62,8 @@ export default class IssueAssign extends React.PureComponent<Props> {
</span>
<span className="issue-meta-label">
{issue.assigneeActive === false
- ? translateWithParameters('user.x_deleted', assignee)
- : assignee}
+ ? translateWithParameters('user.x_deleted', assigneeName)
+ : assigneeName}
</span>
</>
);
diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.tsx b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.tsx
index 1f59775d2fd..d6a42b5218f 100644
--- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.tsx
+++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueAssign-test.tsx
@@ -37,6 +37,10 @@ it('should render with the action', () => {
expect(shallowRender()).toMatchSnapshot();
});
+it('should render a fallback assignee display if assignee info are not available', () => {
+ expect(shallowRender({ issue: { projectOrganization: 'org' } })).toMatchSnapshot();
+});
+
it('should open the popup when the button is clicked', () => {
const togglePopup = jest.fn();
const element = shallowRender({ togglePopup });
@@ -47,7 +51,7 @@ it('should open the popup when the button is clicked', () => {
});
function shallowRender(props: Partial<IssueAssign['props']> = {}) {
- return shallow(
+ return shallow<IssueAssign>(
<IssueAssign
canAssign={true}
isOpen={false}
diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap
index 2e2ccd87499..324452de6ab 100644
--- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap
@@ -58,6 +58,42 @@ exports[`should open the popup when the button is clicked 2`] = `
</div>
`;
+exports[`should render a fallback assignee display if assignee info are not available 1`] = `
+<div
+ className="dropdown"
+>
+ <Toggler
+ closeOnEscape={true}
+ onRequestClose={[Function]}
+ open={false}
+ overlay={
+ <Connect(withCurrentUser(SetAssigneePopup))
+ issue={
+ Object {
+ "projectOrganization": "org",
+ }
+ }
+ onSelect={[MockFunction]}
+ />
+ }
+ >
+ <ButtonLink
+ className="issue-action issue-action-with-options js-issue-assign"
+ onClick={[Function]}
+ >
+ <span
+ className="issue-meta-label"
+ >
+ unassigned
+ </span>
+ <DropdownIcon
+ className="little-spacer-left"
+ />
+ </ButtonLink>
+ </Toggler>
+</div>
+`;
+
exports[`should render with the action 1`] = `
<div
className="dropdown"
diff --git a/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.tsx b/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.tsx
index aacb92f283f..9557ac0e661 100644
--- a/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.tsx
+++ b/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.tsx
@@ -77,29 +77,35 @@ export default class ChangelogPopup extends React.PureComponent<Props, State> {
</td>
</tr>
- {this.state.changelog.map((item, idx) => (
- <tr key={idx}>
- <td className="thin text-left text-top nowrap">
- <DateTimeFormatter date={item.creationDate} />
- </td>
- <td className="text-left text-top">
- <p>
- <Avatar
- className="little-spacer-right"
- hash={item.avatar}
- name={(item.isUserActive && item.userName) || item.user}
- size={16}
- />
- {item.isUserActive
- ? item.userName || item.user
- : translateWithParameters('user.x_deleted', item.user)}
- </p>
- {item.diffs.map(diff => (
- <IssueChangelogDiff diff={diff} key={diff.key} />
- ))}
- </td>
- </tr>
- ))}
+ {this.state.changelog.map((item, idx) => {
+ const userName = item.userName || item.user;
+
+ return (
+ <tr key={idx}>
+ <td className="thin text-left text-top nowrap">
+ <DateTimeFormatter date={item.creationDate} />
+ </td>
+ <td className="text-left text-top">
+ {userName && (
+ <p>
+ <Avatar
+ className="little-spacer-right"
+ hash={item.avatar}
+ name={userName}
+ size={16}
+ />
+ {item.isUserActive
+ ? userName
+ : translateWithParameters('user.x_deleted', userName)}
+ </p>
+ )}
+ {item.diffs.map(diff => (
+ <IssueChangelogDiff diff={diff} key={diff.key} />
+ ))}
+ </td>
+ </tr>
+ );
+ })}
</tbody>
</table>
</div>