]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-14312 Improve hotspot update handling
authorJeremy Davis <jeremy.davis@sonarsource.com>
Thu, 7 Jan 2021 17:10:00 +0000 (18:10 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 11 Jan 2021 20:20:38 +0000 (20:20 +0000)
server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/security-hotspots/components/status/Status.tsx
server/sonar-web/src/main/js/apps/security-hotspots/components/status/StatusSelection.tsx

index c4d7f6d76f8f95bb14b774bee05c9b6167910399..f91c3d44f91f10550c3948fc257e3d8e23708a1b 100644 (file)
@@ -337,12 +337,12 @@ export class SecurityHotspotsApp extends React.PureComponent<Props, State> {
 
         const nextHotspot = allHotspots[Math.min(index, allHotspots.length - 1)];
 
-        this.setState({
+        this.setState(({ selectedHotspot }) => ({
           hotspots: allHotspots,
           hotspotsPageIndex: paging.pageIndex,
           hotspotsTotal: paging.total,
-          selectedHotspot: nextHotspot
-        });
+          selectedHotspot: selectedHotspot?.key === hotspotKey ? nextHotspot : selectedHotspot
+        }));
       })
       .then(this.fetchSecurityHotspotsReviewed);
   };
index c3778956658b710465edfe97d7ba2acc79f7304a..ab47a648f7db80a06dbedba27b930bc5ab1087e5 100644 (file)
@@ -70,7 +70,7 @@ export default function HotspotSnippetContainerRenderer(
           onExpand={noop}
           sourceViewerFile={sourceViewerFile}
         />
-        <DeferredSpinner loading={loading}>
+        <DeferredSpinner className="big-spacer" loading={loading}>
           {sourceLines.length > 0 && (
             <SourceViewerContext.Provider /* Used by LineOptionsPopup */
               value={{ branchLike, file: sourceViewerFile }}>
index 1bebc190874638420786125fa6d6f997708adca0..839db3bdcc5f10d88d40047b79ebf53fa8b62576 100644 (file)
@@ -79,12 +79,10 @@ export default class HotspotViewer extends React.PureComponent<Props, State> {
       .catch(() => this.mounted && this.setState({ loading: false }));
   };
 
-  handleHotspotUpdate = () => {
-    return this.fetchHotspot().then((hotspot?: Hotspot) => {
-      if (hotspot) {
-        return this.props.onUpdateHotspot(hotspot.key);
-      }
-    });
+  handleHotspotUpdate = async () => {
+    const { hotspotKey } = this.props;
+
+    await this.props.onUpdateHotspot(hotspotKey);
   };
 
   handleOpenComment = () => {
index 5d685f17df5e799c198950a89dcab56a8127499f..4947981b877c6a1324b952224fecd11b2363b410 100644 (file)
@@ -74,7 +74,7 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) {
   );
 
   return (
-    <DeferredSpinner loading={loading}>
+    <DeferredSpinner className="big-spacer-left big-spacer-top" loading={loading}>
       {hotspot && (
         <div className="big-padded hotspot-content">
           <div className="huge-spacer-bottom display-flex-space-between">
index 6d5601a4d01956aeb98250cf0841973ae8430fd5..022a4764aef9d0f48e56671387c5e6ffe768067d 100644 (file)
@@ -51,12 +51,11 @@ it('should render correctly', async () => {
   expect(getSecurityHotspotDetails).toHaveBeenCalledWith(newHotspotKey);
 });
 
-it('should update refresh hotspot on update', () => {
-  const wrapper = shallowRender();
-  const mockGetHostpot = getSecurityHotspotDetails as jest.Mock;
-  mockGetHostpot.mockClear();
+it('should refresh hotspot on update', () => {
+  const onUpdateHotspot = jest.fn();
+  const wrapper = shallowRender({ onUpdateHotspot });
   wrapper.find(HotspotViewerRenderer).simulate('updateHotspot');
-  expect(mockGetHostpot).toHaveBeenCalledTimes(1);
+  expect(onUpdateHotspot).toHaveBeenCalled();
 });
 
 it('should open comment form when scroll to comment', () => {
index a5d92a6d7588e166b11adf25b53071700529476f..6ddb9d1ee5ff0e7d582f1d7d898a58e5a0547cf4 100644 (file)
@@ -42,6 +42,7 @@ exports[`should render correctly 1`] = `
       }
     />
     <DeferredSpinner
+      className="big-spacer"
       loading={false}
     />
   </div>
@@ -85,6 +86,7 @@ exports[`should render correctly: with sourcelines 1`] = `
       }
     />
     <DeferredSpinner
+      className="big-spacer"
       loading={false}
     >
       <ContextProvider
index 311e7f29f8f28254957eb51676cefabe3f6a0db8..bc39771668fc7c4aa6c4f842f1cf2b16e05eec81 100644 (file)
@@ -2,6 +2,7 @@
 
 exports[`should render correctly 1`] = `
 <DeferredSpinner
+  className="big-spacer-left big-spacer-top"
   loading={false}
 >
   <div
@@ -646,6 +647,7 @@ exports[`should render correctly 1`] = `
 
 exports[`should render correctly: anonymous user 1`] = `
 <DeferredSpinner
+  className="big-spacer-left big-spacer-top"
   loading={false}
 >
   <div
@@ -1290,6 +1292,7 @@ exports[`should render correctly: anonymous user 1`] = `
 
 exports[`should render correctly: assignee without name 1`] = `
 <DeferredSpinner
+  className="big-spacer-left big-spacer-top"
   loading={false}
 >
   <div
@@ -1934,6 +1937,7 @@ exports[`should render correctly: assignee without name 1`] = `
 
 exports[`should render correctly: deleted assignee 1`] = `
 <DeferredSpinner
+  className="big-spacer-left big-spacer-top"
   loading={false}
 >
   <div
@@ -2578,12 +2582,14 @@ exports[`should render correctly: deleted assignee 1`] = `
 
 exports[`should render correctly: no hotspot 1`] = `
 <DeferredSpinner
+  className="big-spacer-left big-spacer-top"
   loading={false}
 />
 `;
 
 exports[`should render correctly: unassigned 1`] = `
 <DeferredSpinner
+  className="big-spacer-left big-spacer-top"
   loading={false}
 >
   <div
index 214c7043fe40c2b56af0eadc478e6cd2f331637a..4a595fd4735927700f32cd9a2c0fad3c30c50e1e 100644 (file)
@@ -37,7 +37,7 @@ export interface StatusProps {
   currentUser: T.CurrentUser;
   hotspot: Hotspot;
 
-  onStatusChange: () => void;
+  onStatusChange: () => Promise<void>;
 }
 
 export function Status(props: StatusProps) {
@@ -79,9 +79,9 @@ export function Status(props: StatusProps) {
         <DropdownOverlay noPadding={true} placement={PopupPlacement.Bottom}>
           <StatusSelection
             hotspot={hotspot}
-            onStatusOptionChange={() => {
+            onStatusOptionChange={async () => {
+              await props.onStatusChange();
               setIsOpen(false);
-              props.onStatusChange();
             }}
           />
         </DropdownOverlay>
index 1a6c683aa5a761e66fd946a3d5ea4a63f23b4f2b..b029f33193e8ce89ec8eea79d3e8675179c2ee25 100644 (file)
@@ -30,7 +30,7 @@ import StatusSelectionRenderer from './StatusSelectionRenderer';
 
 interface Props {
   hotspot: Hotspot;
-  onStatusOptionChange: (statusOption: HotspotStatusOption) => void;
+  onStatusOptionChange: (statusOption: HotspotStatusOption) => Promise<void>;
 }
 
 interface State {
@@ -84,9 +84,9 @@ export default class StatusSelection extends React.PureComponent<Props, State> {
         ...getStatusAndResolutionFromStatusOption(selectedStatus),
         comment: comment || undefined
       })
-        .then(() => {
+        .then(async () => {
+          await this.props.onStatusOptionChange(selectedStatus);
           this.setState({ loading: false });
-          this.props.onStatusOptionChange(selectedStatus);
         })
         .then(() =>
           addGlobalSuccessMessage(