Browse Source

fix(frontend): close panel update confirmation before progress (#6766)

* fix(frontend): close panel update confirmation promptly

* chore(ci): rerun flaky checks
libmur-dev 17 hours ago
parent
commit
dbfa369632

+ 35 - 31
frontend/src/pages/index/PanelUpdateModal.tsx

@@ -80,6 +80,39 @@ export default function PanelUpdateModal({
     }
     }
   }
   }
 
 
+  async function runPanelUpdate() {
+    const baseTip = t('pages.index.dontRefresh');
+    const tip = info.latestVersion ? `${baseTip} (${info.latestVersion})` : baseTip;
+    onClose();
+    onBusy({ busy: true, tip });
+    const result = await HttpUtil.post<{ runId: string }>('/panel/api/server/updatePanel');
+    if (!result?.success) {
+      onBusy({ busy: false });
+      return;
+    }
+    const outcome = await pollUpdateStatus(result.obj?.runId ?? '');
+    onBusy({ busy: false });
+    if (outcome === 'success') {
+      await PromiseUtil.sleep(800);
+      window.location.reload();
+      return;
+    }
+    modal[outcome === 'failed' ? 'error' : 'warning']({
+      title: t(
+        outcome === 'failed'
+          ? 'pages.index.panelUpdateFailedTitle'
+          : 'pages.index.panelUpdateUnknownTitle',
+      ),
+      content: t(
+        outcome === 'failed'
+          ? 'pages.index.panelUpdateFailedDesc'
+          : 'pages.index.panelUpdateUnknownDesc',
+      ),
+      okText: t('refresh'),
+      onOk: () => window.location.reload(),
+    });
+  }
+
   function updatePanel() {
   function updatePanel() {
     modal.confirm({
     modal.confirm({
       title: t('pages.index.panelUpdateDialog'),
       title: t('pages.index.panelUpdateDialog'),
@@ -89,37 +122,8 @@ export default function PanelUpdateModal({
       ),
       ),
       okText: t('confirm'),
       okText: t('confirm'),
       cancelText: t('cancel'),
       cancelText: t('cancel'),
-      onOk: async () => {
-        const baseTip = t('pages.index.dontRefresh');
-        const tip = info.latestVersion ? `${baseTip} (${info.latestVersion})` : baseTip;
-        onClose();
-        onBusy({ busy: true, tip });
-        const result = await HttpUtil.post<{ runId: string }>('/panel/api/server/updatePanel');
-        if (!result?.success) {
-          onBusy({ busy: false });
-          return;
-        }
-        const outcome = await pollUpdateStatus(result.obj?.runId ?? '');
-        onBusy({ busy: false });
-        if (outcome === 'success') {
-          await PromiseUtil.sleep(800);
-          window.location.reload();
-          return;
-        }
-        modal[outcome === 'failed' ? 'error' : 'warning']({
-          title: t(
-            outcome === 'failed'
-              ? 'pages.index.panelUpdateFailedTitle'
-              : 'pages.index.panelUpdateUnknownTitle',
-          ),
-          content: t(
-            outcome === 'failed'
-              ? 'pages.index.panelUpdateFailedDesc'
-              : 'pages.index.panelUpdateUnknownDesc',
-          ),
-          okText: t('refresh'),
-          onOk: () => window.location.reload(),
-        });
+      onOk: () => {
+        void runPanelUpdate();
       },
       },
     });
     });
   }
   }

+ 62 - 0
frontend/src/test/panel-update-modal.test.tsx

@@ -0,0 +1,62 @@
+import { fireEvent, screen } from '@testing-library/react';
+import { Modal } from 'antd';
+import { afterEach, describe, expect, it, vi } from 'vitest';
+
+import PanelUpdateModal from '@/pages/index/PanelUpdateModal';
+import { HttpUtil } from '@/utils';
+import { renderWithProviders } from './test-utils';
+
+let restoreUseModal = () => {};
+
+describe('panel update modal', () => {
+  afterEach(() => {
+    restoreUseModal();
+    restoreUseModal = () => {};
+    vi.mocked(HttpUtil.post).mockReset();
+  });
+
+  it('lets the confirmation close before the update request finishes', () => {
+    const confirm = vi.fn();
+    const useModal = vi.spyOn(Modal, 'useModal').mockReturnValue([
+      {
+        confirm,
+        error: vi.fn(),
+        warning: vi.fn(),
+      } as unknown as ReturnType<typeof Modal.useModal>[0],
+      <span key="modal-context" />,
+    ]);
+    restoreUseModal = () => useModal.mockRestore();
+    vi.mocked(HttpUtil.post).mockReturnValue(new Promise(() => {}));
+    const onClose = vi.fn();
+    const onBusy = vi.fn();
+    renderWithProviders(
+      <PanelUpdateModal
+        open
+        info={{
+          channel: 'dev',
+          currentVersion: 'dev+old',
+          latestVersion: 'dev+new',
+          currentCommit: 'old',
+          latestCommit: 'new',
+          updateAvailable: true,
+        }}
+        devChannelEnable
+        onClose={onClose}
+        onBusy={onBusy}
+      />,
+    );
+
+    fireEvent.click(screen.getByRole('button', { name: /Update Panel/ }));
+    expect(confirm).toHaveBeenCalledOnce();
+    const config = confirm.mock.calls[0]?.[0];
+    if (!config?.onOk) throw new Error('Confirmation onOk handler was not registered');
+
+    expect(config.onOk()).toBeUndefined();
+    expect(onClose).toHaveBeenCalledOnce();
+    expect(HttpUtil.post).toHaveBeenCalledWith('/panel/api/server/updatePanel');
+    expect(onBusy).toHaveBeenCalledWith({
+      busy: true,
+      tip: 'Installation is in progress, please do not refresh this page (dev+new)',
+    });
+  });
+});