mirror of
https://github.com/penpot/penpot.git
synced 2025-12-11 22:14:05 +01:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
134
frontend/playwright/data/design/get-file-12384.json
Normal file
134
frontend/playwright/data/design/get-file-12384.json
Normal file
@@ -0,0 +1,134 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"fdata/path-data",
|
||||
"plugins/runtime",
|
||||
"design-tokens/v1",
|
||||
"variants/v1",
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/objects-map",
|
||||
"render-wasm/v1",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:team-id": "~u3e5ffd68-2819-8084-8006-eb1c616a5afd",
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "Bug 12384",
|
||||
"~:revn": 4,
|
||||
"~:modified-at": "~m1761124840773",
|
||||
"~:vern": 0,
|
||||
"~:id": "~ufa6ce865-34dd-80ac-8006-fe0dab5539a7",
|
||||
"~:is-shared": false,
|
||||
"~:migrations": {
|
||||
"~#ordered-set": [
|
||||
"legacy-2",
|
||||
"legacy-3",
|
||||
"legacy-5",
|
||||
"legacy-6",
|
||||
"legacy-7",
|
||||
"legacy-8",
|
||||
"legacy-9",
|
||||
"legacy-10",
|
||||
"legacy-11",
|
||||
"legacy-12",
|
||||
"legacy-13",
|
||||
"legacy-14",
|
||||
"legacy-16",
|
||||
"legacy-17",
|
||||
"legacy-18",
|
||||
"legacy-19",
|
||||
"legacy-25",
|
||||
"legacy-26",
|
||||
"legacy-27",
|
||||
"legacy-28",
|
||||
"legacy-29",
|
||||
"legacy-31",
|
||||
"legacy-32",
|
||||
"legacy-33",
|
||||
"legacy-34",
|
||||
"legacy-36",
|
||||
"legacy-37",
|
||||
"legacy-38",
|
||||
"legacy-39",
|
||||
"legacy-40",
|
||||
"legacy-41",
|
||||
"legacy-42",
|
||||
"legacy-43",
|
||||
"legacy-44",
|
||||
"legacy-45",
|
||||
"legacy-46",
|
||||
"legacy-47",
|
||||
"legacy-48",
|
||||
"legacy-49",
|
||||
"legacy-50",
|
||||
"legacy-51",
|
||||
"legacy-52",
|
||||
"legacy-53",
|
||||
"legacy-54",
|
||||
"legacy-55",
|
||||
"legacy-56",
|
||||
"legacy-57",
|
||||
"legacy-59",
|
||||
"legacy-62",
|
||||
"legacy-65",
|
||||
"legacy-66",
|
||||
"legacy-67",
|
||||
"0001-remove-tokens-from-groups",
|
||||
"0002-normalize-bool-content-v2",
|
||||
"0002-clean-shape-interactions",
|
||||
"0003-fix-root-shape",
|
||||
"0003-convert-path-content-v2",
|
||||
"0004-clean-shadow-color",
|
||||
"0005-deprecate-image-type",
|
||||
"0006-fix-old-texts-fills",
|
||||
"0008-fix-library-colors-v4",
|
||||
"0009-clean-library-colors",
|
||||
"0009-add-partial-text-touched-flags",
|
||||
"0010-fix-swap-slots-pointing-non-existent-shapes",
|
||||
"0011-fix-invalid-text-touched-flags",
|
||||
"0012-fix-position-data",
|
||||
"0013-fix-component-path",
|
||||
"0013-clear-invalid-strokes-and-fills",
|
||||
"0014-fix-tokens-lib-duplicate-ids",
|
||||
"0014-clear-components-nil-objects"
|
||||
]
|
||||
},
|
||||
"~:version": 67,
|
||||
"~:project-id": "~u3e5ffd68-2819-8084-8006-eb1c616e69bf",
|
||||
"~:created-at": "~m1761123649876",
|
||||
"~:backend": "legacy-db",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~ufa6ce865-34dd-80ac-8006-fe0dab5539a8"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~ufa6ce865-34dd-80ac-8006-fe0dab5539a8": {
|
||||
"~:objects": {
|
||||
"~#penpot/objects-map/v2": {
|
||||
"~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~u3fc80ad6-7d08-8031-8006-fe0dba3fddf7\"]]]",
|
||||
"~u3fc80ad6-7d08-8031-8006-fe0dba3fddf7": "[\"~#shape\",[\"^ \",\"~:y\",250,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Board\",\"~:width\",265,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",616,\"~:y\",250]],[\"^=\",[\"^ \",\"~:x\",881,\"~:y\",250]],[\"^=\",[\"^ \",\"~:x\",881,\"~:y\",494]],[\"^=\",[\"^ \",\"~:x\",616,\"~:y\",494]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:exports\",[[\"^ \",\"^:\",\"~:png\",\"~:suffix\",\"\",\"~:scale\",1]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u3fc80ad6-7d08-8031-8006-fe0dba3fddf7\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",616,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",616,\"~:y\",250,\"^9\",265,\"~:height\",244,\"~:x1\",616,\"~:y1\",250,\"~:x2\",881,\"~:y2\",494]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^O\",244,\"~:flip-y\",null,\"~:shapes\",[\"~u20a28a94-4ab0-801b-8006-fe0e8cee02c3\"]]]",
|
||||
"~u20a28a94-4ab0-801b-8006-fe0e8cee02c3": "[\"~#shape\",[\"^ \",\"~:y\",297.00000381469727,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",65,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",644.0000085830688,\"~:y\",297.00000381469727]],[\"^<\",[\"^ \",\"~:x\",709.0000085830688,\"~:y\",297.00000381469727]],[\"^<\",[\"^ \",\"~:x\",709.0000085830688,\"~:y\",362.00000381469727]],[\"^<\",[\"^ \",\"~:x\",644.0000085830688,\"~:y\",362.00000381469727]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:constraints-v\",\"~:top\",\"~:constraints-h\",\"~:left\",\"~:r1\",0,\"~:id\",\"~u20a28a94-4ab0-801b-8006-fe0e8cee02c3\",\"~:parent-id\",\"~u3fc80ad6-7d08-8031-8006-fe0dba3fddf7\",\"~:frame-id\",\"~u3fc80ad6-7d08-8031-8006-fe0dba3fddf7\",\"~:strokes\",[],\"~:x\",644.0000085830688,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",644.0000085830688,\"~:y\",297.00000381469727,\"^8\",65,\"~:height\",65,\"~:x1\",644.0000085830688,\"~:y1\",297.00000381469727,\"~:x2\",709.0000085830688,\"~:y2\",362.00000381469727]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^N\",65,\"~:flip-y\",null]]"
|
||||
}
|
||||
},
|
||||
"~:id": "~ufa6ce865-34dd-80ac-8006-fe0dab5539a8",
|
||||
"~:name": "Page 1"
|
||||
}
|
||||
},
|
||||
"~:id": "~ufa6ce865-34dd-80ac-8006-fe0dab5539a7",
|
||||
"~:options": {
|
||||
"~:components-v2": true,
|
||||
"~:base-font-size": "16px"
|
||||
}
|
||||
}
|
||||
}
|
||||
31
frontend/playwright/ui/specs/dashboard-teams.spec.js
Normal file
31
frontend/playwright/ui/specs/dashboard-teams.spec.js
Normal file
@@ -0,0 +1,31 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import DashboardPage from "../pages/DashboardPage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await DashboardPage.init(page);
|
||||
await DashboardPage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"logged-in-user/get-profile-logged-in-no-onboarding.json",
|
||||
);
|
||||
});
|
||||
|
||||
test("BUG 12359 - Selected invitations count is not pluralized", async ({
|
||||
page,
|
||||
}) => {
|
||||
const dashboardPage = new DashboardPage(page);
|
||||
await dashboardPage.setupDashboardFull();
|
||||
await dashboardPage.setupTeamInvitations();
|
||||
|
||||
await dashboardPage.goToSecondTeamInvitationsSection();
|
||||
|
||||
await expect(page.getByText("test1@mail.com")).toBeVisible();
|
||||
|
||||
// NOTE: we cannot use check() or getByLabel() because the checkbox
|
||||
// is hidden inside the label.
|
||||
await page.getByText("test1@mail.com").click();
|
||||
await expect(page.getByText("1 invitation selected")).toBeVisible();
|
||||
|
||||
await page.getByText("test2@mail.com").check();
|
||||
await expect(page.getByText("2 invitations selected")).toBeVisible();
|
||||
});
|
||||
@@ -320,3 +320,50 @@ test("BUG 12287 Fix identical text fills not being added/removed", async ({
|
||||
workspace.page.getByRole("button", { name: "#B1B2B5" }),
|
||||
).toHaveCount(3);
|
||||
});
|
||||
|
||||
test("BUG 12384 - Export crashing when exporting a board", async ({ page }) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "design/get-file-12384.json");
|
||||
|
||||
let hasExportRequestBeenIntercepted = false;
|
||||
await workspace.page.route("**/api/export", (route) => {
|
||||
if (hasExportRequestBeenIntercepted) {
|
||||
route.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
hasExportRequestBeenIntercepted = true;
|
||||
const payload = route.request().postData();
|
||||
const parsedPayload = JSON.parse(payload);
|
||||
|
||||
expect(parsedPayload["~:exports"]).toHaveLength(1);
|
||||
expect(parsedPayload["~:exports"][0]["~:file-id"]).toBe(
|
||||
"~ufa6ce865-34dd-80ac-8006-fe0dab5539a7",
|
||||
);
|
||||
expect(parsedPayload["~:exports"][0]["~:page-id"]).toBe(
|
||||
"~ufa6ce865-34dd-80ac-8006-fe0dab5539a8",
|
||||
);
|
||||
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/json",
|
||||
response: {},
|
||||
});
|
||||
});
|
||||
|
||||
await workspace.goToWorkspace({
|
||||
fileId: "fa6ce865-34dd-80ac-8006-fe0dab5539a7",
|
||||
pageId: "fa6ce865-34dd-80ac-8006-fe0dab5539a8",
|
||||
});
|
||||
|
||||
await workspace.clickLeafLayer("Board");
|
||||
|
||||
let exportRequest = workspace.page.waitForRequest("**/api/export");
|
||||
|
||||
await workspace.rightSidebar
|
||||
.getByRole("button", { name: "Export 1 element" })
|
||||
.click();
|
||||
|
||||
await exportRequest;
|
||||
});
|
||||
|
||||
@@ -101,6 +101,24 @@ const setupTypographyTokensFile = async (page, options = {}) => {
|
||||
});
|
||||
};
|
||||
|
||||
const checkInputFieldWithError = async (tokenThemeUpdateCreateModal, inputLocator) => {
|
||||
await expect(inputLocator).toHaveAttribute("aria-invalid", "true");
|
||||
|
||||
const errorMessageId = await inputLocator.getAttribute("aria-describedby");
|
||||
await expect(
|
||||
tokenThemeUpdateCreateModal.locator(`#${errorMessageId}`),
|
||||
).toBeVisible();
|
||||
};
|
||||
|
||||
const checkInputFieldWithoutError = async (tokenThemeUpdateCreateModal, inputLocator) => {
|
||||
expect(
|
||||
await inputLocator.getAttribute("aria-invalid")
|
||||
).toBeNull();
|
||||
expect(
|
||||
await inputLocator.getAttribute("aria-describedby")
|
||||
).toBeNull();
|
||||
};
|
||||
|
||||
test.describe("Tokens: Tokens Tab", () => {
|
||||
test("Clicking tokens tab button opens tokens sidebar tab", async ({
|
||||
page,
|
||||
@@ -812,18 +830,25 @@ test.describe("Tokens: Themes modal", () => {
|
||||
})
|
||||
.click();
|
||||
|
||||
await tokenThemeUpdateCreateModal
|
||||
.getByLabel("Group")
|
||||
.fill("New Group name");
|
||||
await tokenThemeUpdateCreateModal
|
||||
.getByLabel("Theme")
|
||||
.fill("New Theme name");
|
||||
const groupInput = tokenThemeUpdateCreateModal.getByLabel("Group");
|
||||
const nameInput = tokenThemeUpdateCreateModal.getByLabel("Theme");
|
||||
const saveButton = tokenThemeUpdateCreateModal.getByRole("button", {
|
||||
name: "Save theme",
|
||||
});
|
||||
|
||||
await groupInput.fill("Core"); // Invalid because "Core / Light" theme already exists
|
||||
await nameInput.fill("Light");
|
||||
|
||||
await tokenThemeUpdateCreateModal
|
||||
.getByRole("button", {
|
||||
name: "Save theme",
|
||||
})
|
||||
.click();
|
||||
await checkInputFieldWithError(tokenThemeUpdateCreateModal, nameInput);
|
||||
await expect(saveButton).toBeDisabled();
|
||||
|
||||
await groupInput.fill("New Group name");
|
||||
await nameInput.fill("New Theme name");
|
||||
|
||||
await checkInputFieldWithoutError(tokenThemeUpdateCreateModal, nameInput);
|
||||
await expect(saveButton).not.toBeDisabled();
|
||||
|
||||
await saveButton.click();
|
||||
|
||||
await expect(
|
||||
tokenThemeUpdateCreateModal.getByText("New Theme name"),
|
||||
@@ -851,12 +876,36 @@ test.describe("Tokens: Themes modal", () => {
|
||||
.first()
|
||||
.click();
|
||||
|
||||
await tokenThemeUpdateCreateModal
|
||||
.getByLabel("Theme")
|
||||
.fill("Changed Theme name");
|
||||
await tokenThemeUpdateCreateModal
|
||||
.getByLabel("Group")
|
||||
.fill("Changed Group name");
|
||||
const groupInput = tokenThemeUpdateCreateModal.getByLabel("Group");
|
||||
const nameInput = tokenThemeUpdateCreateModal.getByLabel("Theme");
|
||||
const saveButton = tokenThemeUpdateCreateModal.getByRole("button", {
|
||||
name: "Save theme",
|
||||
});
|
||||
|
||||
await groupInput.fill("Core"); // Invalid because "Core / Dark" theme already exists
|
||||
await nameInput.fill("Dark");
|
||||
|
||||
await checkInputFieldWithError(tokenThemeUpdateCreateModal, nameInput);
|
||||
await expect(saveButton).toBeDisabled();
|
||||
|
||||
await groupInput.fill("Core"); // Valid because "Core / Light" theme already exists
|
||||
await nameInput.fill("Light"); // but it's the same theme we are editing
|
||||
|
||||
await checkInputFieldWithoutError(tokenThemeUpdateCreateModal, nameInput);
|
||||
await expect(saveButton).not.toBeDisabled();
|
||||
|
||||
await nameInput.fill("Changed Theme name"); // New names should be also valid
|
||||
await groupInput.fill("Changed Group name");
|
||||
|
||||
await checkInputFieldWithoutError(tokenThemeUpdateCreateModal, nameInput);
|
||||
await expect(saveButton).not.toBeDisabled();
|
||||
|
||||
expect(
|
||||
await nameInput.getAttribute("aria-invalid")
|
||||
).toBeNull();
|
||||
expect(
|
||||
await nameInput.getAttribute("aria-describedby")
|
||||
).toBeNull();
|
||||
|
||||
const checkboxes = await tokenThemeUpdateCreateModal
|
||||
.locator('[role="checkbox"]')
|
||||
@@ -876,11 +925,9 @@ test.describe("Tokens: Themes modal", () => {
|
||||
|
||||
await firstButton.click();
|
||||
|
||||
await tokenThemeUpdateCreateModal
|
||||
.getByRole("button", {
|
||||
name: "Save theme",
|
||||
})
|
||||
.click();
|
||||
await expect(saveButton).not.toBeDisabled();
|
||||
|
||||
await saveButton.click();
|
||||
|
||||
await expect(
|
||||
tokenThemeUpdateCreateModal.getByText("Changed Theme name"),
|
||||
|
||||
@@ -150,8 +150,8 @@ test("User copy paste a variant container", async ({ page }) => {
|
||||
await workspacePage.clickAt(500, 500);
|
||||
await workspacePage.page.keyboard.press("Control+v");
|
||||
|
||||
const variant_original = await findVariant(workspacePage, 0);
|
||||
const variant_duplicate = await findVariant(workspacePage, 1);
|
||||
const variant_original = await findVariant(workspacePage, 1);
|
||||
const variant_duplicate = await findVariant(workspacePage, 0);
|
||||
|
||||
// Expand the layers
|
||||
await variant_duplicate.container.getByRole("button").first().click();
|
||||
|
||||
Reference in New Issue
Block a user