feat(FN-4238): export merger errors from engine and polish workflow results

Exports merger error types from the engine package for use in the dashboard and CLI. Adds tests for the engine barrel exports and fixes workflow results tab styling to align the edit toggle with button styles.

Fusion-Task-Id: FN-4238
This commit is contained in:
Fusion
2026-05-12 20:35:00 -07:00
committed by gsxdsm
parent 8e9e48881b
commit 7e057a65d2
6 changed files with 99 additions and 10 deletions

View File

@@ -0,0 +1,5 @@
---
"@runfusion/fusion": minor
---
Export `DiffVolumeRegressionError`, `MergeAbortedError`, and `SquashAuditError` from `@fusion/engine` so consumers can use `instanceof` checks without deep-importing.

View File

@@ -54,19 +54,13 @@
.workflow-results-edit-toggle { .workflow-results-edit-toggle {
width: auto; width: auto;
height: calc(var(--space-lg) * 2 - var(--space-xs)); min-height: calc(var(--space-lg) * 2 - var(--space-xs));
padding: 0 calc(var(--space-sm) + var(--space-xs) * 0.5); padding-inline: calc(var(--space-sm) + var(--space-xs) * 0.5);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
background: var(--surface);
color: var(--text-muted); color: var(--text-muted);
font-size: calc(var(--space-sm) + var(--space-xs));
font-weight: 500;
gap: calc(var(--space-xs) + var(--space-xs) * 0.5); gap: calc(var(--space-xs) + var(--space-xs) * 0.5);
} }
.workflow-results-edit-toggle:hover { .workflow-results-edit-toggle:hover {
background: var(--card-hover);
color: var(--text); color: var(--text);
} }
@@ -229,7 +223,7 @@
/* Phase badge base and modifier classes */ /* Phase badge base and modifier classes */
.phase-badge { .phase-badge {
margin-inline-start: var(--space-xs); margin-inline-start: var(--space-xs);
font-size: 11px; font-size: calc(var(--space-sm) + var(--space-xs) * 0.75);
padding: var(--space-xs) var(--space-sm); padding: var(--space-xs) var(--space-sm);
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
} }
@@ -293,6 +287,7 @@
.workflow-result-output-header { .workflow-result-output-header {
display: flex; display: flex;
flex-wrap: wrap;
align-items: center; align-items: center;
gap: var(--space-sm); gap: var(--space-sm);
margin-bottom: var(--space-xs); margin-bottom: var(--space-xs);
@@ -307,6 +302,8 @@
} }
.workflow-result-output-preview { .workflow-result-output-preview {
flex: 1 1 calc(var(--space-2xl) * 6);
min-width: 0;
font-size: calc(var(--space-sm) + var(--space-xs) * 0.75); font-size: calc(var(--space-sm) + var(--space-xs) * 0.75);
color: var(--text-dim); color: var(--text-dim);
font-style: italic; font-style: italic;
@@ -504,6 +501,19 @@
min-height: calc(var(--space-lg) * 2 + var(--space-xs)); min-height: calc(var(--space-lg) * 2 + var(--space-xs));
} }
.workflow-result-output-header {
align-items: flex-start;
}
.workflow-result-output-preview {
flex-basis: 100%;
order: 3;
}
.workflow-result-mode-toggle {
margin-left: 0;
}
.workflow-configured-header { .workflow-configured-header {
align-items: flex-start; align-items: flex-start;
} }

View File

@@ -570,7 +570,7 @@ export function WorkflowResultsTab({
const editButton = canEdit ? ( const editButton = canEdit ? (
<button <button
type="button" type="button"
className="modal-edit-btn workflow-results-edit-toggle" className="btn btn-sm workflow-results-edit-toggle"
onClick={() => setIsEditing((prev) => !prev)} onClick={() => setIsEditing((prev) => !prev)}
data-testid="workflow-steps-edit-toggle" data-testid="workflow-steps-edit-toggle"
aria-label={isEditing ? "Done editing workflow steps" : "Edit workflow steps"} aria-label={isEditing ? "Done editing workflow steps" : "Edit workflow steps"}

View File

@@ -873,6 +873,17 @@ describe("WorkflowResultsTab", () => {
expect(css).toMatch(/\.workflow-output-modal-name\s*\{[^}]*min-width:\s*0;[^}]*overflow-wrap:\s*anywhere;/); expect(css).toMatch(/\.workflow-output-modal-name\s*\{[^}]*min-width:\s*0;[^}]*overflow-wrap:\s*anywhere;/);
}); });
it("keeps workflow output header actions visible without hardcoded badge sizing", () => {
const baseCss = loadAllAppCssBaseOnly();
const allCss = loadAllAppCss();
expect(baseCss).toMatch(/\.phase-badge\s*\{[^}]*font-size:\s*calc\(var\(--space-sm\) \+ var\(--space-xs\) \* 0\.75\);/);
expect(baseCss).toMatch(/\.workflow-result-output-header\s*\{[^}]*flex-wrap:\s*wrap;/);
expect(baseCss).toMatch(/\.workflow-result-output-preview\s*\{[^}]*flex:\s*1 1 calc\(var\(--space-2xl\) \* 6\);[^}]*min-width:\s*0;/);
expect(allCss).toMatch(/@media \(max-width: 768px\)\s*\{[\s\S]*?\.workflow-result-output-preview\s*\{[^}]*flex-basis:\s*100%;[^}]*order:\s*3;/);
expect(allCss).toMatch(/@media \(max-width: 768px\)\s*\{[\s\S]*?\.workflow-result-mode-toggle\s*\{[^}]*margin-left:\s*0;/);
});
it("allows workflow modal controls to wrap on mobile so the close button stays visible", () => { it("allows workflow modal controls to wrap on mobile so the close button stays visible", () => {
const css = loadAllAppCss(); const css = loadAllAppCss();

View File

@@ -0,0 +1,60 @@
import { describe, expect, it } from "vitest";
import {
BranchConflictError,
DiffVolumeRegressionError,
MergeAbortedError,
SquashAuditError,
type SquashAuditFindings,
} from "../index.js";
const emptySquashAuditFindings: SquashAuditFindings = {
strategy: "squash",
squashSha: "abc12345",
parentSha: "def67890",
squashSubject: "squash subject",
auditTargetLabel: "abc12345",
lookback: 30,
branchSubjects: [],
recentMainSubjects: [],
duplicateSubjects: [],
touchedFiles: [],
touchedFileOverlaps: [],
findings: [],
issueCount: 0,
clean: true,
};
describe("engine public api barrel", () => {
it.each([
{
name: "BranchConflictError",
ctor: () => new BranchConflictError({
branchName: "task/fn-4238",
conflictingWorktreePath: "/tmp/worktree",
existingTipSha: "0123456789ab",
strandedCommits: [],
startPoint: "main",
recommendedAction: "Rebase the task branch.",
}),
},
{
name: "DiffVolumeRegressionError",
ctor: () => new DiffVolumeRegressionError([]),
},
{
name: "MergeAbortedError",
ctor: () => new MergeAbortedError("merge aborted"),
},
{
name: "SquashAuditError",
ctor: () => new SquashAuditError("FN-4238", "abc12345", emptySquashAuditFindings),
},
])("exports $name from ../index.js", ({ name, ctor }) => {
expect(ctor).toBeTypeOf("function");
const error = ctor();
expect(error).toBeInstanceOf(Error);
expect(error.name).toBe(name);
});
});

View File

@@ -28,6 +28,9 @@ export {
dropAutostashBySha, dropAutostashBySha,
getAutostashDiff, getAutostashDiff,
notifyAutostashOrphans, notifyAutostashOrphans,
DiffVolumeRegressionError,
MergeAbortedError,
SquashAuditError,
type MergerOptions, type MergerOptions,
type AutostashOrphanRecord, type AutostashOrphanRecord,
} from "./merger.js"; } from "./merger.js";