mirror of
https://github.com/penpot/penpot.git
synced 2026-02-12 14:42:56 +00:00
Merge remote-tracking branch 'origin/staging-render' into develop
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { defineConfig, devices } from "@playwright/test";
|
import { defineConfig, devices } from "@playwright/test";
|
||||||
|
import { platform } from "os";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read environment variables from file.
|
* Read environment variables from file.
|
||||||
@@ -6,6 +7,10 @@ import { defineConfig, devices } from "@playwright/test";
|
|||||||
*/
|
*/
|
||||||
// require('dotenv').config();
|
// require('dotenv').config();
|
||||||
|
|
||||||
|
const userAgent = platform === 'darwin' ?
|
||||||
|
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" :
|
||||||
|
undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see https://playwright.dev/docs/test-configuration
|
* @see https://playwright.dev/docs/test-configuration
|
||||||
*/
|
*/
|
||||||
@@ -43,12 +48,20 @@ export default defineConfig({
|
|||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: "default",
|
name: "default",
|
||||||
use: { ...devices["Desktop Chrome"] },
|
|
||||||
testDir: "./playwright/ui/specs",
|
testDir: "./playwright/ui/specs",
|
||||||
use: {
|
use: {
|
||||||
|
...devices["Desktop Chrome"],
|
||||||
|
viewport: { width: 1920, height: 1080 }, // Add custom viewport size
|
||||||
video: 'retain-on-failure',
|
video: 'retain-on-failure',
|
||||||
trace: 'retain-on-failure',
|
trace: 'retain-on-failure',
|
||||||
}
|
userAgent,
|
||||||
|
},
|
||||||
|
snapshotPathTemplate: "{testDir}/{testFilePath}-snapshots/{arg}.png",
|
||||||
|
expect: {
|
||||||
|
toHaveScreenshot: {
|
||||||
|
maxDiffPixelRatio: 0.001,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "ds",
|
name: "ds",
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"~:file-id": "~u8d38942d-b01f-800e-8007-79ee6a9bac45",
|
||||||
|
"~:tag": "component",
|
||||||
|
"~:object-id": "8d38942d-b01f-800e-8007-79ee6a9bac45/8d38942d-b01f-800e-8007-79ee6a9bac46/6b68aedd-4c5b-80b9-8007-7b38c1d34ce4/component",
|
||||||
|
"~:media-id": "~ube2dc82e-615b-486b-a193-8768bdb51d7a",
|
||||||
|
"~:created-at": "~m1769523563389"
|
||||||
|
}
|
||||||
@@ -253,7 +253,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||||||
|
|
||||||
async #waitForWebSocketReadiness() {
|
async #waitForWebSocketReadiness() {
|
||||||
// TODO: find a better event to settle whether the app is ready to receive notifications via ws
|
// TODO: find a better event to settle whether the app is ready to receive notifications via ws
|
||||||
await expect(this.pageName).toHaveText("Page 1");
|
await expect(this.pageName).toHaveText("Page 1", { timeout: 30000 })
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendPresenceMessage(fixture) {
|
async sendPresenceMessage(fixture) {
|
||||||
@@ -383,19 +383,46 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||||||
await this.page.keyboard.press("T");
|
await this.page.keyboard.press("T");
|
||||||
await this.page.waitForTimeout(timeToWait);
|
await this.page.waitForTimeout(timeToWait);
|
||||||
await this.clickAndMove(x1, y1, x2, y2);
|
await this.clickAndMove(x1, y1, x2, y2);
|
||||||
await this.page.waitForTimeout(timeToWait);
|
await expect(this.page.getByTestId("text-editor")).toBeVisible();
|
||||||
|
|
||||||
if (initialText) {
|
if (initialText) {
|
||||||
await this.page.keyboard.type(initialText);
|
await this.page.keyboard.type(initialText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copies the selected element into the clipboard.
|
* Copies the selected element into the clipboard, or copy the
|
||||||
|
* content of the locator into the clipboard.
|
||||||
*
|
*
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async copy() {
|
async copy(kind = "keyboard", locator = undefined) {
|
||||||
return this.page.keyboard.press("Control+C");
|
if (kind === "context-menu" && locator) {
|
||||||
|
await locator.click({ button: "right" });
|
||||||
|
await this.page.getByText("Copy", { exact: true }).click();
|
||||||
|
} else {
|
||||||
|
await this.page.keyboard.press("ControlOrMeta+C");
|
||||||
|
}
|
||||||
|
// wait for the clipboard to be updated
|
||||||
|
await this.page.waitForFunction(async () => {
|
||||||
|
const content = await navigator.clipboard.readText()
|
||||||
|
return content !== "";
|
||||||
|
}, { timeout: 1000 });
|
||||||
|
}
|
||||||
|
|
||||||
|
async cut(kind = "keyboard", locator = undefined) {
|
||||||
|
if (kind === "context-menu" && locator) {
|
||||||
|
await locator.click({ button: "right" });
|
||||||
|
await this.page.getByText("Cut", { exact: true }).click();
|
||||||
|
} else {
|
||||||
|
await this.page.keyboard.press("ControlOrMeta+X");
|
||||||
|
}
|
||||||
|
// wait for the clipboard to be updated
|
||||||
|
await this.page.waitForFunction(async () => {
|
||||||
|
const content = await navigator.clipboard.readText()
|
||||||
|
return content !== "";
|
||||||
|
}, { timeout: 1000 });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -407,9 +434,9 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||||||
async paste(kind = "keyboard") {
|
async paste(kind = "keyboard") {
|
||||||
if (kind === "context-menu") {
|
if (kind === "context-menu") {
|
||||||
await this.viewport.click({ button: "right" });
|
await this.viewport.click({ button: "right" });
|
||||||
return this.page.getByText("PasteCtrlV").click();
|
return this.page.getByText("Paste", { exact: true }).click();
|
||||||
}
|
}
|
||||||
return this.page.keyboard.press("Control+V");
|
return this.page.keyboard.press("ControlOrMeta+V");
|
||||||
}
|
}
|
||||||
|
|
||||||
async panOnViewportAt(x, y, width, height) {
|
async panOnViewportAt(x, y, width, height) {
|
||||||
@@ -448,11 +475,11 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||||||
const layer = this.layers
|
const layer = this.layers
|
||||||
.getByTestId("layer-row")
|
.getByTestId("layer-row")
|
||||||
.filter({ hasText: name });
|
.filter({ hasText: name });
|
||||||
const button = layer.getByRole("button");
|
const button = layer.getByTestId("toggle-content");
|
||||||
|
|
||||||
await button.waitFor();
|
await expect(button).toBeVisible();
|
||||||
await button.click(clickOptions);
|
await button.click(clickOptions);
|
||||||
await this.page.waitForTimeout(500);
|
await button.waitFor({ ariaExpanded: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
async expectSelectedLayer(name) {
|
async expectSelectedLayer(name) {
|
||||||
@@ -495,13 +522,7 @@ export class WorkspacePage extends BaseWebSocketPage {
|
|||||||
|
|
||||||
async clickColorPalette(clickOptions = {}) {
|
async clickColorPalette(clickOptions = {}) {
|
||||||
await this.palette
|
await this.palette
|
||||||
.getByRole("button", { name: "Color Palette (Alt+P)" })
|
.getByRole("button", { name: /Color Palette/ })
|
||||||
.click(clickOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
async clickColorPalette(clickOptions = {}) {
|
|
||||||
await this.palette
|
|
||||||
.getByRole("button", { name: "Color Palette (Alt+P)" })
|
|
||||||
.click(clickOptions);
|
.click(clickOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ test("User can complete the onboarding", async ({ page }) => {
|
|||||||
const dashboardPage = new DashboardPage(page);
|
const dashboardPage = new DashboardPage(page);
|
||||||
const onboardingPage = new OnboardingPage(page);
|
const onboardingPage = new OnboardingPage(page);
|
||||||
|
|
||||||
|
await dashboardPage.mockConfigFlags(["enable-onboarding"]);
|
||||||
|
|
||||||
await dashboardPage.goToDashboard();
|
await dashboardPage.goToDashboard();
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole("heading", { name: "Help us get to know you" }),
|
page.getByRole("heading", { name: "Help us get to know you" }),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { test, expect } from "@playwright/test";
|
import { test, expect } from "@playwright/test";
|
||||||
import { WasmWorkspacePage, WASM_FLAGS } from "../pages/WasmWorkspacePage";
|
import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await WasmWorkspacePage.init(page);
|
await WasmWorkspacePage.init(page);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { WorkspacePage } from "../pages/WorkspacePage";
|
|||||||
const timeToWait = 100;
|
const timeToWait = 100;
|
||||||
|
|
||||||
test.beforeEach(async ({ page, context }) => {
|
test.beforeEach(async ({ page, context }) => {
|
||||||
await Clipboard.enable(context, Clipboard.Permission.ONLY_WRITE);
|
await Clipboard.enable(context, Clipboard.Permission.ALL);
|
||||||
|
|
||||||
await WorkspacePage.init(page);
|
await WorkspacePage.init(page);
|
||||||
await WorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-v2"]);
|
await WorkspacePage.mockConfigFlags(page, ["enable-feature-text-editor-v2"]);
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import { test, expect } from "@playwright/test";
|
import { test, expect } from "@playwright/test";
|
||||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||||
import { BaseWebSocketPage } from "../pages/BaseWebSocketPage";
|
import { BaseWebSocketPage } from "../pages/BaseWebSocketPage";
|
||||||
|
import { Clipboard } from "../../helpers/Clipboard";
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page, context }) => {
|
||||||
|
await Clipboard.enable(context, Clipboard.Permission.ALL);
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
await WorkspacePage.init(page);
|
await WorkspacePage.init(page);
|
||||||
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-variants.json");
|
await BaseWebSocketPage.mockRPC(page, "get-teams", "get-teams-variants.json");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test.afterEach(async ({ context }) => {
|
||||||
|
context.clearPermissions();
|
||||||
|
});
|
||||||
|
|
||||||
const setupVariantsFile = async (workspacePage) => {
|
const setupVariantsFile = async (workspacePage) => {
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.mockRPC(
|
await workspacePage.mockRPC(
|
||||||
@@ -34,9 +41,9 @@ const setupVariantsFileWithVariant = async (workspacePage) => {
|
|||||||
await setupVariantsFile(workspacePage);
|
await setupVariantsFile(workspacePage);
|
||||||
|
|
||||||
await workspacePage.clickLeafLayer("Rectangle");
|
await workspacePage.clickLeafLayer("Rectangle");
|
||||||
await workspacePage.page.keyboard.press("Control+k");
|
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||||
await workspacePage.page.waitForTimeout(500);
|
await workspacePage.page.waitForTimeout(500);
|
||||||
await workspacePage.page.keyboard.press("Control+k");
|
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||||
await workspacePage.page.waitForTimeout(500);
|
await workspacePage.page.waitForTimeout(500);
|
||||||
|
|
||||||
// We wait until layer-row starts looking like it an component
|
// We wait until layer-row starts looking like it an component
|
||||||
@@ -156,7 +163,7 @@ test("User duplicates a variant container", async ({ page }) => {
|
|||||||
await variant.container.click();
|
await variant.container.click();
|
||||||
|
|
||||||
//Duplicate the variant container
|
//Duplicate the variant container
|
||||||
await workspacePage.page.keyboard.press("Control+d");
|
await workspacePage.page.keyboard.press("ControlOrMeta+d");
|
||||||
|
|
||||||
const variant_original = await findVariant(workspacePage, 1); // On duplicate, the new item is the first
|
const variant_original = await findVariant(workspacePage, 1); // On duplicate, the new item is the first
|
||||||
const variant_duplicate = await findVariant(workspacePage, 0);
|
const variant_duplicate = await findVariant(workspacePage, 0);
|
||||||
@@ -169,25 +176,27 @@ test("User duplicates a variant container", async ({ page }) => {
|
|||||||
await validateVariant(variant_duplicate);
|
await validateVariant(variant_duplicate);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("User copy paste a variant container", async ({ page }) => {
|
test("User copy paste a variant container", async ({ page, context }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WorkspacePage(page);
|
||||||
|
// Access to the read/write clipboard necesary for this functionality
|
||||||
await setupVariantsFileWithVariant(workspacePage);
|
await setupVariantsFileWithVariant(workspacePage);
|
||||||
|
await workspacePage.mockRPC(
|
||||||
|
/create-file-object-thumbnail.*/,
|
||||||
|
"workspace/create-file-object-thumbnail.json",
|
||||||
|
);
|
||||||
|
|
||||||
const variant = findVariantNoWait(workspacePage, 0);
|
const variant = findVariantNoWait(workspacePage, 0);
|
||||||
|
|
||||||
// await variant.container.waitFor();
|
|
||||||
|
|
||||||
// Select the variant container
|
|
||||||
await variant.container.click();
|
|
||||||
|
|
||||||
await workspacePage.page.waitForTimeout(1000);
|
|
||||||
|
|
||||||
// Copy the variant container
|
// Copy the variant container
|
||||||
await workspacePage.page.keyboard.press("Control+c");
|
await workspacePage.clickLeafLayer("Rectangle");
|
||||||
|
await workspacePage.copy("keyboard");
|
||||||
|
|
||||||
// Paste the variant container
|
// Paste the variant container
|
||||||
await workspacePage.clickAt(400, 400);
|
await workspacePage.clickAt(400, 400);
|
||||||
await workspacePage.page.keyboard.press("Control+v");
|
await workspacePage.paste("keyboard");
|
||||||
|
|
||||||
|
const variants = workspacePage.layers.getByText("Rectangle");
|
||||||
|
await expect(variants).toHaveCount(2);
|
||||||
|
|
||||||
const variantDuplicate = findVariantNoWait(workspacePage, 0);
|
const variantDuplicate = findVariantNoWait(workspacePage, 0);
|
||||||
const variantOriginal = findVariantNoWait(workspacePage, 1);
|
const variantOriginal = findVariantNoWait(workspacePage, 1);
|
||||||
@@ -212,18 +221,17 @@ test("User cut paste a variant container", async ({ page }) => {
|
|||||||
await variant.container.click();
|
await variant.container.click();
|
||||||
|
|
||||||
//Cut the variant container
|
//Cut the variant container
|
||||||
await workspacePage.page.keyboard.press("Control+x");
|
await workspacePage.cut("keyboard");
|
||||||
await workspacePage.page.waitForTimeout(500);
|
|
||||||
|
|
||||||
//Paste the variant container
|
//Paste the variant container
|
||||||
await workspacePage.clickAt(500, 500);
|
await workspacePage.clickAt(500, 500);
|
||||||
await workspacePage.page.keyboard.press("Control+v");
|
await workspacePage.paste("keyboard");
|
||||||
await workspacePage.page.waitForTimeout(500);
|
await workspacePage.page.waitForTimeout(500);
|
||||||
|
|
||||||
const variantPasted = await findVariant(workspacePage, 0);
|
const variantPasted = await findVariant(workspacePage, 0);
|
||||||
|
|
||||||
// Expand the layers
|
// Expand the layers
|
||||||
await variantPasted.container.locator("button").first().click();
|
await workspacePage.clickToggableLayer("Rectangle");
|
||||||
|
|
||||||
// The variants are valid
|
// The variants are valid
|
||||||
await validateVariant(variantPasted);
|
await validateVariant(variantPasted);
|
||||||
@@ -239,27 +247,34 @@ test("User cut paste a variant container into a board, and undo twice", async ({
|
|||||||
|
|
||||||
//Create a board
|
//Create a board
|
||||||
await workspacePage.boardButton.click();
|
await workspacePage.boardButton.click();
|
||||||
await workspacePage.clickWithDragViewportAt(500, 500, 100, 100);
|
// NOTE: this board should not intersect the existing variants, otherwise
|
||||||
|
// this test is flaky
|
||||||
|
await workspacePage.clickWithDragViewportAt(200, 200, 100, 100);
|
||||||
await workspacePage.clickAt(495, 495);
|
await workspacePage.clickAt(495, 495);
|
||||||
const board = await workspacePage.rootShape.locator("Board");
|
const board = await workspacePage.rootShape.locator("Board");
|
||||||
|
|
||||||
// Select the variant container
|
// Select the variant container
|
||||||
await variant.container.click();
|
// await variant.container.click();
|
||||||
|
await workspacePage.clickLeafLayer("Rectangle");
|
||||||
|
|
||||||
//Cut the variant container
|
//Cut the variant container
|
||||||
await workspacePage.page.keyboard.press("Control+x");
|
await workspacePage.cut("keyboard");
|
||||||
await workspacePage.page.waitForTimeout(500);
|
await expect(variant.container).not.toBeVisible();
|
||||||
|
|
||||||
//Select the board
|
//Select the board
|
||||||
await workspacePage.clickLeafLayer("Board");
|
await workspacePage.clickLeafLayer("Board");
|
||||||
|
|
||||||
//Paste the variant container inside the board
|
//Paste the variant container inside the board
|
||||||
await workspacePage.page.keyboard.press("Control+v");
|
await workspacePage.paste("keyboard");
|
||||||
|
await expect(variant.container).toBeVisible();
|
||||||
|
|
||||||
//Undo twice
|
//Undo twice
|
||||||
await workspacePage.page.keyboard.press("Control+z");
|
await workspacePage.page.keyboard.press("ControlOrMeta+z");
|
||||||
await workspacePage.page.keyboard.press("Control+z");
|
|
||||||
await workspacePage.page.waitForTimeout(500);
|
await expect(variant.container).not.toBeVisible();
|
||||||
|
|
||||||
|
await workspacePage.page.keyboard.press("ControlOrMeta+z");
|
||||||
|
await expect(variant.container).toBeVisible();
|
||||||
|
|
||||||
const variantAfterUndo = await findVariant(workspacePage, 0);
|
const variantAfterUndo = await findVariant(workspacePage, 0);
|
||||||
|
|
||||||
@@ -276,12 +291,12 @@ test("User copy paste a variant", async ({ page }) => {
|
|||||||
// Select the variant1
|
// Select the variant1
|
||||||
await variant.variant1.click();
|
await variant.variant1.click();
|
||||||
|
|
||||||
//Cut the variant
|
// Copy the variant
|
||||||
await workspacePage.page.keyboard.press("Control+c");
|
await workspacePage.copy("keyboard");
|
||||||
|
|
||||||
//Paste the variant
|
// Paste the variant
|
||||||
await workspacePage.clickAt(500, 500);
|
await workspacePage.clickAt(500, 500);
|
||||||
await workspacePage.page.keyboard.press("Control+v");
|
await workspacePage.paste("keyboard");
|
||||||
|
|
||||||
const copy = await workspacePage.layers
|
const copy = await workspacePage.layers
|
||||||
.getByTestId("layer-row")
|
.getByTestId("layer-row")
|
||||||
@@ -302,11 +317,11 @@ test("User cut paste a variant outside the container", async ({ page }) => {
|
|||||||
await variant.variant1.click();
|
await variant.variant1.click();
|
||||||
|
|
||||||
//Cut the variant
|
//Cut the variant
|
||||||
await workspacePage.page.keyboard.press("Control+x");
|
await workspacePage.cut("keyboard");
|
||||||
|
|
||||||
//Paste the variant
|
//Paste the variant
|
||||||
await workspacePage.clickAt(500, 500);
|
await workspacePage.clickAt(500, 500);
|
||||||
await workspacePage.page.keyboard.press("Control+v");
|
await workspacePage.paste("keyboard");
|
||||||
|
|
||||||
const component = await workspacePage.layers
|
const component = await workspacePage.layers
|
||||||
.getByTestId("layer-row")
|
.getByTestId("layer-row")
|
||||||
@@ -324,15 +339,11 @@ test("User drag and drop a variant outside the container", async ({ page }) => {
|
|||||||
const variant = await findVariant(workspacePage, 0);
|
const variant = await findVariant(workspacePage, 0);
|
||||||
|
|
||||||
// Drag and drop the variant
|
// Drag and drop the variant
|
||||||
await workspacePage.clickWithDragViewportAt(350, 400, 0, 200);
|
// FIXME: to make this test more resilient, we should get the bounding box of the Value 1 variant
|
||||||
|
// and use it to calculate the target position
|
||||||
|
await workspacePage.clickWithDragViewportAt(600, 500, 0, 300);
|
||||||
|
|
||||||
const component = await workspacePage.layers
|
await expect(workspacePage.layers.getByText("Rectangle / Value 1")).toBeVisible();
|
||||||
.getByTestId("layer-row")
|
|
||||||
.filter({ has: workspacePage.page.getByText("Rectangle / Value 1") })
|
|
||||||
.filter({ has: workspacePage.page.getByTestId("icon-component") });
|
|
||||||
|
|
||||||
//The component exists and is visible
|
|
||||||
await expect(component).toBeVisible();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("User cut paste a component inside a variant", async ({ page }) => {
|
test("User cut paste a component inside a variant", async ({ page }) => {
|
||||||
@@ -345,14 +356,14 @@ test("User cut paste a component inside a variant", async ({ page }) => {
|
|||||||
await workspacePage.ellipseShapeButton.click();
|
await workspacePage.ellipseShapeButton.click();
|
||||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||||
await workspacePage.clickLeafLayer("Ellipse");
|
await workspacePage.clickLeafLayer("Ellipse");
|
||||||
await workspacePage.page.keyboard.press("Control+k");
|
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||||
|
|
||||||
//Cut the component
|
//Cut the component
|
||||||
await workspacePage.page.keyboard.press("Control+x");
|
await workspacePage.cut("keyboard");
|
||||||
|
|
||||||
//Paste the component inside the variant
|
//Paste the component inside the variant
|
||||||
await variant.container.click();
|
await variant.container.click();
|
||||||
await workspacePage.page.keyboard.press("Control+v");
|
await workspacePage.paste("keyboard");
|
||||||
|
|
||||||
const variant3 = await workspacePage.layers
|
const variant3 = await workspacePage.layers
|
||||||
.getByTestId("layer-row")
|
.getByTestId("layer-row")
|
||||||
@@ -376,7 +387,7 @@ test("User cut paste a component with path inside a variant", async ({
|
|||||||
await workspacePage.ellipseShapeButton.click();
|
await workspacePage.ellipseShapeButton.click();
|
||||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||||
await workspacePage.clickLeafLayer("Ellipse");
|
await workspacePage.clickLeafLayer("Ellipse");
|
||||||
await workspacePage.page.keyboard.press("Control+k");
|
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||||
|
|
||||||
//Rename the component
|
//Rename the component
|
||||||
await workspacePage.layers.getByText("Ellipse").dblclick();
|
await workspacePage.layers.getByText("Ellipse").dblclick();
|
||||||
@@ -387,11 +398,11 @@ test("User cut paste a component with path inside a variant", async ({
|
|||||||
await workspacePage.page.keyboard.press("Enter");
|
await workspacePage.page.keyboard.press("Enter");
|
||||||
|
|
||||||
//Cut the component
|
//Cut the component
|
||||||
await workspacePage.page.keyboard.press("Control+x");
|
await workspacePage.cut("keyboard");
|
||||||
|
|
||||||
//Paste the component inside the variant
|
//Paste the component inside the variant
|
||||||
await variant.container.click();
|
await variant.container.click();
|
||||||
await workspacePage.page.keyboard.press("Control+v");
|
await workspacePage.paste("keyboard");
|
||||||
|
|
||||||
const variant3 = await workspacePage.layers
|
const variant3 = await workspacePage.layers
|
||||||
.getByTestId("layer-row")
|
.getByTestId("layer-row")
|
||||||
@@ -415,7 +426,7 @@ test("User drag and drop a component with path inside a variant", async ({
|
|||||||
await workspacePage.ellipseShapeButton.click();
|
await workspacePage.ellipseShapeButton.click();
|
||||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||||
await workspacePage.clickLeafLayer("Ellipse");
|
await workspacePage.clickLeafLayer("Ellipse");
|
||||||
await workspacePage.page.keyboard.press("Control+k");
|
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||||
|
|
||||||
//Rename the component
|
//Rename the component
|
||||||
await workspacePage.layers.getByText("Ellipse").dblclick();
|
await workspacePage.layers.getByText("Ellipse").dblclick();
|
||||||
@@ -426,7 +437,7 @@ test("User drag and drop a component with path inside a variant", async ({
|
|||||||
await workspacePage.page.keyboard.press("Enter");
|
await workspacePage.page.keyboard.press("Enter");
|
||||||
|
|
||||||
//Drag and drop the component the component
|
//Drag and drop the component the component
|
||||||
await workspacePage.clickWithDragViewportAt(510, 510, 0, -200);
|
await workspacePage.clickWithDragViewportAt(510, 510, 200, 0);
|
||||||
|
|
||||||
const variant3 = await workspacePage.layers
|
const variant3 = await workspacePage.layers
|
||||||
.getByTestId("layer-row")
|
.getByTestId("layer-row")
|
||||||
@@ -446,8 +457,8 @@ test("User cut paste a variant into another container", async ({ page }) => {
|
|||||||
await workspacePage.ellipseShapeButton.click();
|
await workspacePage.ellipseShapeButton.click();
|
||||||
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
await workspacePage.clickWithDragViewportAt(500, 500, 20, 20);
|
||||||
await workspacePage.clickLeafLayer("Ellipse");
|
await workspacePage.clickLeafLayer("Ellipse");
|
||||||
await workspacePage.page.keyboard.press("Control+k");
|
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||||
await workspacePage.page.keyboard.press("Control+k");
|
await workspacePage.page.keyboard.press("ControlOrMeta+k");
|
||||||
|
|
||||||
const variantOrigin = await findVariantNoWait(workspacePage, 1);
|
const variantOrigin = await findVariantNoWait(workspacePage, 1);
|
||||||
|
|
||||||
@@ -457,11 +468,11 @@ test("User cut paste a variant into another container", async ({ page }) => {
|
|||||||
await variantOrigin.variant1.click();
|
await variantOrigin.variant1.click();
|
||||||
|
|
||||||
//Cut the variant
|
//Cut the variant
|
||||||
await workspacePage.page.keyboard.press("Control+x");
|
await workspacePage.cut("keyboard");
|
||||||
|
|
||||||
//Paste the variant
|
//Paste the variant
|
||||||
await workspacePage.layers.getByText("Ellipse").first().click();
|
await workspacePage.layers.getByText("Ellipse").first().click();
|
||||||
await workspacePage.page.keyboard.press("Control+v");
|
await workspacePage.paste("keyboard");
|
||||||
|
|
||||||
const variant3 = workspacePage.layers
|
const variant3 = workspacePage.layers
|
||||||
.getByTestId("layer-row")
|
.getByTestId("layer-row")
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { test, expect } from "@playwright/test";
|
import { test, expect } from "@playwright/test";
|
||||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
import { WasmWorkspacePage } from "../pages/WorkspacePage";
|
||||||
import { presenceFixture, joinFixture2, joinFixture3 } from "../../data/workspace/ws-notifications";
|
import { presenceFixture, joinFixture2, joinFixture3 } from "../../data/workspace/ws-notifications";
|
||||||
|
|
||||||
test.beforeEach(async ({ page }) => {
|
test.beforeEach(async ({ page }) => {
|
||||||
await WorkspacePage.init(page);
|
await WasmWorkspacePage.init(page);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("User loads worskpace with empty file", async ({ page }) => {
|
test("User loads worskpace with empty file", async ({ page }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
|
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
@@ -16,7 +16,7 @@ test("User loads worskpace with empty file", async ({ page }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("User opens a file with a bad page id", async ({ page }) => {
|
test("User opens a file with a bad page id", async ({ page }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
|
|
||||||
await workspacePage.goToWorkspace({
|
await workspacePage.goToWorkspace({
|
||||||
@@ -29,7 +29,7 @@ test("User opens a file with a bad page id", async ({ page }) => {
|
|||||||
test("User receives presence notifications updates in the workspace", async ({
|
test("User receives presence notifications updates in the workspace", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
|
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
@@ -63,7 +63,7 @@ test("BUG 13058 - Presence list shows up to 3 user avatars", async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("User draws a rect", async ({ page }) => {
|
test("User draws a rect", async ({ page }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.mockRPC(
|
await workspacePage.mockRPC(
|
||||||
"update-file?id=*",
|
"update-file?id=*",
|
||||||
@@ -74,13 +74,12 @@ test("User draws a rect", async ({ page }) => {
|
|||||||
await workspacePage.rectShapeButton.click();
|
await workspacePage.rectShapeButton.click();
|
||||||
await workspacePage.clickWithDragViewportAt(128, 128, 200, 100);
|
await workspacePage.clickWithDragViewportAt(128, 128, 200, 100);
|
||||||
|
|
||||||
const shape = await workspacePage.rootShape.locator("rect");
|
await workspacePage.hideUI();
|
||||||
await expect(shape).toHaveAttribute("width", "200");
|
await expect(workspacePage.canvas).toHaveScreenshot();
|
||||||
await expect(shape).toHaveAttribute("height", "100");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("User makes a group", async ({ page }) => {
|
test("User makes a group", async ({ page }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.mockRPC(
|
await workspacePage.mockRPC(
|
||||||
/get\-file\?/,
|
/get\-file\?/,
|
||||||
@@ -96,14 +95,14 @@ test("User makes a group", async ({ page }) => {
|
|||||||
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
|
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
|
||||||
});
|
});
|
||||||
await workspacePage.clickLeafLayer("Rectangle");
|
await workspacePage.clickLeafLayer("Rectangle");
|
||||||
await workspacePage.page.keyboard.press("Control+g");
|
await workspacePage.page.keyboard.press("ControlOrMeta+g");
|
||||||
await workspacePage.expectSelectedLayer("Group");
|
await workspacePage.expectSelectedLayer("Group");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Bug 7654 - Toolbar keeps toggling on and off on spacebar press", async ({
|
test("Bug 7654 - Toolbar keeps toggling on and off on spacebar press", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
|
|
||||||
@@ -113,10 +112,10 @@ test("Bug 7654 - Toolbar keeps toggling on and off on spacebar press", async ({
|
|||||||
await workspacePage.expectHiddenToolbarOptions();
|
await workspacePage.expectHiddenToolbarOptions();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Bug 7525 - User moves a scrollbar and no selciont rectangle appears", async ({
|
test("Bug 7525 - User moves a scrollbar and no selection rectangle appears", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.mockRPC(
|
await workspacePage.mockRPC(
|
||||||
/get\-file\?/,
|
/get\-file\?/,
|
||||||
@@ -132,8 +131,8 @@ test("Bug 7525 - User moves a scrollbar and no selciont rectangle appears", asyn
|
|||||||
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
|
pageId: "6191cd35-bb1f-81f7-8004-7cc63d087375",
|
||||||
});
|
});
|
||||||
|
|
||||||
// Move created rect to a corner, in orther to get scrollbars
|
// Move created rect to a corner, in order to get scrollbars
|
||||||
await workspacePage.panOnViewportAt(128, 128, 300, 300);
|
await workspacePage.panOnViewportAt(128, 128, 600, 600);
|
||||||
|
|
||||||
// Check scrollbars appear
|
// Check scrollbars appear
|
||||||
const horizontalScrollbar = workspacePage.horizontalScrollbar;
|
const horizontalScrollbar = workspacePage.horizontalScrollbar;
|
||||||
@@ -152,7 +151,7 @@ test("Bug 7525 - User moves a scrollbar and no selciont rectangle appears", asyn
|
|||||||
test("User adds a library and its automatically selected in the color palette", async ({
|
test("User adds a library and its automatically selected in the color palette", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.mockRPC(
|
await workspacePage.mockRPC(
|
||||||
"link-file-to-library",
|
"link-file-to-library",
|
||||||
@@ -197,7 +196,7 @@ test("User adds a library and its automatically selected in the color palette",
|
|||||||
test("Bug 10179 - Drag & drop doesn't add colors to the Recent Colors palette", async ({
|
test("Bug 10179 - Drag & drop doesn't add colors to the Recent Colors palette", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
await workspacePage.moveButton.click();
|
await workspacePage.moveButton.click();
|
||||||
@@ -240,7 +239,7 @@ test("Bug 10179 - Drag & drop doesn't add colors to the Recent Colors palette",
|
|||||||
test("Bug 7489 - Workspace-palette items stay hidden when opening with keyboard-shortcut", async ({
|
test("Bug 7489 - Workspace-palette items stay hidden when opening with keyboard-shortcut", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
|
|
||||||
@@ -257,7 +256,7 @@ test("Bug 7489 - Workspace-palette items stay hidden when opening with keyboard-
|
|||||||
test("Bug 8784 - Use keyboard arrow to move inside a text input does not change tabs", async ({
|
test("Bug 8784 - Use keyboard arrow to move inside a text input does not change tabs", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile();
|
await workspacePage.setupEmptyFile();
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
await workspacePage.pageName.click();
|
await workspacePage.pageName.click();
|
||||||
@@ -267,7 +266,7 @@ test("Bug 8784 - Use keyboard arrow to move inside a text input does not change
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Bug 9066 - Problem with grid layout", async ({ page }) => {
|
test("Bug 9066 - Problem with grid layout", async ({ page }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-9066.json");
|
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-9066.json");
|
||||||
|
|
||||||
@@ -295,7 +294,7 @@ test("Bug 9066 - Problem with grid layout", async ({ page }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("User have toolbar", async ({ page }) => {
|
test("User have toolbar", async ({ page }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
|
|
||||||
@@ -304,7 +303,7 @@ test("User have toolbar", async ({ page }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("User have edition menu entries", async ({ page }) => {
|
test("User have edition menu entries", async ({ page }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
|
|
||||||
@@ -320,7 +319,7 @@ test("User have edition menu entries", async ({ page }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("Copy/paste properties", async ({ page, context }) => {
|
test("Copy/paste properties", async ({ page, context }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
await workspacePage.mockRPC(
|
await workspacePage.mockRPC(
|
||||||
/get\-file\?/,
|
/get\-file\?/,
|
||||||
@@ -386,23 +385,23 @@ test("Copy/paste properties", async ({ page, context }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("[Taiga #9929] Paste text in workspace", async ({ page, context }) => {
|
test("[Taiga #9929] Paste text in workspace", async ({ page, context }) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
|
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
|
||||||
await page.evaluate(() => navigator.clipboard.writeText("Lorem ipsum dolor"));
|
await page.evaluate(() => navigator.clipboard.writeText("Lorem ipsum dolor"));
|
||||||
await workspacePage.viewport.click({ button: "right" });
|
await workspacePage.viewport.click({ button: "right" });
|
||||||
await page.getByText("PasteCtrlV").click();
|
await page.getByText(/^Paste/i).click();
|
||||||
await workspacePage.viewport
|
await workspacePage.viewport
|
||||||
.getByRole("textbox")
|
.getByRole("textbox")
|
||||||
.getByText("Lorem ipsum dolor");
|
.getByText("Lorem ipsum dolor");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("[Taiga #9930] Zoom fit all doesn't fits all", async ({
|
test("[Taiga #9930] Zoom fit all doesn't fit all shapes", async ({
|
||||||
page,
|
page,
|
||||||
context,
|
context,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-9930.json");
|
await workspacePage.mockRPC(/get\-file\?/, "workspace/get-file-9930.json");
|
||||||
await workspacePage.goToWorkspace({
|
await workspacePage.goToWorkspace({
|
||||||
@@ -410,16 +409,18 @@ test("[Taiga #9930] Zoom fit all doesn't fits all", async ({
|
|||||||
pageId: "fb9798e7-a547-80ae-8005-9ffda4a13e2c",
|
pageId: "fb9798e7-a547-80ae-8005-9ffda4a13e2c",
|
||||||
});
|
});
|
||||||
|
|
||||||
const zoom = await page.getByTitle("Zoom");
|
const zoom = page.getByTitle("Zoom");
|
||||||
await zoom.click();
|
await zoom.click();
|
||||||
|
|
||||||
const zoomIn = await page.getByRole("button", { name: "Zoom in" });
|
const zoomIn = page.getByRole("button", { name: "Zoom in" });
|
||||||
await zoomIn.click();
|
await zoomIn.click();
|
||||||
await zoomIn.click();
|
await zoomIn.click();
|
||||||
await zoomIn.click();
|
await zoomIn.click();
|
||||||
|
|
||||||
// Zoom fit all
|
// Zoom fit all
|
||||||
await page.keyboard.press("Shift+1");
|
await page.keyboard.press("Shift+1");
|
||||||
|
// Select all shapes to display selrect
|
||||||
|
await workspacePage.page.keyboard.press("ControlOrMeta+a");
|
||||||
|
|
||||||
const ids = [
|
const ids = [
|
||||||
"shape-165d1e5a-5873-8010-8005-9ffdbeaeec59",
|
"shape-165d1e5a-5873-8010-8005-9ffdbeaeec59",
|
||||||
@@ -441,7 +442,7 @@ test("[Taiga #9930] Zoom fit all doesn't fits all", async ({
|
|||||||
|
|
||||||
const viewportBoundingBox = await workspacePage.viewport.boundingBox();
|
const viewportBoundingBox = await workspacePage.viewport.boundingBox();
|
||||||
for (const id of ids) {
|
for (const id of ids) {
|
||||||
const shape = await page.locator(`.ws-shape-wrapper > g#${id}`);
|
const shape = page.locator(`.viewport-selrect`);
|
||||||
const shapeBoundingBox = await shape.boundingBox();
|
const shapeBoundingBox = await shape.boundingBox();
|
||||||
expect(contains(viewportBoundingBox, shapeBoundingBox)).toBeTruthy();
|
expect(contains(viewportBoundingBox, shapeBoundingBox)).toBeTruthy();
|
||||||
}
|
}
|
||||||
@@ -450,7 +451,7 @@ test("[Taiga #9930] Zoom fit all doesn't fits all", async ({
|
|||||||
test("Bug 9877, user navigation to dashboard from header goes to blank page", async ({
|
test("Bug 9877, user navigation to dashboard from header goes to blank page", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
|
|
||||||
await workspacePage.goToWorkspace();
|
await workspacePage.goToWorkspace();
|
||||||
@@ -467,7 +468,7 @@ test("Bug 9877, user navigation to dashboard from header goes to blank page", as
|
|||||||
test("Bug 8371 - Flatten option is not visible in context menu", async ({
|
test("Bug 8371 - Flatten option is not visible in context menu", async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
const workspacePage = new WorkspacePage(page);
|
const workspacePage = new WasmWorkspacePage(page);
|
||||||
await workspacePage.setupEmptyFile(page);
|
await workspacePage.setupEmptyFile(page);
|
||||||
await workspacePage.mockGetFile("workspace/get-file-8371.json");
|
await workspacePage.mockGetFile("workspace/get-file-8371.json");
|
||||||
await workspacePage.goToWorkspace({
|
await workspacePage.goToWorkspace({
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
90
frontend/resources/wasm-playground/shadows.html
Normal file
90
frontend/resources/wasm-playground/shadows.html
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="es">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>WASM + WebGL2 Canvas</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
background: #111;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
canvas {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<script type="module">
|
||||||
|
import initWasmModule from '/js/render-wasm.js';
|
||||||
|
import {
|
||||||
|
init, addShapeSolidFill, assignCanvas, hexToU32ARGB, getRandomInt, getRandomColor,
|
||||||
|
getRandomFloat, useShape, setShapeChildren, setupInteraction, addShapeSolidStrokeFill
|
||||||
|
} from './js/lib.js';
|
||||||
|
|
||||||
|
const canvas = document.getElementById("canvas");
|
||||||
|
canvas.width = window.innerWidth;
|
||||||
|
canvas.height = window.innerHeight;
|
||||||
|
|
||||||
|
const params = new URLSearchParams(document.location.search);
|
||||||
|
const shapes = params.get("shapes") || 1000;
|
||||||
|
|
||||||
|
initWasmModule().then(Module => {
|
||||||
|
init(Module);
|
||||||
|
assignCanvas(canvas);
|
||||||
|
Module._set_canvas_background(hexToU32ARGB("#FABADA", 1));
|
||||||
|
Module._init_shapes_pool(shapes + 1);
|
||||||
|
setupInteraction(canvas);
|
||||||
|
|
||||||
|
const children = [];
|
||||||
|
for (let i = 0; i < shapes; i++) {
|
||||||
|
const uuid = crypto.randomUUID();
|
||||||
|
children.push(uuid);
|
||||||
|
|
||||||
|
useShape(uuid);
|
||||||
|
Module._set_parent(0, 0, 0, 0);
|
||||||
|
Module._set_shape_type(3);
|
||||||
|
const x1 = getRandomInt(0, canvas.width);
|
||||||
|
const y1 = getRandomInt(0, canvas.height);
|
||||||
|
const width = getRandomInt(20, 100);
|
||||||
|
const height = getRandomInt(20, 100);
|
||||||
|
Module._set_shape_selrect(x1, y1, x1 + width, y1 + height);
|
||||||
|
|
||||||
|
const color = getRandomColor();
|
||||||
|
const argb = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||||
|
addShapeSolidFill(argb)
|
||||||
|
|
||||||
|
Module._add_shape_center_stroke(10, 0, 0, 0);
|
||||||
|
const argb2 = hexToU32ARGB(color, getRandomFloat(0.1, 1.0));
|
||||||
|
addShapeSolidStrokeFill(argb2);
|
||||||
|
|
||||||
|
// Add shadows
|
||||||
|
// Shadow 1: drop-shadow, #dedede opacity 0.33, blur 4, spread -2, offsetX 0, offsetY 2
|
||||||
|
Module._add_shape_shadow(hexToU32ARGB("#dedede", 0.33), 4, -2, 0, 2, 0, false);
|
||||||
|
// Shadow 2: drop-shadow, #dedede opacity 1, blur 12, spread -8, offsetX 0, offsetY 12
|
||||||
|
Module._add_shape_shadow(hexToU32ARGB("#dedede", 1), 12, -8, 0, 12, 0, false);
|
||||||
|
// Shadow 3: inner-shadow, #002046 opacity 0.12, blur 12, spread -8, offsetX 0, offsetY -4
|
||||||
|
Module._add_shape_shadow(hexToU32ARGB("#002046", 0.12), 12, -8, 0, -4, 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
useShape("00000000-0000-0000-0000-000000000000");
|
||||||
|
setShapeChildren(children);
|
||||||
|
|
||||||
|
performance.mark('render:begin');
|
||||||
|
Module._set_view(1, 0, 0);
|
||||||
|
Module._render(Date.now());
|
||||||
|
performance.mark('render:end');
|
||||||
|
const { duration } = performance.measure('render', 'render:begin', 'render:end');
|
||||||
|
// alert(`render time: ${duration.toFixed(2)}ms`);
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
(def ^:private schema:properties-row
|
(def ^:private schema:properties-row
|
||||||
[:map
|
[:map
|
||||||
[:term :string]
|
[:term :string]
|
||||||
[:detail :string]
|
[:detail {:optional true} [:maybe :string]]
|
||||||
[:property {:optional true} :string] ;; CSS valid property
|
[:property {:optional true} :string] ;; CSS valid property
|
||||||
[:token {:optional true} :any] ;; resolved token object
|
[:token {:optional true} :any] ;; resolved token object
|
||||||
[:copiable {:optional true} :boolean]])
|
[:copiable {:optional true} :boolean]])
|
||||||
|
|||||||
@@ -78,13 +78,15 @@
|
|||||||
(fn []
|
(fn []
|
||||||
(close-modals)
|
(close-modals)
|
||||||
;; FIXME: move set-mode to uri?
|
;; FIXME: move set-mode to uri?
|
||||||
(st/emit! (dw/set-options-mode :design)
|
(st/emit! :interrupt
|
||||||
|
(dw/set-options-mode :design)
|
||||||
(dcm/go-to-dashboard-recent))))
|
(dcm/go-to-dashboard-recent))))
|
||||||
|
|
||||||
nav-to-project
|
nav-to-project
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps project-id)
|
(mf/deps project-id)
|
||||||
#(st/emit! (dcm/go-to-dashboard-files ::rt/new-window true :project-id project-id)))]
|
#(st/emit! :interrupt
|
||||||
|
(dcm/go-to-dashboard-files ::rt/new-window true :project-id project-id)))]
|
||||||
|
|
||||||
(mf/with-effect [editing?]
|
(mf/with-effect [editing?]
|
||||||
(when ^boolean editing?
|
(when ^boolean editing?
|
||||||
|
|||||||
@@ -401,7 +401,8 @@
|
|||||||
(dm/fmt "scale(%)" maybe-zoom))}))]
|
(dm/fmt "scale(%)" maybe-zoom))}))]
|
||||||
|
|
||||||
[:g.text-editor {:clip-path (dm/fmt "url(#%)" clip-id)
|
[:g.text-editor {:clip-path (dm/fmt "url(#%)" clip-id)
|
||||||
:transform (dm/str transform)}
|
:transform (dm/str transform)
|
||||||
|
:data-testid "text-editor"}
|
||||||
[:defs
|
[:defs
|
||||||
[:clipPath {:id clip-id}
|
[:clipPath {:id clip-id}
|
||||||
[:rect {:x x :y y :width width :height height}]]]
|
[:rect {:x x :y y :width width :height height}]]]
|
||||||
|
|||||||
@@ -119,7 +119,8 @@
|
|||||||
[:button {:class (stl/css-case
|
[:button {:class (stl/css-case
|
||||||
:toggle-content true
|
:toggle-content true
|
||||||
:inverse expanded?)
|
:inverse expanded?)
|
||||||
:aria-label "Toggle layer"
|
:data-testid "toggle-content"
|
||||||
|
:aria-expanded expanded?
|
||||||
:on-click on-toggle-collapse}
|
:on-click on-toggle-collapse}
|
||||||
deprecated-icon/arrow])
|
deprecated-icon/arrow])
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,7 @@
|
|||||||
:on-blur accept-edit
|
:on-blur accept-edit
|
||||||
:on-key-down on-key-down
|
:on-key-down on-key-down
|
||||||
:auto-focus true
|
:auto-focus true
|
||||||
|
:id (dm/str "layer-name-" shape-id)
|
||||||
:default-value (d/nilv default-value "")}]
|
:default-value (d/nilv default-value "")}]
|
||||||
[:*
|
[:*
|
||||||
[:span
|
[:span
|
||||||
@@ -118,6 +119,7 @@
|
|||||||
:hidden is-hidden
|
:hidden is-hidden
|
||||||
:type-comp type-comp
|
:type-comp type-comp
|
||||||
:type-frame type-frame)
|
:type-frame type-frame)
|
||||||
|
:id (dm/str "layer-name-" shape-id)
|
||||||
:style {"--depth" depth "--parent-size" parent-size}
|
:style {"--depth" depth "--parent-size" parent-size}
|
||||||
:ref ref
|
:ref ref
|
||||||
:on-double-click start-edit}
|
:on-double-click start-edit}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ pub use surfaces::{SurfaceId, Surfaces};
|
|||||||
|
|
||||||
use crate::performance;
|
use crate::performance;
|
||||||
use crate::shapes::{
|
use crate::shapes::{
|
||||||
all_with_ancestors, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Stroke, Type,
|
all_with_ancestors, Blur, BlurType, Corners, Fill, Shadow, Shape, SolidColor, Type,
|
||||||
};
|
};
|
||||||
use crate::state::{ShapesPoolMutRef, ShapesPoolRef};
|
use crate::state::{ShapesPoolMutRef, ShapesPoolRef};
|
||||||
use crate::tiles::{self, PendingTiles, TileRect};
|
use crate::tiles::{self, PendingTiles, TileRect};
|
||||||
@@ -1512,6 +1512,16 @@ impl RenderState {
|
|||||||
Self::combine_blur_values(self.combined_layer_blur(shape.blur), extra_layer_blur);
|
Self::combine_blur_values(self.combined_layer_blur(shape.blur), extra_layer_blur);
|
||||||
let blur_filter = combined_blur
|
let blur_filter = combined_blur
|
||||||
.and_then(|blur| skia::image_filters::blur((blur.value, blur.value), None, None, None));
|
.and_then(|blur| skia::image_filters::blur((blur.value, blur.value), None, None, None));
|
||||||
|
// Legacy path is only stable up to 1.0 zoom: the canvas is scaled and the shadow
|
||||||
|
// filter is evaluated in that scaled space, so for scale > 1 it over-inflates blur/spread.
|
||||||
|
// We also disable it when combined layer blur is present to avoid incorrect composition.
|
||||||
|
let use_low_zoom_path = scale <= 1.0 && combined_blur.is_none();
|
||||||
|
|
||||||
|
if use_low_zoom_path {
|
||||||
|
// Match pre-commit behavior: scale blur/spread with zoom for low zoom levels.
|
||||||
|
transformed_shadow.to_mut().blur = shadow.blur * scale;
|
||||||
|
transformed_shadow.to_mut().spread = shadow.spread * scale;
|
||||||
|
}
|
||||||
|
|
||||||
let mut transform_matrix = shape.transform;
|
let mut transform_matrix = shape.transform;
|
||||||
let center = shape.center();
|
let center = shape.center();
|
||||||
@@ -1526,28 +1536,20 @@ impl RenderState {
|
|||||||
let world_offset = (mapped.x, mapped.y);
|
let world_offset = (mapped.x, mapped.y);
|
||||||
|
|
||||||
// The opacity of fills and strokes shouldn't affect the shadow,
|
// The opacity of fills and strokes shouldn't affect the shadow,
|
||||||
// so we paint everything black with the same opacity
|
// so we paint everything black with the same opacity.
|
||||||
plain_shape.to_mut().clear_fills();
|
let plain_shape_mut = plain_shape.to_mut();
|
||||||
|
plain_shape_mut.clear_fills();
|
||||||
if shape.has_fills() {
|
if shape.has_fills() {
|
||||||
plain_shape
|
plain_shape_mut.add_fill(Fill::Solid(SolidColor(skia::Color::BLACK)));
|
||||||
.to_mut()
|
|
||||||
.add_fill(Fill::Solid(SolidColor(skia::Color::BLACK)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plain_shape.to_mut().clear_strokes();
|
// Reuse existing strokes and only override their fill color.
|
||||||
for stroke in shape.strokes.iter() {
|
for stroke in plain_shape_mut.strokes.iter_mut() {
|
||||||
plain_shape.to_mut().add_stroke(Stroke {
|
stroke.fill = Fill::Solid(SolidColor(skia::Color::BLACK));
|
||||||
fill: Fill::Solid(SolidColor(skia::Color::BLACK)),
|
|
||||||
width: stroke.width,
|
|
||||||
style: stroke.style,
|
|
||||||
cap_end: stroke.cap_end,
|
|
||||||
cap_start: stroke.cap_start,
|
|
||||||
kind: stroke.kind,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
plain_shape.to_mut().clear_shadows();
|
plain_shape_mut.clear_shadows();
|
||||||
plain_shape.to_mut().blur = None;
|
plain_shape_mut.blur = None;
|
||||||
|
|
||||||
let Some(drop_filter) = transformed_shadow.get_drop_shadow_filter() else {
|
let Some(drop_filter) = transformed_shadow.get_drop_shadow_filter() else {
|
||||||
return;
|
return;
|
||||||
@@ -1556,6 +1558,39 @@ impl RenderState {
|
|||||||
let mut bounds = drop_filter.compute_fast_bounds(shape_bounds);
|
let mut bounds = drop_filter.compute_fast_bounds(shape_bounds);
|
||||||
// Account for the shadow offset so the temporary surface fully contains the shifted blur.
|
// Account for the shadow offset so the temporary surface fully contains the shifted blur.
|
||||||
bounds.offset(world_offset);
|
bounds.offset(world_offset);
|
||||||
|
// Early cull if the shadow bounds are outside the render area.
|
||||||
|
if !bounds.intersects(self.render_area) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if use_low_zoom_path {
|
||||||
|
let mut shadow_paint = skia::Paint::default();
|
||||||
|
shadow_paint.set_image_filter(drop_filter);
|
||||||
|
shadow_paint.set_blend_mode(skia::BlendMode::SrcOver);
|
||||||
|
|
||||||
|
let layer_rec = skia::canvas::SaveLayerRec::default().paint(&shadow_paint);
|
||||||
|
let drop_canvas = self.surfaces.canvas(SurfaceId::DropShadows);
|
||||||
|
drop_canvas.save_layer(&layer_rec);
|
||||||
|
drop_canvas.scale((scale, scale));
|
||||||
|
drop_canvas.translate(translation);
|
||||||
|
|
||||||
|
self.with_nested_blurs_suppressed(|state| {
|
||||||
|
state.render_shape(
|
||||||
|
&plain_shape,
|
||||||
|
clip_bounds,
|
||||||
|
SurfaceId::DropShadows,
|
||||||
|
SurfaceId::DropShadows,
|
||||||
|
SurfaceId::DropShadows,
|
||||||
|
SurfaceId::DropShadows,
|
||||||
|
false,
|
||||||
|
Some(shadow.offset),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.surfaces.canvas(SurfaceId::DropShadows).restore();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let filter_result =
|
let filter_result =
|
||||||
filters::render_into_filter_surface(self, bounds, |state, temp_surface| {
|
filters::render_into_filter_surface(self, bounds, |state, temp_surface| {
|
||||||
|
|||||||
Reference in New Issue
Block a user