FN-5648: show in-progress feedback while merging PRs
Fusion-Task-Id: FN-5648 Fusion-Task-Lineage: f056a665-4d52-4472-8495-487a1865aea4
This commit is contained in:
@@ -173,6 +173,15 @@
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.pr-hint--info {
|
||||
background: color-mix(in srgb, var(--color-info) 12%, transparent);
|
||||
border: var(--btn-border-width) solid color-mix(in srgb, var(--color-info) 35%, transparent);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--color-info);
|
||||
font-size: 0.8125rem;
|
||||
padding: var(--space-md);
|
||||
}
|
||||
|
||||
.pr-hint--warning {
|
||||
background: color-mix(in srgb, var(--color-warning) 12%, transparent);
|
||||
border: var(--btn-border-width) solid color-mix(in srgb, var(--color-warning) 35%, transparent);
|
||||
|
||||
@@ -269,7 +269,7 @@ function PrCard({
|
||||
</div>
|
||||
|
||||
{showMergeControls ? (
|
||||
<div className="pr-panel-section"><div className="pr-panel-row-label">Merge</div><div className="pr-merge-controls"><select className="select" value={mergeStrategy} onChange={(event) => setMergeStrategy(event.target.value as "merge" | "squash" | "rebase")}><option value="merge">merge</option><option value="squash">squash</option><option value="rebase">rebase</option></select><button className="btn btn-primary btn-sm" onClick={handleMerge} disabled={!mergeReady || isMerging} title={mergeReady ? "Merge pull request" : blockingReasonsTitle || "Refresh PR status to check merge readiness"}>Merge pull request</button><label className="checkbox-label"><input type="checkbox" checked={Boolean(prInfo.autoMergeOnGreen)} onChange={(event) => { void handleAutoMergeToggle(event.currentTarget.checked); }} />Auto-merge when green</label></div>{prInfo.lastMergeError ? <div className="pr-merge-error"><span>{prInfo.lastMergeError}</span><button className="btn btn-sm" onClick={handleMerge} disabled={isMerging}>Retry</button></div> : null}</div>
|
||||
<div className="pr-panel-section"><div className="pr-panel-row-label">Merge</div><div className="pr-merge-controls"><select className="select" value={mergeStrategy} disabled={isMerging} onChange={(event) => setMergeStrategy(event.target.value as "merge" | "squash" | "rebase")}><option value="merge">merge</option><option value="squash">squash</option><option value="rebase">rebase</option></select><button className="btn btn-primary btn-sm" onClick={handleMerge} disabled={!mergeReady || isMerging} aria-busy={isMerging} data-testid="pr-merge-button" title={mergeReady ? "Merge pull request" : blockingReasonsTitle || "Refresh PR status to check merge readiness"}>{isMerging ? <><RefreshCw size={14} className="spin" />Merging…</> : "Merge pull request"}</button><label className="checkbox-label"><input type="checkbox" checked={Boolean(prInfo.autoMergeOnGreen)} disabled={isMerging} onChange={(event) => { void handleAutoMergeToggle(event.currentTarget.checked); }} />Auto-merge when green</label></div>{isMerging ? <div className="pr-hint pr-hint--info" role="status" aria-live="polite">Merging pull request…</div> : null}{prInfo.lastMergeError ? <div className="pr-merge-error"><span>{prInfo.lastMergeError}</span><button className="btn btn-sm" onClick={handleMerge} disabled={isMerging}>Retry</button></div> : null}</div>
|
||||
) : null}
|
||||
|
||||
{showConflictHint ? <div className="pr-hint pr-hint--conflict">Merge conflict detected. Resolve/rebase branch and retry reclaim.<button className="btn btn-sm" onClick={async () => { setIsReclaimingConflict(true); try { const result = await reclaimPrConflict(taskId, projectId); if (result.queued) { addToast("Conflict reclaim queued", "success"); const updated = await refreshPrStatus(taskId, projectId); setRefreshState(updated); const targetPr = (updated.all?.map((entry) => entry.prInfo) ?? [updated.prInfo]).find((entry) => entry.number === prInfo.number); if (targetPr) onPrUpdated(targetPr); } else { addToast(result.reason ?? "Conflict reclaim unavailable", "warning"); } } catch (err) { addToast(getErrorMessage(err) || "Failed to queue conflict reclaim", "error"); } finally { setIsReclaimingConflict(false); } }} disabled={isReclaimingConflict}>Retry conflict reclaim</button></div> : null}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { PrInfo } from "../../api";
|
||||
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
|
||||
import { PrPanel } from "../PrPanel";
|
||||
import { mergePr, refreshPrStatus } from "../../api";
|
||||
@@ -147,6 +148,50 @@ describe("PrPanel merge controls", () => {
|
||||
expect(screen.getByRole("button", { name: "Merge pull request" })).toBeDisabled();
|
||||
});
|
||||
|
||||
it("shows in-progress merge feedback until merge resolves", async () => {
|
||||
let resolveMerge: ((value: { prInfo: PrInfo }) => void) | undefined;
|
||||
(mergePr as ReturnType<typeof vi.fn>).mockImplementation(() => new Promise((resolve) => {
|
||||
resolveMerge = resolve;
|
||||
}));
|
||||
const addToast = vi.fn();
|
||||
|
||||
render(
|
||||
<PrPanel
|
||||
taskId="FN-1"
|
||||
prAuthAvailable
|
||||
onPrUpdated={() => {}}
|
||||
addToast={addToast}
|
||||
prInfo={{ url: "https://github.com/o/r/pull/1", number: 1, status: "open", title: "t", headBranch: "h", baseBranch: "main", commentCount: 0, mergeable: "clean" }}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByTestId("pr-merge-button"));
|
||||
|
||||
const mergeButton = screen.getByRole("button", { name: /merging/i });
|
||||
expect(mergeButton).toBeDisabled();
|
||||
expect(mergeButton).toHaveAttribute("aria-busy", "true");
|
||||
expect(screen.getByRole("status")).toHaveTextContent("Merging pull request…");
|
||||
expect(screen.getByRole("combobox")).toBeDisabled();
|
||||
|
||||
resolveMerge?.({
|
||||
prInfo: {
|
||||
url: "https://github.com/o/r/pull/1",
|
||||
number: 1,
|
||||
status: "merged",
|
||||
title: "t",
|
||||
headBranch: "h",
|
||||
baseBranch: "main",
|
||||
commentCount: 0,
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole("button", { name: "Merge pull request" })).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.queryByText("Merging pull request…")).not.toBeInTheDocument();
|
||||
expect(addToast).toHaveBeenCalledWith("Pull request merged", "success");
|
||||
});
|
||||
|
||||
it("shows error block and retry", () => {
|
||||
render(<PrPanel taskId="FN-1" prAuthAvailable onPrUpdated={() => {}} addToast={() => {}} prInfo={{ url: "https://github.com/o/r/pull/1", number: 1, status: "open", title: "t", headBranch: "h", baseBranch: "main", commentCount: 0, lastMergeError: "boom" }} />);
|
||||
expect(screen.getByText("boom")).toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user