Skip to content

Commit 2f73e3f

Browse files
committed
fix: cross-scope configuration import failing incorrectly
Importing configuration from Global scope to Local scope failed with "Configuration scope has changed since preview" error Cause: comparing preview.sourceTarget (original export scope) with current import target scope, which always mismatched for cross-scope imports Solution: added targetScope field to ImportPreviewData to store the intended import scope at preview time and compare against it during confirmation
1 parent 5cd2ea0 commit 2f73e3f

6 files changed

Lines changed: 91 additions & 1 deletion

File tree

src/internal/managers/import-export-manager.spec.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,7 @@ describe("ImportExportManager", () => {
877877
buttons: sampleButtons,
878878
fileUri: "/import/config.json",
879879
sourceTarget: "global" as const,
880+
targetScope: "global" as const,
880881
timestamp: Date.now(),
881882
};
882883

@@ -895,6 +896,7 @@ describe("ImportExportManager", () => {
895896
buttons: sampleButtons,
896897
fileUri: "/import/config.json",
897898
sourceTarget: "global" as const,
899+
targetScope: "global" as const,
898900
timestamp: Date.now() - EXPIRED_PREVIEW_OFFSET_MS,
899901
};
900902

@@ -915,6 +917,7 @@ describe("ImportExportManager", () => {
915917
buttons: sampleButtons,
916918
fileUri: "/import/config.json",
917919
sourceTarget: "global" as const,
920+
targetScope: "global" as const,
918921
timestamp: Date.now(),
919922
};
920923

@@ -934,6 +937,7 @@ describe("ImportExportManager", () => {
934937
buttons: sampleButtons,
935938
fileUri: "/import/config.json",
936939
sourceTarget: "global" as const,
940+
targetScope: "global" as const,
937941
timestamp: Date.now(),
938942
};
939943

src/internal/managers/import-export-manager.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ export class ImportExportManager {
233233
buttons: data.buttons,
234234
fileUri: fileUri.fsPath,
235235
sourceTarget: data.configurationTarget,
236+
targetScope: targetScope as ConfigurationTarget,
236237
timestamp: Date.now(),
237238
};
238239

src/internal/providers/webview-provider.spec.ts

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -714,5 +714,88 @@ describe("webview-provider", () => {
714714
);
715715
});
716716
});
717+
718+
describe("confirmImport", () => {
719+
it("should reject import when scope changed between preview and confirm", async () => {
720+
const mockImportExportManager = {
721+
confirmImport: jest.fn(),
722+
};
723+
724+
const preview = {
725+
analysis: { added: [], modified: [], shortcutConflicts: [], unchanged: [] },
726+
buttons: [{ command: "test", name: "Test" }],
727+
fileUri: "/import/config.json",
728+
sourceTarget: "global" as const,
729+
targetScope: "global" as const,
730+
timestamp: Date.now(),
731+
};
732+
733+
const message = {
734+
data: { preview, strategy: "merge" },
735+
requestId: "confirm-import-request",
736+
type: "confirmImport" as const,
737+
};
738+
739+
(mockConfigManager.getCurrentConfigurationTarget as jest.Mock).mockReturnValue("local");
740+
741+
await handleWebviewMessage(
742+
message,
743+
mockWebview,
744+
mockConfigReader,
745+
mockConfigManager,
746+
mockImportExportManager as never
747+
);
748+
749+
expect(mockImportExportManager.confirmImport).not.toHaveBeenCalled();
750+
expect(mockWebview.postMessage).toHaveBeenCalledWith({
751+
error: expect.stringContaining("scope"),
752+
requestId: "confirm-import-request",
753+
type: "error",
754+
});
755+
});
756+
757+
it("should proceed with import when scope matches between preview and confirm", async () => {
758+
const mockImportExportManager = {
759+
confirmImport: jest.fn().mockResolvedValue({
760+
backupPath: "/backup/path",
761+
conflictsResolved: 0,
762+
importedCount: 1,
763+
success: true,
764+
}),
765+
};
766+
767+
const preview = {
768+
analysis: { added: [], modified: [], shortcutConflicts: [], unchanged: [] },
769+
buttons: [{ command: "test", name: "Test" }],
770+
fileUri: "/import/config.json",
771+
sourceTarget: "global" as const,
772+
targetScope: "local" as const,
773+
timestamp: Date.now(),
774+
};
775+
776+
const message = {
777+
data: { preview, strategy: "merge" },
778+
requestId: "confirm-import-request",
779+
type: "confirmImport" as const,
780+
};
781+
782+
(mockConfigManager.getCurrentConfigurationTarget as jest.Mock).mockReturnValue("local");
783+
784+
await handleWebviewMessage(
785+
message,
786+
mockWebview,
787+
mockConfigReader,
788+
mockConfigManager,
789+
mockImportExportManager as never
790+
);
791+
792+
expect(mockImportExportManager.confirmImport).toHaveBeenCalledWith(preview, "local", "merge");
793+
expect(mockWebview.postMessage).toHaveBeenCalledWith({
794+
data: expect.objectContaining({ success: true }),
795+
requestId: "confirm-import-request",
796+
type: "success",
797+
});
798+
});
799+
});
717800
});
718801
});

src/internal/providers/webview-provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ export const handleWebviewMessage = async (
329329
}
330330
const { preview, strategy } = message.data;
331331
const confirmTarget = configManager.getCurrentConfigurationTarget();
332-
if (preview.sourceTarget !== confirmTarget) {
332+
if (preview.targetScope !== confirmTarget) {
333333
throw new Error(MESSAGES.ERROR.configScopeChangedSincePreview);
334334
}
335335
const confirmResult = await importExportManager.confirmImport(

src/shared/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ export type ImportPreviewData = {
199199
buttons: ButtonConfigWithOptionalId[];
200200
fileUri: string;
201201
sourceTarget: ConfigurationTarget;
202+
targetScope: ConfigurationTarget;
202203
timestamp: number;
203204
};
204205

src/view/src/mock/vscode-mock.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ class VSCodeMock {
187187
buttons: MOCK_IMPORT_BUTTONS,
188188
fileUri: "/mock/import/config.json",
189189
sourceTarget: this.configurationTarget as ConfigurationTarget,
190+
targetScope: this.configurationTarget as ConfigurationTarget,
190191
timestamp: Date.now(),
191192
};
192193
}

0 commit comments

Comments
 (0)