mirror of
https://github.com/multica-ai/multica.git
synced 2026-06-17 11:48:42 +02:00
fix(skills): preserve bulk flow after conflict resolution
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -453,4 +453,79 @@ describe("RuntimeLocalSkillImportPanel", () => {
|
||||
|
||||
expect(await screen.findByText("Updated")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps bulk completion behavior when conflict resolution leaves one success", async () => {
|
||||
mockRuntimeLocalSkillsOptions.mockReturnValue({
|
||||
queryKey: ["runtimes", "local-skills", "runtime-1"],
|
||||
queryFn: () =>
|
||||
Promise.resolve({
|
||||
supported: true,
|
||||
skills: [MOCK_SKILL_A, MOCK_SKILL_B],
|
||||
}),
|
||||
});
|
||||
mockResolveRuntimeLocalSkillImport
|
||||
.mockResolvedValueOnce({
|
||||
status: "conflict",
|
||||
conflict: {
|
||||
existing_skill_id: "existing-skill-1",
|
||||
existing_created_by: "user-1",
|
||||
can_overwrite: true,
|
||||
},
|
||||
})
|
||||
.mockRejectedValueOnce(new Error("daemon failed"))
|
||||
.mockResolvedValueOnce({
|
||||
status: "updated",
|
||||
skill: {
|
||||
...MOCK_IMPORTED_SKILL_A,
|
||||
id: "existing-skill-1",
|
||||
},
|
||||
});
|
||||
|
||||
const onImported = vi.fn();
|
||||
const onBulkDone = vi.fn();
|
||||
renderPanel({ onImported, onBulkDone });
|
||||
|
||||
expect(
|
||||
await screen.findByText("Review Helper", {}, { timeout: 5000 }),
|
||||
).toBeInTheDocument();
|
||||
|
||||
const selectAllLabel = screen.getByText(/Select all/i);
|
||||
const selectAllCheckbox = selectAllLabel
|
||||
.closest("label")!
|
||||
.querySelector("input[type='checkbox']")!;
|
||||
fireEvent.click(selectAllCheckbox);
|
||||
|
||||
const importButton = screen.getByRole("button", {
|
||||
name: /Import 2 Skills/i,
|
||||
});
|
||||
await waitFor(() => expect(importButton).not.toBeDisabled(), {
|
||||
timeout: 5000,
|
||||
});
|
||||
fireEvent.click(importButton);
|
||||
|
||||
expect(
|
||||
await screen.findByText(/A skill with this name already exists/i),
|
||||
).toBeInTheDocument();
|
||||
|
||||
const applyButton = screen.getByRole("button", {
|
||||
name: /Apply decisions/i,
|
||||
});
|
||||
await waitFor(() => expect(applyButton).not.toBeDisabled(), {
|
||||
timeout: 5000,
|
||||
});
|
||||
fireEvent.click(applyButton);
|
||||
|
||||
await waitFor(
|
||||
() => {
|
||||
expect(
|
||||
screen.getByRole("button", { name: /Done/i }),
|
||||
).toBeInTheDocument();
|
||||
},
|
||||
{ timeout: 10000 },
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /Done/i }));
|
||||
expect(onBulkDone).toHaveBeenCalledTimes(1);
|
||||
expect(onImported).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -68,6 +68,7 @@ type BulkImportState = {
|
||||
phase: "idle" | "importing" | "resolving" | "done" | "cancelled";
|
||||
total: number;
|
||||
completed: number;
|
||||
selectedCount: number;
|
||||
results: BulkImportResult[];
|
||||
};
|
||||
|
||||
@@ -82,6 +83,7 @@ const INITIAL_BULK_STATE: BulkImportState = {
|
||||
phase: "idle",
|
||||
total: 0,
|
||||
completed: 0,
|
||||
selectedCount: 0,
|
||||
results: [],
|
||||
};
|
||||
|
||||
@@ -622,7 +624,13 @@ export function RuntimeLocalSkillImportPanel({
|
||||
const total = skillsToImport.length;
|
||||
|
||||
cancelRef.current = false;
|
||||
setBulkState({ phase: "importing", total, completed: 0, results: [] });
|
||||
setBulkState({
|
||||
phase: "importing",
|
||||
total,
|
||||
completed: 0,
|
||||
selectedCount: total,
|
||||
results: [],
|
||||
});
|
||||
|
||||
const results: BulkImportResult[] = [];
|
||||
|
||||
@@ -1029,7 +1037,11 @@ export function RuntimeLocalSkillImportPanel({
|
||||
);
|
||||
// Single-import flow: navigate to the imported skill detail page.
|
||||
// Multi-import flow: close the dialog even if only one succeeded.
|
||||
if (bulkState.total === 1 && succeeded.length === 1 && succeeded[0]!.skill) {
|
||||
if (
|
||||
bulkState.selectedCount === 1 &&
|
||||
succeeded.length === 1 &&
|
||||
succeeded[0]!.skill
|
||||
) {
|
||||
onImported?.(succeeded[0]!.skill);
|
||||
} else {
|
||||
onBulkDone?.();
|
||||
|
||||
Reference in New Issue
Block a user