penpot/frontend/playwright/ui/specs/workspace-texts-typography-persist.spec.js
Andrés Moya 3cd9bfa9de
Add sync with design tokens in external libraries (#10293)
*  Auto link tokens when adding external libraries (provisional)

* 🔧 Refactor tokens-lib initialization

* 🔧 Add separated TokenStatus to store status apart of TokensLib

* 🔧 Make all status operations use the new data structure

* 🔧 Normalize status helper functions and access token sets by id

* 🔧 Rename :tokens-file to :tokens-source

* 🎉 Allow the user to choose the tokens-source of a file

* 🎉 Make tokens library readonly when it's in an external file

* 🎉 Show tokens in library summaries

* 🎉 Show source info in sidebar

* 🔧 Fix integration tests

* 🐛 Propagate changes of token values in external library

* 🎉 Layout updates

* 🔧 Refactor tokens source calculations

* 🔧 Add harder checks for nil or empty values in everything

* 🐛 Fix some integration tests

* 🔧 Add integration tests for tokens in external libs

* 🔧 Validate and repair missing tokens status

* 🎉 Make ui changes optional with config flag

* 🐛 Propagate tokens after synchronizing components in ext library

* 🐛 Propagate tokens after creating new instances

* 🐛 Propagate tokens after synchronizing tokens in ext library

* 🐛 Add a tokens source icon to libraries section (#11439)

* 🐛 Add a tokens source icon to libraries section

* 🐛 Fix ellipsis on library names

* ♻️ Remove code under flag on legacy component

* 🐛 Fix token theme name on inspect tab

* 🎉 Add changes notification (#11476)

* 🎉 Add changes notification

* ♻️ Change fn names

* 🐛 Fix tokens source label truncation and missing translations (#11533)

* 🐛 Fix tokens source label truncation and missing translations

The tokens source file name always showed, even for the current file,
and long names wrapped onto a second line instead of truncating
because the header used flex-wrap and overflow-wrap: break-word
instead of single-line ellipsis.

Show the source row unconditionally (it now displays "This file" when
the source is the current file, matching the connected-library case),
truncate the file name to one line with an ellipsis, and only attach a
tooltip with the full name when the text is actually truncated.

Replace the hardcoded UI strings with translated ones and add their
English and Spanish entries.

AI-assisted-by: claude-sonnet-5

* 🐛 Remove redundant effect dependency in tokens source

file-name-truncated? was listed as a dependency of the with-effect
that checks and observes label truncation, even though it isn't
read inside the effect body. Since the effect itself flips that
state via check-file-name-truncated, including it as a dependency
caused the ResizeObserver to be needlessly disconnected and
reconnected on every truncation change.

AI-assisted-by: claude-sonnet-5

* 🐛 Fix small visual error

* 🐛 Fix problem with plugins

* 🐛 Fix playwright tests

---------

Co-authored-by: Eva Marco <evamarcod@gmail.com>
Co-authored-by: Eva Marco <eva.marco@kaleidos.net>
Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-09-21 15:37:45 +02:00

110 lines
4.8 KiB
JavaScript

import { test, expect } from "@playwright/test";
import { readFile } from "node:fs/promises";
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
// ---------------------------------------------------------------------------
// BUG 10925 - Font family typography asset must not persist across files in
// newly created text layers.
//
// `save-font` writes the current font (plus the typography refs of the edited
// shape, when it uses one) into the session-global `:workspace-global
// :default-font`. That state is what seeds the content of brand-new text
// shapes via `v2-default-text-content`. Because it is session-global it
// survives a file switch, so a text created in file B could end up referencing
// a typography asset that only exists in file A (see workspace/texts.cljs
// save-font and workspace.cljs initialize/finalize-workspace).
//
// This E2E reproduces the leak faithfully in a single SPA session:
// 1. Open file A (has a text shape linked to a typography asset).
// 2. Change a font attribute on that shape (triggers `emit-update!` ->
// `save-font` with the current text-node attrs, typography refs included).
// 3. Switch to file B (same session, fragment navigation keeps JS state).
// 4. Create a brand-new text layer in file B.
// 5. Assert the new text uses the DEFAULT Penpot font ("Source Sans Pro"),
// not the typography font-family carried over from file A.
// ---------------------------------------------------------------------------
const FILE_A = {
id: "c7ce0794-0992-8105-8004-38f280443849",
pageId: "1062e0a0-8fe0-80ae-8007-e70b4993f5f0",
// "Text with typography asset one" carries a ref to in-file typography whose
// font-family is "IM Fell French Canon SC" (multiselection-typography.json).
};
const FILE_B = {
id: "434b0541-fa2f-802f-8006-59827d964a9b",
pageId: "434b0541-fa2f-802f-8006-59827d964a9c",
// render-wasm/get-file-text-custom-fonts.json - a mostly empty file whose
// only text uses the default font (no typography asset).
};
async function serveTwoFiles(page) {
const fileABody = await readFile(
"playwright/data/workspace/multiselection-typography.json",
"utf-8",
);
const fileBBody = await readFile(
"playwright/data/render-wasm/get-file-text-custom-fonts.json",
"utf-8",
);
// Dispatch on the `id` query param of the `get-file` RPC so each file gets
// its own fixture while keeping a single SPA session alive.
await page.route(/get\-file\?/, (route) => {
const url = new URL(route.request().url());
const fileId = url.searchParams.get("id");
const body = fileId === FILE_A.id ? fileABody : fileBBody;
return route.fulfill({
status: 200,
contentType: "application/transit+json",
body,
});
});
}
test.beforeEach(async ({ page }) => {
await WasmWorkspacePage.init(page);
// WASM_FLAGS already enables the v2 text editor / render-wasm. Add the WASM
// text editor on top so typography styles are read through the current text
// values path.
await WasmWorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-wasm"]);
});
test("BUG 10925 - typography font does not leak into new text in a different file", async ({ page }) => {
const workspace = new WasmWorkspacePage(page, { textEditor: true });
await workspace.setupEmptyFile();
await workspace.mockRPC(
"get-font-variants?team-id=*",
"render-wasm/get-font-variants-custom-fonts.json",
);
await serveTwoFiles(page);
// ---- File A: select the text linked to a typography and change a font ----
await workspace.goToWorkspace({ fileId: FILE_A.id, pageId: FILE_A.pageId });
await workspace.waitForFirstRender();
await workspace.doubleClickLeafLayer("Text with typography asset one");
await workspace.textEditor.startEditing();
// Changing a font attribute triggers save-font with the current text-node
// attrs (including the typography refs) storing them into default-font.
await workspace.textEditor.changeFontSize(24);
await workspace.textEditor.stopEditing();
// ---- File B: same SPA session, switch to a file with no typography ----
await workspace.goToWorkspace({ fileId: FILE_B.id, pageId: FILE_B.pageId });
await workspace.waitForFirstRender();
// Create a brand-new text layer in file B and query its font-family.
await workspace.createTextShape(100, 100, 300, 200, "hello");
await workspace.textEditor.stopEditing();
await workspace.clickLeafLayer("hello");
await workspace.textEditor.startEditing();
await workspace.page.keyboard.press("ControlOrMeta+a");
const fontFamily = workspace.rightSidebar.getByTitle("Font Family");
await expect(fontFamily).toContainText("Source Sans Pro");
// The custom typography family from file A (IM Fell French Canon SC) must NOT
// be carried over.
await expect(fontFamily).not.toContainText("IM Fell");
});