mirror of
https://github.com/penpot/penpot.git
synced 2026-02-12 15:42:29 -05:00
Compare commits
3 Commits
develop
...
elenatorro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d68092c9c | ||
|
|
97d24b190f | ||
|
|
434ac0556a |
@@ -1,14 +1,14 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
test("User adds a library and its automatically selected in the color palette", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
"link-file-to-library",
|
||||
@@ -53,7 +53,7 @@ test("User adds a library and its automatically selected in the color palette",
|
||||
test("BUG 10090 - Local library should be expanded by default", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
// Fix for https://tree.taiga.io/project/penpot/issue/7549
|
||||
test("Bug 7549 - User clicks on color swatch to display the color picker next to it", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
@@ -25,7 +25,7 @@ test("Bug 7549 - User clicks on color swatch to display the color picker next to
|
||||
});
|
||||
|
||||
test("Create a LINEAR gradient", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
/get\-file\?/,
|
||||
@@ -99,7 +99,7 @@ test("Create a LINEAR gradient", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("Create a RADIAL gradient", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
/get\-file\?/,
|
||||
@@ -183,7 +183,7 @@ test("Create a RADIAL gradient", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("Gradient stops limit", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.mockConfigFlags(["enable-feature-render-wasm"]);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
@@ -215,7 +215,7 @@ test("Gradient stops limit", async ({ page }) => {
|
||||
test("Bug 9900 - Color picker has no inputs for HSV values", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
@@ -232,7 +232,7 @@ test("Bug 9900 - Color picker has no inputs for HSV values", async ({
|
||||
});
|
||||
|
||||
test("Bug 10089 - Cannot change alpha", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(
|
||||
/get\-file\?/,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
const multipleConstraintsFileId = `03bff843-920f-81a1-8004-756365e1eb6a`;
|
||||
@@ -42,7 +42,7 @@ test.describe("Constraints", () => {
|
||||
test("Constraint dropdown shows 'Mixed' when multiple layers are selected with different constraints", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await setupFileWithMultipeConstraints(workspace);
|
||||
await workspace.goToWorkspace({
|
||||
fileId: multipleConstraintsFileId,
|
||||
@@ -70,7 +70,7 @@ test.describe("Shape attributes", () => {
|
||||
test("Cannot add a new fill when the limit has been reached", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.mockConfigFlags(["enable-feature-render-wasm"]);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "design/get-file-fills-limit.json");
|
||||
@@ -94,7 +94,7 @@ test.describe("Shape attributes", () => {
|
||||
test.skip("Cannot add a new text fill when the limit has been reached", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.mockConfigFlags(["enable-feature-render-wasm"]);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(
|
||||
@@ -128,7 +128,7 @@ test.describe("Multiple shapes attributes", () => {
|
||||
test("User selects multiple shapes with sames fills, strokes, shadows and blur", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await setupFileWithMultipeConstraints(workspace);
|
||||
await workspace.goToWorkspace({
|
||||
fileId: multipleConstraintsFileId,
|
||||
@@ -148,7 +148,7 @@ test.describe("Multiple shapes attributes", () => {
|
||||
test("User selects multiple shapes with different fills, strokes, shadows and blur", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await setupFileWithMultipeAttributes(workspace);
|
||||
await workspace.goToWorkspace({
|
||||
fileId: multipleAttributesFileId,
|
||||
@@ -168,7 +168,7 @@ test.describe("Multiple shapes attributes", () => {
|
||||
test("BUG 7760 - Layout losing properties when changing parents", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-7760.json");
|
||||
await workspacePage.mockRPC(
|
||||
@@ -205,7 +205,7 @@ test("BUG 7760 - Layout losing properties when changing parents", async ({
|
||||
test("BUG 9061 - Group blur visibility toggle icon not updating", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "design/get-file-9061.json");
|
||||
await workspace.mockRPC(
|
||||
@@ -234,7 +234,7 @@ test("BUG 9061 - Group blur visibility toggle icon not updating", async ({
|
||||
test("BUG 9543 - Layout padding inputs not showing 'mixed' when needed", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "design/get-file-9543.json");
|
||||
await workspace.mockRPC(
|
||||
@@ -267,7 +267,7 @@ test("BUG 9543 - Layout padding inputs not showing 'mixed' when needed", async (
|
||||
test("BUG 11177 - Font size input not showing 'mixed' when needed", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "design/get-file-11177.json");
|
||||
|
||||
@@ -288,7 +288,7 @@ test("BUG 11177 - Font size input not showing 'mixed' when needed", async ({
|
||||
test("BUG 12287 Fix identical text fills not being added/removed", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "design/get-file-12287.json");
|
||||
|
||||
@@ -323,7 +323,7 @@ test("BUG 12287 Fix identical text fills not being added/removed", async ({
|
||||
});
|
||||
|
||||
test("BUG 12384 - Export crashing when exporting a board", async ({ page }) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
await workspace.mockRPC(/get\-file\?/, "design/get-file-12384.json");
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -32,7 +32,7 @@ test.describe("Export frames to PDF", () => {
|
||||
test("Export frames menu option is NOT visible when page has no frames", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
@@ -48,7 +48,7 @@ test.describe("Export frames to PDF", () => {
|
||||
test("Export frames menu option is visible when there are frames (even if not selected)", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await setupWorkspaceWithFrames(workspacePage);
|
||||
|
||||
// Open main menu
|
||||
@@ -62,7 +62,7 @@ test.describe("Export frames to PDF", () => {
|
||||
test("Export frames modal shows all frames when none are selected", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await setupWorkspaceWithFrames(workspacePage);
|
||||
|
||||
// Don't select any frame
|
||||
@@ -88,7 +88,7 @@ test.describe("Export frames to PDF", () => {
|
||||
test("Export frames modal shows only the selected frames", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await setupWorkspaceWithFrames(workspacePage);
|
||||
|
||||
// Select Frame 1
|
||||
@@ -116,7 +116,7 @@ test.describe("Export frames to PDF", () => {
|
||||
});
|
||||
|
||||
test("User can deselect frames in the export modal", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await setupWorkspaceWithFrames(workspacePage);
|
||||
|
||||
// Select Frame 1
|
||||
@@ -149,7 +149,7 @@ test.describe("Export frames to PDF", () => {
|
||||
test("Export button is disabled when all frames are deselected", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await setupWorkspaceWithFrames(workspacePage);
|
||||
|
||||
// Select Frame 1
|
||||
@@ -173,7 +173,7 @@ test.describe("Export frames to PDF", () => {
|
||||
});
|
||||
|
||||
test("User can cancel the export modal", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await setupWorkspaceWithFrames(workspacePage);
|
||||
|
||||
// Select Frame 1
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
// Fix for https://tree.taiga.io/project/penpot/issue/9042
|
||||
test("Bug 9042 - Measurement unit dropdowns for columns are cut off in grid layout edit mode", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-9042.json");
|
||||
await workspacePage.mockRPC(
|
||||
@@ -37,7 +37,7 @@ test("[Taiga #9116] Copy CSS background color in the selected format in the INSP
|
||||
page,
|
||||
context,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.goToWorkspace();
|
||||
|
||||
@@ -87,7 +87,7 @@ test("[Taiga #10630] [INSPECT] Style assets not being displayed on info tab", as
|
||||
page,
|
||||
context,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await workspacePage.goToWorkspace();
|
||||
await workspacePage.mockRPC(
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
test("BUG 7466 - Layers tab height extends to the bottom when 'Pages' is collapsed", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
await workspace.setupEmptyFile();
|
||||
|
||||
await workspace.goToWorkspace();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import WorkspacePage from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WorkspacePage.mockConfigFlags(page, [
|
||||
await WasmWorkspacePage.init(page);
|
||||
await WasmWorkspacePage.mockConfigFlags(page, [
|
||||
"enable-subscriptions",
|
||||
"disable-onboarding",
|
||||
]);
|
||||
@@ -13,16 +13,16 @@ test.describe("Subscriptions: workspace", () => {
|
||||
test("Unlimited team should have 'Power up your plan' link in main menu", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
@@ -41,16 +41,16 @@ test.describe("Subscriptions: workspace", () => {
|
||||
test("Enterprise team should not have 'Power up your plan' link in main menu", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-enterprise-subscription.json",
|
||||
);
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
@@ -69,16 +69,16 @@ test.describe("Subscriptions: workspace", () => {
|
||||
test("Professional team should have 7 days autosaved versions", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-enterprise-subscription.json",
|
||||
);
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
@@ -105,22 +105,22 @@ test.describe("Subscriptions: workspace", () => {
|
||||
test("Unlimited team should have 30 days autosaved versions", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-unlimited-subscription.json",
|
||||
);
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-unlimited-one-team.json",
|
||||
@@ -147,22 +147,22 @@ test.describe("Subscriptions: workspace", () => {
|
||||
test("Unlimited team should have 90 days autosaved versions", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-profile",
|
||||
"subscription/get-profile-enterprise-subscription.json",
|
||||
);
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-subscription-usage",
|
||||
"subscription/get-subscription-usage.json",
|
||||
);
|
||||
|
||||
await WorkspacePage.mockRPC(
|
||||
await WasmWorkspacePage.mockRPC(
|
||||
page,
|
||||
"get-teams",
|
||||
"subscription/get-teams-enterprise-one-team.json",
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
import { presenceFixture } from "../../data/workspace/ws-notifications";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
});
|
||||
|
||||
test("Save and restore version", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
|
||||
await workspacePage.mockRPC(/get\-file\?/, "workspace/versions-init.json");
|
||||
await workspacePage.mockRPC(
|
||||
@@ -97,7 +97,7 @@ test("Save and restore version", async ({ page }) => {
|
||||
});
|
||||
|
||||
test("BUG 11006 - Fix history panel shortcut", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.mockRPC(/get\-file\?/, "workspace/versions-init.json");
|
||||
await workspacePage.mockRPC(
|
||||
"get-file-snapshots?file-id=*",
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
test("Group bubbles when zooming out if they overlap", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
await workspacePage.setupFileWithComments();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
const mainFileId = "3622460c-3408-81e2-8005-2fd0e55888b7";
|
||||
const sharedFileId = "3622460c-3408-81e2-8005-2fc938010233";
|
||||
@@ -13,12 +13,12 @@ const sharedFileFragmentId1 = "3622460c-3408-81e2-8005-31859c15ff91";
|
||||
const sharedFileFragmentId2 = "3622460c-3408-81e2-8005-31859c15ff90";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
});
|
||||
|
||||
// Fix for https://tree.taiga.io/project/penpot/issue/9042
|
||||
test("Bug 9056 - 'More info' doesn't open the update tab", async ({ page }) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
|
||||
await workspacePage.mockRPC(
|
||||
@@ -76,7 +76,7 @@ test("Bug 9056 - 'More info' doesn't open the update tab", async ({ page }) => {
|
||||
test("Bug 10113 - Empty library modal for non-empty library", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspace = new WorkspacePage(page);
|
||||
const workspace = new WasmWorkspacePage(page);
|
||||
|
||||
await workspace.setupEmptyFile(page);
|
||||
await workspace.mockRPC(/get\-file\?/, "workspace/get-file-10113.json");
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
import { presenceFixture } from "../../data/workspace/ws-notifications";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
await WasmWorkspacePage.init(page);
|
||||
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
const workspacePage = new WasmWorkspacePage(page);
|
||||
await workspacePage.setupEmptyFile(page);
|
||||
await WorkspacePage.mockRPC(page, "get-teams", "get-teams-role-viewer.json");
|
||||
await WasmWorkspacePage.mockRPC(page, "get-teams", "get-teams-role-viewer.json");
|
||||
|
||||
await workspacePage.goToWorkspace();
|
||||
});
|
||||
|
||||
@@ -8,166 +8,252 @@ use crate::{with_state_mut, STATE};
|
||||
|
||||
use super::RawShapeType;
|
||||
|
||||
/// Binary layout for batched shape base properties:
|
||||
///
|
||||
/// | Offset | Size | Field | Type |
|
||||
/// |--------|------|--------------|-----------------------------------|
|
||||
/// | 0 | 16 | id | UUID (4 × u32 LE) |
|
||||
/// | 16 | 16 | parent_id | UUID (4 × u32 LE) |
|
||||
/// | 32 | 1 | shape_type | u8 |
|
||||
/// | 33 | 1 | flags | u8 (bit0: clip, bit1: hidden) |
|
||||
/// | 34 | 1 | blend_mode | u8 |
|
||||
/// | 35 | 1 | constraint_h | u8 (0xFF = None) |
|
||||
/// | 36 | 1 | constraint_v | u8 (0xFF = None) |
|
||||
/// | 37 | 3 | padding | - |
|
||||
/// | 40 | 4 | opacity | f32 LE |
|
||||
/// | 44 | 4 | rotation | f32 LE |
|
||||
/// | 48 | 24 | transform | 6 × f32 LE (a,b,c,d,e,f) |
|
||||
/// | 72 | 16 | selrect | 4 × f32 LE (x1,y1,x2,y2) |
|
||||
/// | 88 | 16 | corners | 4 × f32 LE (r1,r2,r3,r4) |
|
||||
/// |--------|------|--------------|-----------------------------------|
|
||||
/// | Total | 104 | | |
|
||||
pub const BASE_PROPS_SIZE: usize = 104;
|
||||
|
||||
const FLAG_CLIP_CONTENT: u8 = 0b0000_0001;
|
||||
const FLAG_HIDDEN: u8 = 0b0000_0010;
|
||||
const CONSTRAINT_NONE: u8 = 0xFF;
|
||||
|
||||
/// Reads a f32 from a byte slice at the given offset (little-endian)
|
||||
#[inline]
|
||||
fn read_f32_le(bytes: &[u8], offset: usize) -> f32 {
|
||||
f32::from_le_bytes([
|
||||
bytes[offset],
|
||||
bytes[offset + 1],
|
||||
bytes[offset + 2],
|
||||
bytes[offset + 3],
|
||||
])
|
||||
const RAW_BASE_PROPS_SIZE: usize = std::mem::size_of::<RawBasePropsData>();
|
||||
|
||||
/// Binary layout for batched shape base properties.
|
||||
///
|
||||
/// The struct fields directly mirror the binary protocol — the layout
|
||||
/// documentation lives in the struct definition itself via `#[repr(C)]`.
|
||||
#[repr(C)]
|
||||
#[repr(align(4))]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct RawBasePropsData {
|
||||
// UUID id (16 bytes)
|
||||
id_a: u32,
|
||||
id_b: u32,
|
||||
id_c: u32,
|
||||
id_d: u32,
|
||||
// UUID parent_id (16 bytes)
|
||||
parent_a: u32,
|
||||
parent_b: u32,
|
||||
parent_c: u32,
|
||||
parent_d: u32,
|
||||
// Single-byte fields
|
||||
shape_type: u8,
|
||||
flags: u8,
|
||||
blend_mode: u8,
|
||||
constraint_h: u8,
|
||||
constraint_v: u8,
|
||||
padding: [u8; 3],
|
||||
// f32 fields
|
||||
opacity: f32,
|
||||
rotation: f32,
|
||||
// Transform matrix (a, b, c, d, e, f)
|
||||
transform_a: f32,
|
||||
transform_b: f32,
|
||||
transform_c: f32,
|
||||
transform_d: f32,
|
||||
transform_e: f32,
|
||||
transform_f: f32,
|
||||
// Selrect (x1, y1, x2, y2)
|
||||
selrect_x1: f32,
|
||||
selrect_y1: f32,
|
||||
selrect_x2: f32,
|
||||
selrect_y2: f32,
|
||||
// Corners (r1, r2, r3, r4)
|
||||
corner_r1: f32,
|
||||
corner_r2: f32,
|
||||
corner_r3: f32,
|
||||
corner_r4: f32,
|
||||
}
|
||||
|
||||
/// Reads a u32 from a byte slice at the given offset (little-endian)
|
||||
#[inline]
|
||||
fn read_u32_le(bytes: &[u8], offset: usize) -> u32 {
|
||||
u32::from_le_bytes([
|
||||
bytes[offset],
|
||||
bytes[offset + 1],
|
||||
bytes[offset + 2],
|
||||
bytes[offset + 3],
|
||||
])
|
||||
impl RawBasePropsData {
|
||||
fn id(&self) -> Uuid {
|
||||
uuid_from_u32_quartet(self.id_a, self.id_b, self.id_c, self.id_d)
|
||||
}
|
||||
|
||||
fn parent_id(&self) -> Uuid {
|
||||
uuid_from_u32_quartet(self.parent_a, self.parent_b, self.parent_c, self.parent_d)
|
||||
}
|
||||
|
||||
fn clip_content(&self) -> bool {
|
||||
(self.flags & FLAG_CLIP_CONTENT) != 0
|
||||
}
|
||||
|
||||
fn hidden(&self) -> bool {
|
||||
(self.flags & FLAG_HIDDEN) != 0
|
||||
}
|
||||
|
||||
fn blend_mode(&self) -> BlendMode {
|
||||
RawBlendMode::from(self.blend_mode).into()
|
||||
}
|
||||
|
||||
fn constraint_h(&self) -> Option<ConstraintH> {
|
||||
if self.constraint_h == CONSTRAINT_NONE {
|
||||
None
|
||||
} else {
|
||||
Some(RawConstraintH::from(self.constraint_h).into())
|
||||
}
|
||||
}
|
||||
|
||||
fn constraint_v(&self) -> Option<ConstraintV> {
|
||||
if self.constraint_v == CONSTRAINT_NONE {
|
||||
None
|
||||
} else {
|
||||
Some(RawConstraintV::from(self.constraint_v).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses UUID from bytes at given offset
|
||||
#[inline]
|
||||
fn read_uuid(bytes: &[u8], offset: usize) -> Uuid {
|
||||
uuid_from_u32_quartet(
|
||||
read_u32_le(bytes, offset),
|
||||
read_u32_le(bytes, offset + 4),
|
||||
read_u32_le(bytes, offset + 8),
|
||||
read_u32_le(bytes, offset + 12),
|
||||
)
|
||||
impl From<[u8; RAW_BASE_PROPS_SIZE]> for RawBasePropsData {
|
||||
fn from(bytes: [u8; RAW_BASE_PROPS_SIZE]) -> Self {
|
||||
unsafe { std::mem::transmute(bytes) }
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn set_shape_base_props() {
|
||||
let bytes = mem::bytes();
|
||||
|
||||
if bytes.len() < BASE_PROPS_SIZE {
|
||||
if bytes.len() < RAW_BASE_PROPS_SIZE {
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse all fields from the buffer
|
||||
let id = read_uuid(&bytes, 0);
|
||||
let parent_id = read_uuid(&bytes, 16);
|
||||
let shape_type = bytes[32];
|
||||
let flags = bytes[33];
|
||||
let blend_mode = bytes[34];
|
||||
let constraint_h = bytes[35];
|
||||
let constraint_v = bytes[36];
|
||||
// bytes[37..40] are padding
|
||||
let data: [u8; RAW_BASE_PROPS_SIZE] = bytes[..RAW_BASE_PROPS_SIZE].try_into().unwrap();
|
||||
let raw = RawBasePropsData::from(data);
|
||||
|
||||
let opacity = read_f32_le(&bytes, 40);
|
||||
let rotation = read_f32_le(&bytes, 44);
|
||||
|
||||
// Transform matrix (a, b, c, d, e, f)
|
||||
let transform_a = read_f32_le(&bytes, 48);
|
||||
let transform_b = read_f32_le(&bytes, 52);
|
||||
let transform_c = read_f32_le(&bytes, 56);
|
||||
let transform_d = read_f32_le(&bytes, 60);
|
||||
let transform_e = read_f32_le(&bytes, 64);
|
||||
let transform_f = read_f32_le(&bytes, 68);
|
||||
|
||||
// Selrect (x1, y1, x2, y2)
|
||||
let selrect_x1 = read_f32_le(&bytes, 72);
|
||||
let selrect_y1 = read_f32_le(&bytes, 76);
|
||||
let selrect_x2 = read_f32_le(&bytes, 80);
|
||||
let selrect_y2 = read_f32_le(&bytes, 84);
|
||||
|
||||
// Corners (r1, r2, r3, r4)
|
||||
let corner_r1 = read_f32_le(&bytes, 88);
|
||||
let corner_r2 = read_f32_le(&bytes, 92);
|
||||
let corner_r3 = read_f32_le(&bytes, 96);
|
||||
let corner_r4 = read_f32_le(&bytes, 100);
|
||||
|
||||
// Decode flags
|
||||
let clip_content = (flags & FLAG_CLIP_CONTENT) != 0;
|
||||
let hidden = (flags & FLAG_HIDDEN) != 0;
|
||||
|
||||
// Convert raw enum values
|
||||
let shape_type_enum = RawShapeType::from(shape_type);
|
||||
let blend_mode_enum: BlendMode = RawBlendMode::from(blend_mode).into();
|
||||
|
||||
let constraint_h_opt: Option<ConstraintH> = if constraint_h == CONSTRAINT_NONE {
|
||||
None
|
||||
} else {
|
||||
Some(RawConstraintH::from(constraint_h).into())
|
||||
};
|
||||
|
||||
let constraint_v_opt: Option<ConstraintV> = if constraint_v == CONSTRAINT_NONE {
|
||||
None
|
||||
} else {
|
||||
Some(RawConstraintV::from(constraint_v).into())
|
||||
};
|
||||
let id = raw.id();
|
||||
let parent_id = raw.parent_id();
|
||||
let shape_type = RawShapeType::from(raw.shape_type);
|
||||
|
||||
with_state_mut!(state, {
|
||||
// Select/create the shape
|
||||
state.use_shape(id);
|
||||
|
||||
// Set parent relationship
|
||||
state.set_parent_for_current_shape(parent_id);
|
||||
|
||||
// Mark shape as touched
|
||||
state.touch_current();
|
||||
|
||||
// Apply all properties to the current shape
|
||||
if let Some(shape) = state.current_shape_mut() {
|
||||
// Type
|
||||
shape.set_shape_type(shape_type_enum.into());
|
||||
|
||||
// Boolean flags
|
||||
shape.set_clip(clip_content);
|
||||
shape.set_hidden(hidden);
|
||||
|
||||
// Blend mode and opacity
|
||||
shape.set_blend_mode(blend_mode_enum);
|
||||
shape.set_opacity(opacity);
|
||||
|
||||
// Constraints
|
||||
shape.set_constraint_h(constraint_h_opt);
|
||||
shape.set_constraint_v(constraint_v_opt);
|
||||
|
||||
// Transform
|
||||
shape.set_rotation(rotation);
|
||||
shape.set_shape_type(shape_type.into());
|
||||
shape.set_clip(raw.clip_content());
|
||||
shape.set_hidden(raw.hidden());
|
||||
shape.set_blend_mode(raw.blend_mode());
|
||||
shape.set_opacity(raw.opacity);
|
||||
shape.set_constraint_h(raw.constraint_h());
|
||||
shape.set_constraint_v(raw.constraint_v());
|
||||
shape.set_rotation(raw.rotation);
|
||||
shape.set_transform(
|
||||
transform_a,
|
||||
transform_b,
|
||||
transform_c,
|
||||
transform_d,
|
||||
transform_e,
|
||||
transform_f,
|
||||
raw.transform_a,
|
||||
raw.transform_b,
|
||||
raw.transform_c,
|
||||
raw.transform_d,
|
||||
raw.transform_e,
|
||||
raw.transform_f,
|
||||
);
|
||||
|
||||
// Geometry
|
||||
shape.set_selrect(selrect_x1, selrect_y1, selrect_x2, selrect_y2);
|
||||
shape.set_corners((corner_r1, corner_r2, corner_r3, corner_r4));
|
||||
shape.set_selrect(
|
||||
raw.selrect_x1,
|
||||
raw.selrect_y1,
|
||||
raw.selrect_x2,
|
||||
raw.selrect_y2,
|
||||
);
|
||||
shape.set_corners((raw.corner_r1, raw.corner_r2, raw.corner_r3, raw.corner_r4));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Helper: builds a 104-byte buffer with all zeros, then lets the
|
||||
/// caller poke specific offsets before transmuting.
|
||||
fn make_bytes() -> [u8; RAW_BASE_PROPS_SIZE] {
|
||||
[0u8; RAW_BASE_PROPS_SIZE]
|
||||
}
|
||||
|
||||
fn raw_from(bytes: &[u8; RAW_BASE_PROPS_SIZE]) -> RawBasePropsData {
|
||||
RawBasePropsData::from(*bytes)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_raw_base_props_layout() {
|
||||
assert_eq!(RAW_BASE_PROPS_SIZE, 104);
|
||||
assert_eq!(std::mem::align_of::<RawBasePropsData>(), 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_field_offsets_match_binary_protocol() {
|
||||
// Verify that key struct fields sit at the documented byte offsets.
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, id_a), 0);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, parent_a), 16);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, shape_type), 32);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, flags), 33);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, blend_mode), 34);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, constraint_h), 35);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, constraint_v), 36);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, padding), 37);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, opacity), 40);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, rotation), 44);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, transform_a), 48);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, selrect_x1), 72);
|
||||
assert_eq!(std::mem::offset_of!(RawBasePropsData, corner_r1), 88);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full_deserialization() {
|
||||
let mut bytes = make_bytes();
|
||||
// id
|
||||
bytes[0..4].copy_from_slice(&1_u32.to_le_bytes());
|
||||
bytes[4..8].copy_from_slice(&2_u32.to_le_bytes());
|
||||
bytes[8..12].copy_from_slice(&3_u32.to_le_bytes());
|
||||
bytes[12..16].copy_from_slice(&4_u32.to_le_bytes());
|
||||
// parent_id
|
||||
bytes[16..20].copy_from_slice(&5_u32.to_le_bytes());
|
||||
bytes[20..24].copy_from_slice(&6_u32.to_le_bytes());
|
||||
bytes[24..28].copy_from_slice(&7_u32.to_le_bytes());
|
||||
bytes[28..32].copy_from_slice(&8_u32.to_le_bytes());
|
||||
// shape_type = Rect (3)
|
||||
bytes[32] = 3;
|
||||
// flags = clip + hidden
|
||||
bytes[33] = FLAG_CLIP_CONTENT | FLAG_HIDDEN;
|
||||
// blend_mode = Overlay (15)
|
||||
bytes[34] = 15;
|
||||
// constraint_h = Center (3)
|
||||
bytes[35] = 3;
|
||||
// constraint_v = Scale (4)
|
||||
bytes[36] = 4;
|
||||
// opacity
|
||||
bytes[40..44].copy_from_slice(&0.5_f32.to_le_bytes());
|
||||
// rotation
|
||||
bytes[44..48].copy_from_slice(&90.0_f32.to_le_bytes());
|
||||
// transform (a=2, b=0, c=0, d=2, e=50, f=60)
|
||||
bytes[48..52].copy_from_slice(&2.0_f32.to_le_bytes());
|
||||
bytes[52..56].copy_from_slice(&0.0_f32.to_le_bytes());
|
||||
bytes[56..60].copy_from_slice(&0.0_f32.to_le_bytes());
|
||||
bytes[60..64].copy_from_slice(&2.0_f32.to_le_bytes());
|
||||
bytes[64..68].copy_from_slice(&50.0_f32.to_le_bytes());
|
||||
bytes[68..72].copy_from_slice(&60.0_f32.to_le_bytes());
|
||||
// selrect
|
||||
bytes[72..76].copy_from_slice(&0.0_f32.to_le_bytes());
|
||||
bytes[76..80].copy_from_slice(&0.0_f32.to_le_bytes());
|
||||
bytes[80..84].copy_from_slice(&100.0_f32.to_le_bytes());
|
||||
bytes[84..88].copy_from_slice(&200.0_f32.to_le_bytes());
|
||||
// corners
|
||||
bytes[88..92].copy_from_slice(&4.0_f32.to_le_bytes());
|
||||
bytes[92..96].copy_from_slice(&8.0_f32.to_le_bytes());
|
||||
bytes[96..100].copy_from_slice(&12.0_f32.to_le_bytes());
|
||||
bytes[100..104].copy_from_slice(&16.0_f32.to_le_bytes());
|
||||
|
||||
let raw = raw_from(&bytes);
|
||||
|
||||
assert_eq!(raw.id(), uuid_from_u32_quartet(1, 2, 3, 4));
|
||||
assert_eq!(raw.parent_id(), uuid_from_u32_quartet(5, 6, 7, 8));
|
||||
assert_eq!(raw.shape_type, 3); // Rect
|
||||
assert!(raw.clip_content());
|
||||
assert!(raw.hidden());
|
||||
assert_eq!(raw.blend_mode(), BlendMode(skia_safe::BlendMode::Overlay));
|
||||
assert_eq!(raw.constraint_h(), Some(ConstraintH::Center));
|
||||
assert_eq!(raw.constraint_v(), Some(ConstraintV::Scale));
|
||||
assert_eq!(raw.opacity, 0.5);
|
||||
assert_eq!(raw.rotation, 90.0);
|
||||
assert_eq!(raw.transform_a, 2.0);
|
||||
assert_eq!(raw.transform_e, 50.0);
|
||||
assert_eq!(raw.transform_f, 60.0);
|
||||
assert_eq!(raw.selrect_x1, 0.0);
|
||||
assert_eq!(raw.selrect_y2, 200.0);
|
||||
assert_eq!(raw.corner_r1, 4.0);
|
||||
assert_eq!(raw.corner_r4, 16.0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user