diff --git a/frontend/playwright/ui/specs/tokens.spec.js b/frontend/playwright/ui/specs/tokens.spec.js index 2bb09a3564..e6c3239267 100644 --- a/frontend/playwright/ui/specs/tokens.spec.js +++ b/frontend/playwright/ui/specs/tokens.spec.js @@ -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); }); }); });