🐛 Fix token rename allowing cross-set name collision

This commit is contained in:
Elena Torro
2026-05-18 15:57:59 +02:00
parent 0956becd12
commit 78e7d5a92e
2 changed files with 77 additions and 2 deletions

View File

@@ -696,3 +696,75 @@ test.describe("Remapping group of tokens", () => {
await expect(tokenReference).toBeVisible();
});
});
test.describe("Token rename validation across sets", () => {
test("Renaming a token to a name present in another active set fails validation", async ({
page,
}) => {
const {
tokensUpdateCreateModal,
tokensSidebar,
tokenContextMenuForToken,
tokenThemesSetsSidebar,
} = await setupTokensFileRender(page);
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
// Create "base-color" in the default active set (theme)
await tokensTabPanel
.getByRole("button", { name: "Add Token: Color" })
.click();
await expect(tokensUpdateCreateModal).toBeVisible();
await tokensUpdateCreateModal.getByLabel("Name").fill("base-color");
await tokensUpdateCreateModal.getByLabel("Value").fill("#FFFFFF");
await tokensUpdateCreateModal
.getByRole("button", { name: "Save" })
.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
// Create a new set-b, select it, and enable it
await tokenThemesSetsSidebar
.getByRole("button", { name: "Add set" })
.click();
const setBInput = tokenThemesSetsSidebar.locator("input:focus");
await setBInput.fill("set-b");
await setBInput.press("Enter");
await tokenThemesSetsSidebar
.getByRole("button", { name: "set-b" })
.click();
await tokenThemesSetsSidebar
.getByRole("button", { name: "set-b" })
.getByRole("checkbox")
.click();
// Create "unique-name" in set-b
await tokensTabPanel
.getByRole("button", { name: "Add Token: Color" })
.click();
await expect(tokensUpdateCreateModal).toBeVisible();
await tokensUpdateCreateModal.getByLabel("Name").fill("unique-name");
await tokensUpdateCreateModal.getByLabel("Value").fill("#000000");
await tokensUpdateCreateModal
.getByRole("button", { name: "Save" })
.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
// Try to rename "unique-name" (in set-b) to "base-color" (which exists in the active theme set)
await tokensSidebar
.getByRole("button", { name: "unique-name" })
.click({ button: "right" });
await tokenContextMenuForToken.getByText("Edit token").click();
await expect(tokensUpdateCreateModal).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name");
await nameField.fill("base-color");
// The validation error should appear reactively and the Save button should be disabled
await expect(
tokensUpdateCreateModal.getByText(/already exists at the path/i),
).toBeVisible();
await expect(
tokensUpdateCreateModal.getByRole("button", { name: "Save" }),
).toBeDisabled();
});
});

View File

@@ -27,13 +27,16 @@
tokens-in-selected-set
(mf/deref refs/workspace-all-tokens-in-selected-set)
active-tokens
(mf/deref refs/workspace-active-theme-sets-tokens)
token-path
(mf/with-memo [token]
(ctob/get-token-path token))
tokens-tree-in-selected-set
(mf/with-memo [token-path tokens-in-selected-set]
(-> (ctob/tokens-tree tokens-in-selected-set)
(mf/with-memo [token-path tokens-in-selected-set active-tokens]
(-> (ctob/tokens-tree (merge active-tokens tokens-in-selected-set))
(d/dissoc-in token-path)))
props
(mf/spread-props props {:token-type token-type