mirror of
https://github.com/penpot/penpot.git
synced 2026-09-19 02:16:14 +00:00
Move SPA routing out of the URL fragment into the normal query string. The screen travels in a reserved `screen` key holding the route name (`?screen=workspace&team-id=…`); every other param keeps its name. `rt/nav` and `rt/resolve` keep their signatures. This deletes the fragment-mirroring URL surgery, simplifies link-preview (the server sees everything) and nginx (single path, no SPA fallback rules needed), and migrates OIDC redirects, email links, e2e helpers and plugin test utils to the new format. Legacy `#/…` URLs translate client-side for one Penpot version (`legacy-routes`, marked TODO(next-version)); non-SPA paths are untouched. AI-assisted-by: muse-spark-1.3-contributor
166 lines
4.6 KiB
JavaScript
166 lines
4.6 KiB
JavaScript
import { BaseWebSocketPage } from "./BaseWebSocketPage";
|
|
import { MockWebSocketHelper } from "../../helpers/MockWebSocketHelper";
|
|
|
|
export class ViewerPage extends BaseWebSocketPage {
|
|
static anyFileId = "c7ce0794-0992-8105-8004-38f280443849";
|
|
static anyPageId = "c7ce0794-0992-8105-8004-38f28044384a";
|
|
|
|
async setupLoggedInUser() {
|
|
await this.mockRPC(
|
|
"get-profile",
|
|
"logged-in-user/get-profile-logged-in.json",
|
|
);
|
|
}
|
|
|
|
async setupEmptyFile() {
|
|
await this.mockRPC(
|
|
/get\-view\-only\-bundle\?/,
|
|
"viewer/get-view-only-bundle-empty-file.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-comment-threads?file-id=*",
|
|
"workspace/get-comment-threads-empty.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-file-fragment?file-id=*&fragment-id=*",
|
|
"viewer/get-file-fragment-empty-file.json",
|
|
);
|
|
}
|
|
|
|
async setupFileWithSingleBoard() {
|
|
await this.mockRPC(
|
|
/get\-view\-only\-bundle\?/,
|
|
"viewer/get-view-only-bundle-single-board.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-comment-threads?file-id=*",
|
|
"workspace/get-comment-threads-empty.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-file-fragment?file-id=*&fragment-id=*",
|
|
"viewer/get-file-fragment-single-board.json",
|
|
);
|
|
}
|
|
|
|
async setupFileWithMultipleBoards() {
|
|
await this.mockRPC(
|
|
/get\-view\-only\-bundle\?/,
|
|
"viewer/get-view-only-bundle-multiple-boards.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-comment-threads?file-id=*",
|
|
"workspace/get-comment-threads-empty.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-file-fragment?file-id=*&fragment-id=*",
|
|
"viewer/get-file-fragment-multiple-boards.json",
|
|
);
|
|
}
|
|
|
|
async setupFileWithInteractionBlocksChild() {
|
|
await this.mockRPC(
|
|
/get\-view\-only\-bundle\?/,
|
|
"viewer/get-view-only-bundle-interaction-blocks-child.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-comment-threads?file-id=*",
|
|
"workspace/get-comment-threads-empty.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-file-fragment?file-id=*&fragment-id=*",
|
|
"viewer/get-file-fragment-interaction-blocks-child.json",
|
|
);
|
|
}
|
|
|
|
async setupFileWithRotatedBoardStroke() {
|
|
await this.mockRPC(
|
|
/get\-view\-only\-bundle\?/,
|
|
"viewer/get-view-only-bundle-rotated-board-stroke.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-comment-threads?file-id=*",
|
|
"workspace/get-comment-threads-empty.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-file-fragment?file-id=*&fragment-id=*",
|
|
"viewer/get-file-fragment-rotated-board-stroke.json",
|
|
);
|
|
}
|
|
|
|
async setupFileWithComments() {
|
|
await this.mockRPC(
|
|
/get\-view\-only\-bundle\?/,
|
|
"viewer/get-view-only-bundle-single-board.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-comment-threads?file-id=*",
|
|
"workspace/get-comment-threads-not-empty.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-file-fragment?file-id=*&fragment-id=*",
|
|
"viewer/get-file-fragment-single-board.json",
|
|
);
|
|
await this.mockRPC(
|
|
"get-comments?thread-id=*",
|
|
"workspace/get-thread-comments.json",
|
|
);
|
|
await this.mockRPC(
|
|
"update-comment-thread-status",
|
|
"workspace/update-comment-thread-status.json",
|
|
);
|
|
}
|
|
|
|
#ws = null;
|
|
|
|
constructor(page) {
|
|
super(page);
|
|
}
|
|
|
|
async goToViewer({
|
|
fileId = ViewerPage.anyFileId,
|
|
pageId = ViewerPage.anyPageId,
|
|
} = {}) {
|
|
// Same as WorkspacePage.goToWorkspace: skip the reload when already
|
|
// on the target file so repeated setups keep the in-memory file
|
|
// state. Extra query params are ignored.
|
|
const currentParams = new URL(this.page.url()).searchParams;
|
|
const sameFile =
|
|
currentParams.get("screen") === "viewer" &&
|
|
currentParams.get("file-id") === fileId &&
|
|
currentParams.get("page-id") === pageId;
|
|
if (!sameFile) {
|
|
// Same as WorkspacePage.goToWorkspace: drop stale mocks from any
|
|
// previous document before reloading the app.
|
|
MockWebSocketHelper.clear();
|
|
await this.page.goto(
|
|
`/?screen=viewer&file-id=${fileId}&page-id=${pageId}§ion=interactions&index=0`,
|
|
);
|
|
}
|
|
|
|
this.#ws = await this.waitForNotificationsWebSocket();
|
|
await this.#ws.mockOpen();
|
|
}
|
|
|
|
async cleanUp() {
|
|
await this.#ws.mockClose();
|
|
}
|
|
|
|
async showComments(clickOptions = {}) {
|
|
await this.page
|
|
.getByRole("button", { name: "Comments (G C)" })
|
|
.click(clickOptions);
|
|
}
|
|
|
|
async showCommentsThread(number, clickOptions = {}) {
|
|
await this.page
|
|
.getByTestId(`floating-thread-bubble-${number.toString()}`)
|
|
.click(clickOptions);
|
|
}
|
|
|
|
async showCode(clickOptions = {}) {
|
|
await this.page
|
|
.getByRole("button", { name: "Inspect (G I)" })
|
|
.click(clickOptions);
|
|
}
|
|
}
|