mirror of
https://github.com/penpot/penpot.git
synced 2026-05-30 04:08:08 +00:00
🐛 Fix rename on non empty page (#9850)
This commit is contained in:
parent
017f1d9994
commit
9439d63682
308
frontend/playwright/data/workspace/get-file-rename-page.json
Normal file
308
frontend/playwright/data/workspace/get-file-rename-page.json
Normal file
@ -0,0 +1,308 @@
|
||||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true,
|
||||
"~:can-read": true,
|
||||
"~:is-logged": true
|
||||
},
|
||||
"~:has-media-trimmed": false,
|
||||
"~:comment-thread-seqn": 0,
|
||||
"~:name": "Rename Page Test File",
|
||||
"~:revn": 1,
|
||||
"~:modified-at": "~m1713873823633",
|
||||
"~:id": "~uaaaaaaaa-0000-0000-0000-000000000001",
|
||||
"~:is-shared": false,
|
||||
"~:version": 46,
|
||||
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
|
||||
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
|
||||
"~:created-at": "~m1713536343369",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~ubbbbbbbb-0000-0000-0000-000000000001",
|
||||
"~ucccccccc-0000-0000-0000-000000000002",
|
||||
"~udddddddd-0000-0000-0000-000000000003"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~ubbbbbbbb-0000-0000-0000-000000000001": {
|
||||
"~:options": {},
|
||||
"~:objects": {
|
||||
"~u00000000-0000-0000-0000-000000000000": {
|
||||
"~#shape": {
|
||||
"~:y": 0,
|
||||
"~:hide-fill-on-export": false,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:name": "Root Frame",
|
||||
"~:width": 0.01,
|
||||
"~:type": "~:frame",
|
||||
"~:points": [
|
||||
{"~#point": {"~:x": 0, "~:y": 0}},
|
||||
{"~#point": {"~:x": 0.01, "~:y": 0}},
|
||||
{"~#point": {"~:x": 0.01, "~:y": 0.01}},
|
||||
{"~#point": {"~:x": 0, "~:y": 0.01}}
|
||||
],
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [],
|
||||
"~:x": 0,
|
||||
"~:proportion": 1.0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 0,
|
||||
"~:y": 0,
|
||||
"~:width": 0.01,
|
||||
"~:height": 0.01,
|
||||
"~:x1": 0,
|
||||
"~:y1": 0,
|
||||
"~:x2": 0.01,
|
||||
"~:y2": 0.01
|
||||
}
|
||||
},
|
||||
"~:fills": [{"~:fill-color": "#FFFFFF", "~:fill-opacity": 1}],
|
||||
"~:flip-x": null,
|
||||
"~:height": 0.01,
|
||||
"~:flip-y": null,
|
||||
"~:shapes": ["~ueeeeeee0-0000-0000-0000-000000000001"]
|
||||
}
|
||||
},
|
||||
"~ueeeeeee0-0000-0000-0000-000000000001": {
|
||||
"~#shape": {
|
||||
"~:y": 100,
|
||||
"~:r1": 0,
|
||||
"~:r2": 0,
|
||||
"~:r3": 0,
|
||||
"~:r4": 0,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:grow-type": "~:fixed",
|
||||
"~:hide-in-viewer": false,
|
||||
"~:name": "Rectangle",
|
||||
"~:width": 200,
|
||||
"~:type": "~:rect",
|
||||
"~:points": [
|
||||
{"~#point": {"~:x": 100, "~:y": 100}},
|
||||
{"~#point": {"~:x": 300, "~:y": 100}},
|
||||
{"~#point": {"~:x": 300, "~:y": 300}},
|
||||
{"~#point": {"~:x": 100, "~:y": 300}}
|
||||
],
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:id": "~ueeeeeee0-0000-0000-0000-000000000001",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [],
|
||||
"~:x": 100,
|
||||
"~:proportion": 1.0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 100,
|
||||
"~:y": 100,
|
||||
"~:width": 200,
|
||||
"~:height": 200,
|
||||
"~:x1": 100,
|
||||
"~:y1": 100,
|
||||
"~:x2": 300,
|
||||
"~:y2": 300
|
||||
}
|
||||
},
|
||||
"~:fills": [{"~:fill-color": "#B1B2B5", "~:fill-opacity": 1}],
|
||||
"~:flip-x": null,
|
||||
"~:ry": 0,
|
||||
"~:height": 200,
|
||||
"~:flip-y": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"~:id": "~ubbbbbbbb-0000-0000-0000-000000000001",
|
||||
"~:name": "Page 1"
|
||||
},
|
||||
"~ucccccccc-0000-0000-0000-000000000002": {
|
||||
"~:options": {},
|
||||
"~:objects": {
|
||||
"~u00000000-0000-0000-0000-000000000000": {
|
||||
"~#shape": {
|
||||
"~:y": 0,
|
||||
"~:hide-fill-on-export": false,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:name": "Root Frame",
|
||||
"~:width": 0.01,
|
||||
"~:type": "~:frame",
|
||||
"~:points": [
|
||||
{"~#point": {"~:x": 0, "~:y": 0}},
|
||||
{"~#point": {"~:x": 0.01, "~:y": 0}},
|
||||
{"~#point": {"~:x": 0.01, "~:y": 0.01}},
|
||||
{"~#point": {"~:x": 0, "~:y": 0.01}}
|
||||
],
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [],
|
||||
"~:x": 0,
|
||||
"~:proportion": 1.0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 0,
|
||||
"~:y": 0,
|
||||
"~:width": 0.01,
|
||||
"~:height": 0.01,
|
||||
"~:x1": 0,
|
||||
"~:y1": 0,
|
||||
"~:x2": 0.01,
|
||||
"~:y2": 0.01
|
||||
}
|
||||
},
|
||||
"~:fills": [{"~:fill-color": "#FFFFFF", "~:fill-opacity": 1}],
|
||||
"~:flip-x": null,
|
||||
"~:height": 0.01,
|
||||
"~:flip-y": null,
|
||||
"~:shapes": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"~:id": "~ucccccccc-0000-0000-0000-000000000002",
|
||||
"~:name": "Page 2"
|
||||
},
|
||||
"~udddddddd-0000-0000-0000-000000000003": {
|
||||
"~:options": {},
|
||||
"~:objects": {
|
||||
"~u00000000-0000-0000-0000-000000000000": {
|
||||
"~#shape": {
|
||||
"~:y": 0,
|
||||
"~:hide-fill-on-export": false,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:rotation": 0,
|
||||
"~:name": "Root Frame",
|
||||
"~:width": 0.01,
|
||||
"~:type": "~:frame",
|
||||
"~:points": [
|
||||
{"~#point": {"~:x": 0, "~:y": 0}},
|
||||
{"~#point": {"~:x": 0.01, "~:y": 0}},
|
||||
{"~#point": {"~:x": 0.01, "~:y": 0.01}},
|
||||
{"~#point": {"~:x": 0, "~:y": 0.01}}
|
||||
],
|
||||
"~:proportion-lock": false,
|
||||
"~:transform-inverse": {
|
||||
"~#matrix": {
|
||||
"~:a": 1.0,
|
||||
"~:b": 0.0,
|
||||
"~:c": 0.0,
|
||||
"~:d": 1.0,
|
||||
"~:e": 0.0,
|
||||
"~:f": 0.0
|
||||
}
|
||||
},
|
||||
"~:id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:parent-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:frame-id": "~u00000000-0000-0000-0000-000000000000",
|
||||
"~:strokes": [],
|
||||
"~:x": 0,
|
||||
"~:proportion": 1.0,
|
||||
"~:selrect": {
|
||||
"~#rect": {
|
||||
"~:x": 0,
|
||||
"~:y": 0,
|
||||
"~:width": 0.01,
|
||||
"~:height": 0.01,
|
||||
"~:x1": 0,
|
||||
"~:y1": 0,
|
||||
"~:x2": 0.01,
|
||||
"~:y2": 0.01
|
||||
}
|
||||
},
|
||||
"~:fills": [{"~:fill-color": "#FFFFFF", "~:fill-opacity": 1}],
|
||||
"~:flip-x": null,
|
||||
"~:height": 0.01,
|
||||
"~:flip-y": null,
|
||||
"~:shapes": []
|
||||
}
|
||||
}
|
||||
},
|
||||
"~:id": "~udddddddd-0000-0000-0000-000000000003",
|
||||
"~:name": "Page 3"
|
||||
}
|
||||
},
|
||||
"~:id": "~uaaaaaaaa-0000-0000-0000-000000000001",
|
||||
"~:options": {
|
||||
"~:components-v2": true
|
||||
},
|
||||
"~:recent-colors": []
|
||||
}
|
||||
}
|
||||
127
frontend/playwright/ui/specs/rename-page.spec.js
Normal file
127
frontend/playwright/ui/specs/rename-page.spec.js
Normal file
@ -0,0 +1,127 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
import { WorkspacePage } from "../pages/WorkspacePage";
|
||||
|
||||
// UUIDs matching the fixture file `workspace/get-file-rename-page.json`
|
||||
const FILE_ID = "aaaaaaaa-0000-0000-0000-000000000001";
|
||||
const PAGE1_ID = "bbbbbbbb-0000-0000-0000-000000000001"; // non-empty (has a rectangle)
|
||||
const PAGE2_ID = "cccccccc-0000-0000-0000-000000000002"; // empty
|
||||
const PAGE3_ID = "dddddddd-0000-0000-0000-000000000003"; // empty
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WorkspacePage.init(page);
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns a locator for the interactive body of a page item in the sitemap
|
||||
* sidebar, identified by its page UUID.
|
||||
*/
|
||||
function getPageItem(page, pageId) {
|
||||
return page.getByTestId(`page-${pageId}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the visible name span inside a page item (not in edit mode).
|
||||
*/
|
||||
function getPageNameSpan(page, pageId) {
|
||||
return getPageItem(page, pageId).getByTestId("page-name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Double-clicks a page item to enter rename mode, types the new name and
|
||||
* confirms with Enter.
|
||||
*/
|
||||
async function renamePage(page, pageId, newName) {
|
||||
const item = getPageItem(page, pageId);
|
||||
await item.dblclick();
|
||||
const input = item.locator("input");
|
||||
await expect(input).toBeVisible();
|
||||
await input.selectText();
|
||||
await input.fill(newName);
|
||||
await page.keyboard.press("Enter");
|
||||
}
|
||||
|
||||
async function setupWorkspace(workspacePage) {
|
||||
await workspacePage.setupEmptyFile();
|
||||
|
||||
// Override the file mock with the 3-page fixture.
|
||||
await workspacePage.mockRPC(
|
||||
/get\-file\?/,
|
||||
"workspace/get-file-rename-page.json",
|
||||
);
|
||||
|
||||
// Mock update-file so rename commits don't fail.
|
||||
await workspacePage.mockRPC(
|
||||
"update-file?id=*",
|
||||
"workspace/update-file-create-rect.json",
|
||||
);
|
||||
|
||||
// The base WorkspacePage uses `this.pageName` (getByTestId("page-name")) as a
|
||||
// readiness signal inside goToWorkspace. With 3 pages in the sidebar there are
|
||||
// 3 matching elements, which violates Playwright's strict mode. Narrow the
|
||||
// locator to the first occurrence so the internal readiness check can pass.
|
||||
workspacePage.pageName = workspacePage.page
|
||||
.getByTestId("page-name")
|
||||
.first();
|
||||
|
||||
await workspacePage.goToWorkspace({
|
||||
fileId: FILE_ID,
|
||||
pageId: PAGE1_ID,
|
||||
pageName: "Page 1",
|
||||
});
|
||||
}
|
||||
|
||||
test("User renames a non-empty page to '---' — page is renamed, URL does not change", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
await setupWorkspace(workspacePage);
|
||||
|
||||
// Confirm we are on Page 1.
|
||||
await expect(getPageNameSpan(page, PAGE1_ID)).toHaveText("Page 1");
|
||||
|
||||
const urlBefore = page.url();
|
||||
expect(urlBefore).toContain(`page-id=${PAGE1_ID}`);
|
||||
|
||||
// Rename the non-empty page to "---".
|
||||
await renamePage(page, PAGE1_ID, "---");
|
||||
|
||||
// The page name should have changed to "---".
|
||||
await expect(getPageNameSpan(page, PAGE1_ID)).toHaveText("---");
|
||||
|
||||
// The URL must still point to the same page (no navigation triggered).
|
||||
await expect(page).toHaveURL(new RegExp(`page-id=${PAGE1_ID}`));
|
||||
|
||||
// The page must NOT be rendered as a separator (it still has shapes).
|
||||
await expect(
|
||||
getPageItem(page, PAGE1_ID).getByTestId("page-separator"),
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
test("User renames an empty page to '---' — page becomes a separator and URL changes", async ({
|
||||
page,
|
||||
}) => {
|
||||
const workspacePage = new WorkspacePage(page);
|
||||
await setupWorkspace(workspacePage);
|
||||
|
||||
// Navigate to the second page (empty) by clicking it in the sitemap.
|
||||
const page2Item = getPageItem(page, PAGE2_ID);
|
||||
await page2Item.click();
|
||||
|
||||
// Wait until the URL reflects the navigation to Page 2.
|
||||
await expect(page).toHaveURL(new RegExp(`page-id=${PAGE2_ID}`));
|
||||
|
||||
// Rename the empty page to "---".
|
||||
await renamePage(page, PAGE2_ID, "---");
|
||||
|
||||
// Since the renamed page is empty, it must now be shown as a separator.
|
||||
await expect(
|
||||
getPageItem(page, PAGE2_ID).getByTestId("page-separator"),
|
||||
).toBeVisible();
|
||||
|
||||
// The application must have navigated away from the separator page to
|
||||
// a different page (Page 1 or Page 3 depending on fallback logic).
|
||||
await expect(page).not.toHaveURL(new RegExp(`page-id=${PAGE2_ID}`));
|
||||
|
||||
// The current URL must still be a workspace URL pointing to the same file.
|
||||
await expect(page).toHaveURL(new RegExp(`file-id=${FILE_ID}`));
|
||||
});
|
||||
@ -329,6 +329,9 @@
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page (dsh/lookup-page state id)
|
||||
objects (:objects page)
|
||||
empty-page? (and (= 1 (count objects))
|
||||
(= uuid/zero (first (keys objects))))
|
||||
changes (-> (pcb/empty-changes it)
|
||||
(pcb/with-page page)
|
||||
(pcb/mod-page page {:name name}))
|
||||
@ -342,7 +345,11 @@
|
||||
separator? (= "---" (str/trim name))]
|
||||
(rx/concat
|
||||
(rx/of (dch/commit-changes changes))
|
||||
;; Go to other page only if page is empty (only has the root shape)
|
||||
;; and the separator page is being renamed, otherwise user can rename
|
||||
;; any page to separator and be forced to go to another page
|
||||
(when (and separator?
|
||||
empty-page?
|
||||
(= id (:current-page-id state))
|
||||
(some? fallback-page-id))
|
||||
(rx/of (dcm/go-to-workspace :page-id fallback-page-id))))))))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user