💄 Fix tests nesting

This commit is contained in:
Andrés Moya
2025-10-31 17:01:59 +01:00
committed by Andrey Antukh
parent 5f22220a8b
commit a5e9f7229b

View File

@@ -972,500 +972,499 @@ test.describe("Tokens: Themes modal", () => {
tokenThemeUpdateCreateModal.getByText("Changed Group name"),
).toBeVisible();
});
});
test.describe("Tokens: Apply token", () => {
// When deleting the "enable-token-color" flag, permanently remove this test.
test("User applies color token to a shape without tokens on design-tab", async ({
page,
}) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page);
test.describe("Tokens: Apply token", () => {
// When deleting the "enable-token-color" flag, permanently remove this test.
test("User applies color token to a shape without tokens on design-tab", async ({
page,
}) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page);
await page.getByRole("tab", { name: "Layers" }).click();
await page.getByRole("tab", { name: "Layers" }).click();
await workspacePage.layers
.getByTestId("layer-row")
.filter({ hasText: "Button" })
.click();
await workspacePage.layers
.getByTestId("layer-row")
.filter({ hasText: "Button" })
.click();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
await tokensSidebar
.getByRole("button")
.filter({ hasText: "Color" })
.click();
await tokensSidebar
.getByRole("button")
.filter({ hasText: "Color" })
.click();
await tokensSidebar
.getByRole("button", { name: "colors.black" })
.click({ button: "right" });
await tokenContextMenuForToken.getByText("Fill").click();
await tokensSidebar
.getByRole("button", { name: "colors.black" })
.click({ button: "right" });
await tokenContextMenuForToken.getByText("Fill").click();
const inputColor = workspacePage.page.getByRole("textbox", {
name: "Color",
});
await expect(inputColor).toHaveValue("000000");
const inputColor = workspacePage.page.getByRole("textbox", {
name: "Color",
});
await expect(inputColor).toHaveValue("000000");
});
test("User applies color token to a shape", async ({ page }) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page, { flags: ["enable-token-color"] });
await page.getByRole("tab", { name: "Layers" }).click();
await workspacePage.layers
.getByTestId("layer-row")
.filter({ hasText: "Button" })
.click();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
await tokensSidebar
.getByRole("button")
.filter({ hasText: "Color" })
.click();
await tokensSidebar
.getByRole("button", { name: "colors.black" })
.click({ button: "right" });
await tokenContextMenuForToken.getByText("Fill").click();
await expect(
workspacePage.page.getByLabel("Name: colors.black"),
).toBeVisible();
});
test("User applies typography token to a text shape", async ({ page }) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTypographyTokensFile(page);
await page.getByRole("tab", { name: "Layers" }).click();
await workspacePage.layers
.getByTestId("layer-row")
.filter({ hasText: "Some Text" })
.click();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
await tokensSidebar
.getByRole("button")
.filter({ hasText: "Typography" })
.click();
await tokensSidebar.getByRole("button", { name: "Full" }).click();
const fontSizeInput = workspacePage.rightSidebar.getByRole("textbox", {
name: "Font Size",
});
await expect(fontSizeInput).toBeVisible();
await expect(fontSizeInput).toHaveValue("100");
});
test("User edits typography token and all fields are valid", async ({
page,
}) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page);
await tokensSidebar
.getByRole("button")
.filter({ hasText: "Typography" })
.click();
// Open edit modal for "Full" typography token
const token = tokensSidebar.getByRole("button", { name: "Full" });
await token.click({ button: "right" });
await page.getByText("Edit token").click();
// Modal opens
await expect(tokensUpdateCreateModal).toBeVisible();
const saveButton = tokensUpdateCreateModal.getByRole("button", {
name: /save/i,
});
test("User applies color token to a shape", async ({ page }) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTokensFile(page, { flags: ["enable-token-color"] });
// Fill font-family to verify to verify that input value doesn't get split into list of characters
const fontFamilyField = tokensUpdateCreateModal
.getByLabel("Font family")
.first();
await fontFamilyField.fill("OneWord");
await page.getByRole("tab", { name: "Layers" }).click();
// Invalidate incorrect values for font size
const fontSizeField = tokensUpdateCreateModal.getByLabel(/Font Size/i);
await fontSizeField.fill("invalid");
await expect(
tokensUpdateCreateModal.getByText(/Invalid token value:/),
).toBeVisible();
await expect(saveButton).toBeDisabled();
await workspacePage.layers
.getByTestId("layer-row")
.filter({ hasText: "Button" })
.click();
// Show error with line-height depending on invalid font-size
await fontSizeField.fill("");
await expect(saveButton).toBeDisabled();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
// Fill in values for all fields and verify they persist when switching tabs
await fontSizeField.fill("16");
await tokensSidebar
.getByRole("button")
.filter({ hasText: "Color" })
.click();
const fontWeightField =
tokensUpdateCreateModal.getByLabel(/Font Weight/i);
const letterSpacingField =
tokensUpdateCreateModal.getByLabel(/Letter Spacing/i);
const lineHeightField =
tokensUpdateCreateModal.getByLabel(/Line Height/i);
const textCaseField = tokensUpdateCreateModal.getByLabel(/Text Case/i);
const textDecorationField =
tokensUpdateCreateModal.getByLabel(/Text Decoration/i);
await tokensSidebar
.getByRole("button", { name: "colors.black" })
.click({ button: "right" });
await tokenContextMenuForToken.getByText("Fill").click();
// Capture all values before switching tabs
const originalValues = {
fontSize: await fontSizeField.inputValue(),
fontFamily: await fontFamilyField.inputValue(),
fontWeight: await fontWeightField.inputValue(),
letterSpacing: await letterSpacingField.inputValue(),
lineHeight: await lineHeightField.inputValue(),
textCase: await textCaseField.inputValue(),
textDecoration: await textDecorationField.inputValue(),
};
await expect(
workspacePage.page.getByLabel("Name: colors.black"),
).toBeVisible();
// Switch to reference tab and back to composite tab
const referenceTabButton =
tokensUpdateCreateModal.getByTestId("reference-opt");
await referenceTabButton.click();
// Empty reference tab should be disabled
await expect(saveButton).toBeDisabled();
const compositeTabButton =
tokensUpdateCreateModal.getByTestId("composite-opt");
await compositeTabButton.click();
// Filled composite tab should be enabled
await expect(saveButton).toBeEnabled();
// Verify all values are preserved after switching tabs
await expect(fontSizeField).toHaveValue(originalValues.fontSize);
await expect(fontFamilyField).toHaveValue(originalValues.fontFamily);
await expect(fontWeightField).toHaveValue(originalValues.fontWeight);
await expect(letterSpacingField).toHaveValue(
originalValues.letterSpacing,
);
await expect(lineHeightField).toHaveValue(originalValues.lineHeight);
await expect(textCaseField).toHaveValue(originalValues.textCase);
await expect(textDecorationField).toHaveValue(
originalValues.textDecoration,
);
await saveButton.click();
// Modal should close, token should be visible (with new name) in sidebar
await expect(tokensUpdateCreateModal).not.toBeVisible();
});
test("User cant submit empty typography token or reference", async ({
page,
}) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page);
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await tokensTabPanel
.getByRole("button", { name: "Add Token: Typography" })
.click();
await expect(tokensUpdateCreateModal).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name");
await nameField.fill("typography.empty");
const valueField = tokensUpdateCreateModal.getByLabel("Font Size");
// Insert a value and then delete it
await valueField.fill("1");
await valueField.fill("");
// Submit button should be disabled when field is empty
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
await expect(submitButton).toBeDisabled();
// Switch to reference tab, should not be submittable either
const referenceTabButton =
tokensUpdateCreateModal.getByTestId("reference-opt");
await referenceTabButton.click();
await expect(submitButton).toBeDisabled();
});
test("User adds typography token with reference", async ({ page }) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page);
const newTokenTitle = "NewReference";
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await tokensTabPanel
.getByRole("button", { name: "Add Token: Typography" })
.click();
await expect(tokensUpdateCreateModal).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name");
await nameField.fill(newTokenTitle);
const referenceTabButton =
tokensUpdateCreateModal.getByTestId("reference-opt");
referenceTabButton.click();
const referenceField = tokensUpdateCreateModal.getByLabel("Reference");
await referenceField.fill("{Full}");
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
test("User applies typography token to a text shape", async ({ page }) => {
const { workspacePage, tokensSidebar, tokenContextMenuForToken } =
await setupTypographyTokensFile(page);
const resolvedValue =
await tokensUpdateCreateModal.getByText("Resolved value:");
await expect(resolvedValue).toBeVisible();
await expect(resolvedValue).toContainText("Font Family: 42dot Sans");
await expect(resolvedValue).toContainText("Font Size: 100");
await expect(resolvedValue).toContainText("Font Weight: 300");
await expect(resolvedValue).toContainText("Letter Spacing: 2");
await expect(resolvedValue).toContainText("Text Case: uppercase");
await expect(resolvedValue).toContainText("Text Decoration: underline");
await page.getByRole("tab", { name: "Layers" }).click();
await expect(submitButton).toBeEnabled();
await submitButton.click();
await workspacePage.layers
.getByTestId("layer-row")
.filter({ hasText: "Some Text" })
.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
const tokensTabButton = page.getByRole("tab", { name: "Tokens" });
await tokensTabButton.click();
await tokensSidebar
.getByRole("button")
.filter({ hasText: "Typography" })
.click();
await tokensSidebar.getByRole("button", { name: "Full" }).click();
const fontSizeInput = workspacePage.rightSidebar.getByRole("textbox", {
name: "Font Size",
});
await expect(fontSizeInput).toBeVisible();
await expect(fontSizeInput).toHaveValue("100");
const newToken = tokensSidebar.getByRole("button", {
name: newTokenTitle,
});
test("User edits typography token and all fields are valid", async ({
page,
}) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page);
await expect(newToken).toBeVisible();
});
await tokensSidebar
.getByRole("button")
.filter({ hasText: "Typography" })
test("User adds shadow token with multiple shadows and applies it to shape", async ({ page, }) => {
const { tokensUpdateCreateModal, tokensSidebar, workspacePage, tokenContextMenuForToken } =
await setupTokensFile(page, { flags: ["enable-token-shadow"] });
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await test.step("Stage 1: Basic open", async () => {
// User adds shadow via the sidebar
await tokensTabPanel
.getByRole("button", { name: "Add Token: Shadow" })
.click();
// Open edit modal for "Full" typography token
const token = tokensSidebar.getByRole("button", { name: "Full" });
await token.click({ button: "right" });
await page.getByText("Edit token").click();
// Modal opens
await expect(tokensUpdateCreateModal).toBeVisible();
const saveButton = tokensUpdateCreateModal.getByRole("button", {
name: /save/i,
const nameField = tokensUpdateCreateModal.getByLabel("Name");
await nameField.fill("shadow.primary");
// User adds first shadow with a color from the color ramp
const firstShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-0",
);
await expect(firstShadowFields).toBeVisible();
// Fill in the shadow values
const offsetXInput = firstShadowFields.getByLabel("X");
const offsetYInput = firstShadowFields.getByLabel("Y");
const blurInput = firstShadowFields.getByLabel("Blur");
const spreadInput = firstShadowFields.getByLabel("Spread");
await offsetXInput.fill("2");
await offsetYInput.fill("2");
await blurInput.fill("4");
await spreadInput.fill("0");
// Add color using the color picker
const colorBullet = firstShadowFields.getByTestId(
"token-form-color-bullet",
);
await colorBullet.click();
// Click on the color ramp to select a color
const valueSaturationSelector = tokensUpdateCreateModal.getByTestId(
"value-saturation-selector",
);
await expect(valueSaturationSelector).toBeVisible();
await valueSaturationSelector.click({ position: { x: 50, y: 50 } });
// Verify that a color value was set
const colorInput = firstShadowFields.getByLabel("Color");
const firstColorValue = await colorInput.inputValue();
await expect(firstColorValue).toMatch(/^rgb(.*)$/);
// Wait for validation to complete
await expect(tokensUpdateCreateModal.getByText(/Resolved value:/).first()).toBeVisible();
// Save button should be enabled
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
await expect(submitButton).toBeEnabled();
});
// Fill font-family to verify to verify that input value doesn't get split into list of characters
const fontFamilyField = tokensUpdateCreateModal
.getByLabel("Font family")
.first();
await fontFamilyField.fill("OneWord");
await test.step("Stage 2: Shadow adding/removing works", async () => {
const firstShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-0",
);
const colorInput = firstShadowFields.getByLabel("Color");
const firstColorValue = await colorInput.inputValue();
// Invalidate incorrect values for font size
const fontSizeField = tokensUpdateCreateModal.getByLabel(/Font Size/i);
await fontSizeField.fill("invalid");
await expect(
tokensUpdateCreateModal.getByText(/Invalid token value:/),
).toBeVisible();
await expect(saveButton).toBeDisabled();
// User adds a second shadow
const addButton = firstShadowFields.getByTestId("shadow-add-button-0");
await addButton.click();
// Show error with line-height depending on invalid font-size
await fontSizeField.fill("");
await expect(saveButton).toBeDisabled();
const secondShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-1",
);
await expect(secondShadowFields).toBeVisible();
// Fill in values for all fields and verify they persist when switching tabs
await fontSizeField.fill("16");
// User adds a third shadow
const addButton2 = secondShadowFields.getByTestId("shadow-add-button-1");
await addButton2.click();
const fontWeightField =
tokensUpdateCreateModal.getByLabel(/Font Weight/i);
const letterSpacingField =
tokensUpdateCreateModal.getByLabel(/Letter Spacing/i);
const lineHeightField =
tokensUpdateCreateModal.getByLabel(/Line Height/i);
const textCaseField = tokensUpdateCreateModal.getByLabel(/Text Case/i);
const textDecorationField =
tokensUpdateCreateModal.getByLabel(/Text Decoration/i);
const thirdShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-2",
);
await expect(thirdShadowFields).toBeVisible();
// Capture all values before switching tabs
const originalValues = {
fontSize: await fontSizeField.inputValue(),
fontFamily: await fontFamilyField.inputValue(),
fontWeight: await fontWeightField.inputValue(),
letterSpacing: await letterSpacingField.inputValue(),
lineHeight: await lineHeightField.inputValue(),
textCase: await textCaseField.inputValue(),
textDecoration: await textDecorationField.inputValue(),
};
// User adds values for the third shadow
const thirdOffsetXInput = thirdShadowFields.getByLabel("X");
const thirdOffsetYInput = thirdShadowFields.getByLabel("Y");
const thirdBlurInput = thirdShadowFields.getByLabel("Blur");
const thirdSpreadInput = thirdShadowFields.getByLabel("Spread");
const thirdColorInput = thirdShadowFields.getByLabel("Color");
// Switch to reference tab and back to composite tab
const referenceTabButton =
tokensUpdateCreateModal.getByTestId("reference-opt");
await thirdOffsetXInput.fill("10");
await thirdOffsetYInput.fill("10");
await thirdBlurInput.fill("20");
await thirdSpreadInput.fill("5");
await thirdColorInput.fill("#FF0000");
// User removes the 2nd shadow
const removeButton2 = secondShadowFields.getByTestId("shadow-remove-button-1");
await removeButton2.click();
// Verify second shadow is removed
await expect(secondShadowFields.getByTestId("shadow-add-button-3")).not.toBeVisible();
// Verify that the first shadow kept its values
const firstOffsetXValue = await firstShadowFields.getByLabel("X").inputValue();
const firstOffsetYValue = await firstShadowFields.getByLabel("Y").inputValue();
const firstBlurValue = await firstShadowFields.getByLabel("Blur").inputValue();
const firstSpreadValue = await firstShadowFields.getByLabel("Spread").inputValue();
const firstColorValueAfter = await firstShadowFields.getByLabel("Color").inputValue();
await expect(firstOffsetXValue).toBe("2");
await expect(firstOffsetYValue).toBe("2");
await expect(firstBlurValue).toBe("4");
await expect(firstSpreadValue).toBe("0");
await expect(firstColorValueAfter).toBe(firstColorValue);
// Verify that the third shadow (now second) kept its values
// After removing index 1, the third shadow becomes the second shadow at index 1
const newSecondShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-1",
);
await expect(newSecondShadowFields).toBeVisible();
const secondOffsetXValue = await newSecondShadowFields.getByLabel("X").inputValue();
const secondOffsetYValue = await newSecondShadowFields.getByLabel("Y").inputValue();
const secondBlurValue = await newSecondShadowFields.getByLabel("Blur").inputValue();
const secondSpreadValue = await newSecondShadowFields.getByLabel("Spread").inputValue();
const secondColorValue = await newSecondShadowFields.getByLabel("Color").inputValue();
await expect(secondOffsetXValue).toBe("10");
await expect(secondOffsetYValue).toBe("10");
await expect(secondBlurValue).toBe("20");
await expect(secondSpreadValue).toBe("5");
await expect(secondColorValue).toBe("#FF0000");
});
await test.step("Stage 3: Restore when switching tabs works", async () => {
const firstShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-0",
);
const newSecondShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-1",
);
const colorInput = firstShadowFields.getByLabel("Color");
const firstColorValue = await colorInput.inputValue();
// Switch to reference tab
const referenceTabButton = tokensUpdateCreateModal.getByTestId("reference-opt");
await referenceTabButton.click();
// Empty reference tab should be disabled
await expect(saveButton).toBeDisabled();
// Verify we're in reference mode - the composite fields should not be visible
await expect(firstShadowFields).not.toBeVisible();
const compositeTabButton =
tokensUpdateCreateModal.getByTestId("composite-opt");
// Switch back to composite tab
const compositeTabButton = tokensUpdateCreateModal.getByTestId("composite-opt");
await compositeTabButton.click();
// Filled composite tab should be enabled
await expect(saveButton).toBeEnabled();
// Verify that shadows are restored
await expect(firstShadowFields).toBeVisible();
await expect(newSecondShadowFields).toBeVisible();
// Verify all values are preserved after switching tabs
await expect(fontSizeField).toHaveValue(originalValues.fontSize);
await expect(fontFamilyField).toHaveValue(originalValues.fontFamily);
await expect(fontWeightField).toHaveValue(originalValues.fontWeight);
await expect(letterSpacingField).toHaveValue(
originalValues.letterSpacing,
);
await expect(lineHeightField).toHaveValue(originalValues.lineHeight);
await expect(textCaseField).toHaveValue(originalValues.textCase);
await expect(textDecorationField).toHaveValue(
originalValues.textDecoration,
);
// Verify first shadow values are still there
const restoredFirstOffsetX = await firstShadowFields.getByLabel("X").inputValue();
const restoredFirstOffsetY = await firstShadowFields.getByLabel("Y").inputValue();
const restoredFirstBlur = await firstShadowFields.getByLabel("Blur").inputValue();
const restoredFirstSpread = await firstShadowFields.getByLabel("Spread").inputValue();
const restoredFirstColor = await firstShadowFields.getByLabel("Color").inputValue();
await saveButton.click();
await expect(restoredFirstOffsetX).toBe("2");
await expect(restoredFirstOffsetY).toBe("2");
await expect(restoredFirstBlur).toBe("4");
await expect(restoredFirstSpread).toBe("0");
await expect(restoredFirstColor).toBe(firstColorValue);
// Modal should close, token should be visible (with new name) in sidebar
await expect(tokensUpdateCreateModal).not.toBeVisible();
// Verify second shadow values are still there
const restoredSecondOffsetX = await newSecondShadowFields.getByLabel("X").inputValue();
const restoredSecondOffsetY = await newSecondShadowFields.getByLabel("Y").inputValue();
const restoredSecondBlur = await newSecondShadowFields.getByLabel("Blur").inputValue();
const restoredSecondSpread = await newSecondShadowFields.getByLabel("Spread").inputValue();
const restoredSecondColor = await newSecondShadowFields.getByLabel("Color").inputValue();
await expect(restoredSecondOffsetX).toBe("10");
await expect(restoredSecondOffsetY).toBe("10");
await expect(restoredSecondBlur).toBe("20");
await expect(restoredSecondSpread).toBe("5");
await expect(restoredSecondColor).toBe("#FF0000");
});
test("User cant submit empty typography token or reference", async ({
page,
}) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page);
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await tokensTabPanel
.getByRole("button", { name: "Add Token: Typography" })
.click();
await expect(tokensUpdateCreateModal).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name");
await nameField.fill("typography.empty");
const valueField = tokensUpdateCreateModal.getByLabel("Font Size");
// Insert a value and then delete it
await valueField.fill("1");
await valueField.fill("");
// Submit button should be disabled when field is empty
await test.step("Stage 4: Layer application works", async () => {
// Save the token
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
await expect(submitButton).toBeDisabled();
// Switch to reference tab, should not be submittable either
const referenceTabButton =
tokensUpdateCreateModal.getByTestId("reference-opt");
await referenceTabButton.click();
await expect(submitButton).toBeDisabled();
});
test("User adds typography token with reference", async ({ page }) => {
const { tokensUpdateCreateModal, tokenThemesSetsSidebar, tokensSidebar } =
await setupTypographyTokensFile(page);
const newTokenTitle = "NewReference";
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
await tokensTabPanel
.getByRole("button", { name: "Add Token: Typography" })
.click();
await expect(tokensUpdateCreateModal).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name");
await nameField.fill(newTokenTitle);
const referenceTabButton =
tokensUpdateCreateModal.getByTestId("reference-opt");
referenceTabButton.click();
const referenceField = tokensUpdateCreateModal.getByLabel("Reference");
await referenceField.fill("{Full}");
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
const resolvedValue =
await tokensUpdateCreateModal.getByText("Resolved value:");
await expect(resolvedValue).toBeVisible();
await expect(resolvedValue).toContainText("Font Family: 42dot Sans");
await expect(resolvedValue).toContainText("Font Size: 100");
await expect(resolvedValue).toContainText("Font Weight: 300");
await expect(resolvedValue).toContainText("Letter Spacing: 2");
await expect(resolvedValue).toContainText("Text Case: uppercase");
await expect(resolvedValue).toContainText("Text Decoration: underline");
await expect(submitButton).toBeEnabled();
await submitButton.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
const newToken = tokensSidebar.getByRole("button", {
name: newTokenTitle,
// Verify token appears in sidebar
const shadowToken = tokensSidebar.getByRole("button", {
name: "shadow.primary",
});
await expect(newToken).toBeVisible();
});
await expect(shadowToken).toBeEnabled();
test("User adds shadow token with multiple shadows and applies it to shape", async ({
page,
}) => {
const { tokensUpdateCreateModal, tokensSidebar, workspacePage, tokenContextMenuForToken } =
await setupTokensFile(page, { flags: ["enable-token-shadow"] });
// Apply the shadow
await workspacePage.clickLayers();
await workspacePage.clickLeafLayer("Button");
const tokensTabPanel = page.getByRole("tabpanel", { name: "tokens" });
const shadowSection = workspacePage.rightSidebar.getByText("Drop shadow");
await expect(shadowSection).toHaveCount(0);
await test.step("Stage 1: Basic open", async () => {
// User adds shadow via the sidebar
await tokensTabPanel
.getByRole("button", { name: "Add Token: Shadow" })
.click();
await page.getByRole("tab", { name: "Tokens" }).click();
await shadowToken.click();
await expect(tokensUpdateCreateModal).toBeVisible();
const nameField = tokensUpdateCreateModal.getByLabel("Name");
await nameField.fill("shadow.primary");
// User adds first shadow with a color from the color ramp
const firstShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-0",
);
await expect(firstShadowFields).toBeVisible();
// Fill in the shadow values
const offsetXInput = firstShadowFields.getByLabel("X");
const offsetYInput = firstShadowFields.getByLabel("Y");
const blurInput = firstShadowFields.getByLabel("Blur");
const spreadInput = firstShadowFields.getByLabel("Spread");
await offsetXInput.fill("2");
await offsetYInput.fill("2");
await blurInput.fill("4");
await spreadInput.fill("0");
// Add color using the color picker
const colorBullet = firstShadowFields.getByTestId(
"token-form-color-bullet",
);
await colorBullet.click();
// Click on the color ramp to select a color
const valueSaturationSelector = tokensUpdateCreateModal.getByTestId(
"value-saturation-selector",
);
await expect(valueSaturationSelector).toBeVisible();
await valueSaturationSelector.click({ position: { x: 50, y: 50 } });
// Verify that a color value was set
const colorInput = firstShadowFields.getByLabel("Color");
const firstColorValue = await colorInput.inputValue();
await expect(firstColorValue).toMatch(/^rgb(.*)$/);
// Wait for validation to complete
await expect(tokensUpdateCreateModal.getByText(/Resolved value:/).first()).toBeVisible();
// Save button should be enabled
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
await expect(submitButton).toBeEnabled();
});
await test.step("Stage 2: Shadow adding/removing works", async () => {
const firstShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-0",
);
const colorInput = firstShadowFields.getByLabel("Color");
const firstColorValue = await colorInput.inputValue();
// User adds a second shadow
const addButton = firstShadowFields.getByTestId("shadow-add-button-0");
await addButton.click();
const secondShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-1",
);
await expect(secondShadowFields).toBeVisible();
// User adds a third shadow
const addButton2 = secondShadowFields.getByTestId("shadow-add-button-1");
await addButton2.click();
const thirdShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-2",
);
await expect(thirdShadowFields).toBeVisible();
// User adds values for the third shadow
const thirdOffsetXInput = thirdShadowFields.getByLabel("X");
const thirdOffsetYInput = thirdShadowFields.getByLabel("Y");
const thirdBlurInput = thirdShadowFields.getByLabel("Blur");
const thirdSpreadInput = thirdShadowFields.getByLabel("Spread");
const thirdColorInput = thirdShadowFields.getByLabel("Color");
await thirdOffsetXInput.fill("10");
await thirdOffsetYInput.fill("10");
await thirdBlurInput.fill("20");
await thirdSpreadInput.fill("5");
await thirdColorInput.fill("#FF0000");
// User removes the 2nd shadow
const removeButton2 = secondShadowFields.getByTestId("shadow-remove-button-1");
await removeButton2.click();
// Verify second shadow is removed
await expect(secondShadowFields.getByTestId("shadow-add-button-3")).not.toBeVisible();
// Verify that the first shadow kept its values
const firstOffsetXValue = await firstShadowFields.getByLabel("X").inputValue();
const firstOffsetYValue = await firstShadowFields.getByLabel("Y").inputValue();
const firstBlurValue = await firstShadowFields.getByLabel("Blur").inputValue();
const firstSpreadValue = await firstShadowFields.getByLabel("Spread").inputValue();
const firstColorValueAfter = await firstShadowFields.getByLabel("Color").inputValue();
await expect(firstOffsetXValue).toBe("2");
await expect(firstOffsetYValue).toBe("2");
await expect(firstBlurValue).toBe("4");
await expect(firstSpreadValue).toBe("0");
await expect(firstColorValueAfter).toBe(firstColorValue);
// Verify that the third shadow (now second) kept its values
// After removing index 1, the third shadow becomes the second shadow at index 1
const newSecondShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-1",
);
await expect(newSecondShadowFields).toBeVisible();
const secondOffsetXValue = await newSecondShadowFields.getByLabel("X").inputValue();
const secondOffsetYValue = await newSecondShadowFields.getByLabel("Y").inputValue();
const secondBlurValue = await newSecondShadowFields.getByLabel("Blur").inputValue();
const secondSpreadValue = await newSecondShadowFields.getByLabel("Spread").inputValue();
const secondColorValue = await newSecondShadowFields.getByLabel("Color").inputValue();
await expect(secondOffsetXValue).toBe("10");
await expect(secondOffsetYValue).toBe("10");
await expect(secondBlurValue).toBe("20");
await expect(secondSpreadValue).toBe("5");
await expect(secondColorValue).toBe("#FF0000");
});
await test.step("Stage 3: Restore when switching tabs works", async () => {
const firstShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-0",
);
const newSecondShadowFields = tokensUpdateCreateModal.getByTestId(
"shadow-input-fields-1",
);
const colorInput = firstShadowFields.getByLabel("Color");
const firstColorValue = await colorInput.inputValue();
// Switch to reference tab
const referenceTabButton = tokensUpdateCreateModal.getByTestId("reference-opt");
await referenceTabButton.click();
// Verify we're in reference mode - the composite fields should not be visible
await expect(firstShadowFields).not.toBeVisible();
// Switch back to composite tab
const compositeTabButton = tokensUpdateCreateModal.getByTestId("composite-opt");
await compositeTabButton.click();
// Verify that shadows are restored
await expect(firstShadowFields).toBeVisible();
await expect(newSecondShadowFields).toBeVisible();
// Verify first shadow values are still there
const restoredFirstOffsetX = await firstShadowFields.getByLabel("X").inputValue();
const restoredFirstOffsetY = await firstShadowFields.getByLabel("Y").inputValue();
const restoredFirstBlur = await firstShadowFields.getByLabel("Blur").inputValue();
const restoredFirstSpread = await firstShadowFields.getByLabel("Spread").inputValue();
const restoredFirstColor = await firstShadowFields.getByLabel("Color").inputValue();
await expect(restoredFirstOffsetX).toBe("2");
await expect(restoredFirstOffsetY).toBe("2");
await expect(restoredFirstBlur).toBe("4");
await expect(restoredFirstSpread).toBe("0");
await expect(restoredFirstColor).toBe(firstColorValue);
// Verify second shadow values are still there
const restoredSecondOffsetX = await newSecondShadowFields.getByLabel("X").inputValue();
const restoredSecondOffsetY = await newSecondShadowFields.getByLabel("Y").inputValue();
const restoredSecondBlur = await newSecondShadowFields.getByLabel("Blur").inputValue();
const restoredSecondSpread = await newSecondShadowFields.getByLabel("Spread").inputValue();
const restoredSecondColor = await newSecondShadowFields.getByLabel("Color").inputValue();
await expect(restoredSecondOffsetX).toBe("10");
await expect(restoredSecondOffsetY).toBe("10");
await expect(restoredSecondBlur).toBe("20");
await expect(restoredSecondSpread).toBe("5");
await expect(restoredSecondColor).toBe("#FF0000");
});
await test.step("Stage 4: Layer application works", async () => {
// Save the token
const submitButton = tokensUpdateCreateModal.getByRole("button", {
name: "Save",
});
await submitButton.click();
await expect(tokensUpdateCreateModal).not.toBeVisible();
// Verify token appears in sidebar
const shadowToken = tokensSidebar.getByRole("button", {
name: "shadow.primary",
});
await expect(shadowToken).toBeEnabled();
// Apply the shadow
await workspacePage.clickLayers();
await workspacePage.clickLeafLayer("Button");
const shadowSection = workspacePage.rightSidebar.getByText("Drop shadow");
await expect(shadowSection).toHaveCount(0);
await page.getByRole("tab", { name: "Tokens" }).click();
await shadowToken.click();
await expect(shadowSection).toHaveCount(2);
});
await expect(shadowSection).toHaveCount(2);
});
});
});