mirror of
https://github.com/penpot/penpot.git
synced 2025-02-23 07:16:07 -05:00
✨ Adapt mock and add workspace test with websocket mock
This commit is contained in:
parent
30321e54f0
commit
3bae6e4661
19 changed files with 347 additions and 132 deletions
|
@ -0,0 +1 @@
|
|||
[]
|
58
frontend/playwright/fixtures/workspace/get-file-blank.json
Normal file
58
frontend/playwright/fixtures/workspace/get-file-blank.json
Normal file
|
@ -0,0 +1,58 @@
|
|||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/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": "New File 1",
|
||||
"~:revn": 11,
|
||||
"~:modified-at": "~m1713873823633",
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38f280443849",
|
||||
"~:is-shared": false,
|
||||
"~:version": 46,
|
||||
"~:project-id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
|
||||
"~:created-at": "~m1713536343369",
|
||||
"~:data": {
|
||||
"~:pages": [
|
||||
"~uc7ce0794-0992-8105-8004-38f28044384a"
|
||||
],
|
||||
"~:pages-index": {
|
||||
"~uc7ce0794-0992-8105-8004-38f28044384a": {
|
||||
"~#penpot/pointer": [
|
||||
"~ude58c8f6-c5c2-8196-8004-3df9e2e52d88",
|
||||
{
|
||||
"~:created-at": "~m1713873823636"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38f280443849",
|
||||
"~:options": {
|
||||
"~:components-v2": true
|
||||
},
|
||||
"~:recent-colors": [
|
||||
{
|
||||
"~:color": "#0000ff",
|
||||
"~:opacity": 1,
|
||||
"~:id": null,
|
||||
"~:file-id": null,
|
||||
"~:image": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"~:id": "~ude58c8f6-c5c2-8196-8004-3df9e2e52d88",
|
||||
"~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849",
|
||||
"~:created-at": "~m1713873823631",
|
||||
"~:content": {
|
||||
"~:options": {},
|
||||
"~:objects": {
|
||||
"~u00000000-0000-0000-0000-000000000000": {
|
||||
"~#shape": {
|
||||
"~:y": 0,
|
||||
"~:hide-fill-on-export": false,
|
||||
"~:transform": {
|
||||
"~#matrix": {
|
||||
"~:a": 1,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 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,
|
||||
"~:b": 0,
|
||||
"~:c": 0,
|
||||
"~:d": 1,
|
||||
"~:e": 0,
|
||||
"~:f": 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,
|
||||
"~: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": "~uc7ce0794-0992-8105-8004-38f28044384a",
|
||||
"~:name": "Page 1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"c7ce0794-0992-8105-8004-38f280443849/c7ce0794-0992-8105-8004-38f28044384a/8c1035fa-01f0-8071-8004-3df966ff2c64/frame": "http://localhost:3449/assets/by-id/50d097ed-d321-4319-b00b-e82a9c9435ea"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -0,0 +1,9 @@
|
|||
[
|
||||
{
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
|
||||
"~:email": "foo@example.com",
|
||||
"~:name": "Princesa Leia",
|
||||
"~:fullname": "Princesa Leia",
|
||||
"~:is-active": true
|
||||
}
|
||||
]
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38e630f7920b",
|
||||
"~:team-id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
|
||||
"~:created-at": "~m1713533116382",
|
||||
"~:modified-at": "~m1713873823633",
|
||||
"~:is-default": true,
|
||||
"~:name": "Drafts"
|
||||
}
|
23
frontend/playwright/fixtures/workspace/get-team-default.json
Normal file
23
frontend/playwright/fixtures/workspace/get-team-default.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"~:features": {
|
||||
"~#set": [
|
||||
"layout/grid",
|
||||
"styles/v2",
|
||||
"fdata/pointer-map",
|
||||
"fdata/objects-map",
|
||||
"components/v2",
|
||||
"fdata/shape-data-type"
|
||||
]
|
||||
},
|
||||
"~:permissions": {
|
||||
"~:type": "~:membership",
|
||||
"~:is-owner": true,
|
||||
"~:is-admin": true,
|
||||
"~:can-edit": true
|
||||
},
|
||||
"~:name": "Default",
|
||||
"~:modified-at": "~m1713533116375",
|
||||
"~:id": "~uc7ce0794-0992-8105-8004-38e630f40f6d",
|
||||
"~:created-at": "~m1713533116375",
|
||||
"~:is-default": true
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
export const presenceFixture = {
|
||||
"~:type": "~:presence",
|
||||
"~:file-id": "~uc7ce0794-0992-8105-8004-38f280443849",
|
||||
"~:session-id": "~u37730924-d520-80f1-8004-4ae6e5c3942d",
|
||||
"~:profile-id": "~uc7ce0794-0992-8105-8004-38e630f29a9b",
|
||||
"~:subs-id": "~uc7ce0794-0992-8105-8004-38f280443849",
|
||||
};
|
17
frontend/playwright/helpers/MockAPI.js
Normal file
17
frontend/playwright/helpers/MockAPI.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
export const interceptRPC = (page, path, jsonFilename) =>
|
||||
page.route(`**/api/rpc/command/${path}`, (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/transit+json",
|
||||
path: `playwright/fixtures/${jsonFilename}`,
|
||||
}),
|
||||
);
|
||||
|
||||
export const interceptRPCByRegex = (page, regex, jsonFilename) =>
|
||||
page.route(regex, (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/transit+json",
|
||||
path: `playwright/fixtures/${jsonFilename}`,
|
||||
}),
|
||||
);
|
|
@ -1,8 +0,0 @@
|
|||
export const interceptRPC = (page, path, jsonFilename) =>
|
||||
page.route(`**/api/rpc/command/${path}`, (route) =>
|
||||
route.fulfill({
|
||||
status: 200,
|
||||
contentType: "application/transit+json",
|
||||
path: `playwright/fixtures/${jsonFilename}`,
|
||||
})
|
||||
);
|
|
@ -1,81 +1,5 @@
|
|||
export class MockWebSocket extends EventTarget {
|
||||
static #mocks = new Map();
|
||||
|
||||
export class WebSocketManager {
|
||||
static async init(page) {
|
||||
await page.exposeFunction('MockWebSocket$$constructor', (url, protocols) => {
|
||||
console.log('MockWebSocket$$constructor', MockWebSocket, url, protocols)
|
||||
const webSocket = new MockWebSocket(page, url, protocols);
|
||||
this.#mocks.set(url, webSocket);
|
||||
});
|
||||
await page.exposeFunction('MockWebSocket$$spyMessage', (url, data) => {
|
||||
console.log('MockWebSocket$$spyMessage', url, data)
|
||||
this.#mocks.get(url).dispatchEvent(new MessageEvent('message', { data }))
|
||||
});
|
||||
await page.exposeFunction('MockWebSocket$$spyClose', (url, code, reason) => {
|
||||
console.log('MockWebSocket$$spyClose', url, code, reason)
|
||||
this.#mocks.get(url).dispatchEvent(new CloseEvent('close', { code, reason }))
|
||||
});
|
||||
await page.addInitScript({ path: "playwright/scripts/MockWebSocket.js" });
|
||||
}
|
||||
|
||||
static waitForURL(url) {
|
||||
return new Promise((resolve) => {
|
||||
let intervalID = setInterval(() => {
|
||||
for (const [wsURL, ws] of this.#mocks) {
|
||||
console.log(wsURL)
|
||||
if (wsURL.includes(url)) {
|
||||
clearInterval(intervalID);
|
||||
return resolve(ws);
|
||||
}
|
||||
}
|
||||
}, 30)
|
||||
})
|
||||
}
|
||||
|
||||
#page = null
|
||||
#url
|
||||
#protocols
|
||||
|
||||
// spies.
|
||||
#spyClose = null
|
||||
#spyMessage = null
|
||||
|
||||
constructor(page, url, protocols) {
|
||||
super()
|
||||
this.#page = page
|
||||
this.#url = url
|
||||
this.#protocols = protocols
|
||||
}
|
||||
|
||||
mockOpen(options) {
|
||||
return this.#page.evaluate((options) => {
|
||||
WebSocket.getByURL(url).mockOpen(options)
|
||||
}, options)
|
||||
}
|
||||
|
||||
mockMessage(data) {
|
||||
return this.#page.evaluate((data) => {
|
||||
WebSocket.getByURL(url).mockMessage(data)
|
||||
}, data)
|
||||
}
|
||||
|
||||
mockClose() {
|
||||
return this.#page.evaluate(() => {
|
||||
WebSocket.getByURL(url).mockClose()
|
||||
})
|
||||
}
|
||||
|
||||
spyClose(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
throw new TypeError('Invalid callback')
|
||||
}
|
||||
this.#spyClose = fn
|
||||
}
|
||||
|
||||
spyMessage(fn) {
|
||||
if (typeof fn !== 'function') {
|
||||
throw new TypeError('Invalid callback')
|
||||
}
|
||||
this.#spyMessage = fn
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
import { interceptRPC } from "./helpers/MockRPC";
|
||||
import { MockWebSocket } from "./helpers/MockWebSocket";
|
||||
import { interceptRPC } from "./helpers/MockAPI";
|
||||
|
||||
const setupLoggedOutUser = async (page) => {
|
||||
await interceptRPC(page, "get-profile", "get-profile-anonymous.json");
|
||||
|
@ -30,17 +29,9 @@ const setupDashboardUser = async (page) => {
|
|||
"get-profiles-for-file-comments",
|
||||
"logged-in-user/get-profiles-for-file-comments-empty.json",
|
||||
);
|
||||
await interceptRPC(
|
||||
page,
|
||||
"get-builtin-templates",
|
||||
"logged-in-user/get-builtin-templates-empty.json",
|
||||
);
|
||||
await interceptRPC(page, "get-builtin-templates", "logged-in-user/get-builtin-templates-empty.json");
|
||||
};
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await MockWebSocket.init(page);
|
||||
})
|
||||
|
||||
test("Shows login page when going to index and user is logged out", async ({ page }) => {
|
||||
await setupLoggedOutUser(page);
|
||||
|
||||
|
@ -62,8 +53,5 @@ test("User logs in by filling the login form", async ({ page }) => {
|
|||
|
||||
await page.getByRole("button", { name: "Login" }).click();
|
||||
|
||||
const ws = await MockWebSocket.waitForURL('ws://0.0.0.0:3500/ws/notifications');
|
||||
console.log(ws)
|
||||
|
||||
await expect(page).toHaveURL(/dashboard/);
|
||||
});
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
console.log("MockWebSocket mock loaded");
|
||||
window.WebSocket = class MockWebSocket extends EventTarget {
|
||||
static CONNECTING = 0;
|
||||
static OPEN = 1;
|
||||
|
@ -15,6 +14,19 @@ window.WebSocket = class MockWebSocket extends EventTarget {
|
|||
return this.#mocks.get(url);
|
||||
}
|
||||
|
||||
static waitForURL(url) {
|
||||
return new Promise((resolve) => {
|
||||
let intervalID = setInterval(() => {
|
||||
for (const [wsURL, ws] of this.#mocks) {
|
||||
if (wsURL.includes(url)) {
|
||||
clearInterval(intervalID);
|
||||
resolve(ws);
|
||||
}
|
||||
}
|
||||
}, 30);
|
||||
});
|
||||
}
|
||||
|
||||
#url;
|
||||
#protocols;
|
||||
#protocol = "";
|
||||
|
@ -32,10 +44,9 @@ window.WebSocket = class MockWebSocket extends EventTarget {
|
|||
#spyClose = null;
|
||||
|
||||
constructor(url, protocols) {
|
||||
console.log("🤖 New websocket at", url);
|
||||
super();
|
||||
|
||||
console.log("MockWebSocket", url, protocols);
|
||||
|
||||
this.#url = url;
|
||||
this.#protocols = protocols || [];
|
||||
|
||||
|
@ -84,56 +95,56 @@ window.WebSocket = class MockWebSocket extends EventTarget {
|
|||
}
|
||||
|
||||
set onopen(callback) {
|
||||
if (callback === null) {
|
||||
this.removeEventListener("open", this.#onopen);
|
||||
} else if (typeof callback === "function") {
|
||||
if (this.#onopen) this.removeEventListener("open", this.#onopen);
|
||||
this.#onopen = null;
|
||||
|
||||
if (typeof callback === "function") {
|
||||
this.addEventListener("open", callback);
|
||||
}
|
||||
this.#onopen = callback;
|
||||
}
|
||||
}
|
||||
|
||||
get onopen() {
|
||||
return this.#onopen;
|
||||
}
|
||||
|
||||
set onerror(callback) {
|
||||
if (callback === null) {
|
||||
this.removeEventListener("error", this.#onerror);
|
||||
} else if (typeof callback === "function") {
|
||||
if (this.#onerror) this.removeEventListener("error", this.#onerror);
|
||||
this.#onerror = null;
|
||||
|
||||
if (typeof callback === "function") {
|
||||
this.addEventListener("error", callback);
|
||||
}
|
||||
this.#onerror = callback;
|
||||
}
|
||||
}
|
||||
|
||||
get onerror() {
|
||||
return this.#onerror;
|
||||
}
|
||||
|
||||
set onmessage(callback) {
|
||||
if (callback === null) {
|
||||
this.removeEventListener("message", this.#onmessage);
|
||||
} else if (typeof callback === "function") {
|
||||
if (this.#onmessage) this.removeEventListener("message", this.#onmessage);
|
||||
this.#onmessage = null;
|
||||
|
||||
if (typeof callback === "function") {
|
||||
this.addEventListener("message", callback);
|
||||
}
|
||||
this.#onmessage = callback;
|
||||
}
|
||||
}
|
||||
|
||||
get onmessage() {
|
||||
return this.#onmessage;
|
||||
}
|
||||
|
||||
set onclose(callback) {
|
||||
if (callback === null) {
|
||||
this.removeEventListener("close", this.#onclose);
|
||||
} else if (typeof callback === "function") {
|
||||
if (this.#onclose) this.removeEventListener("close", this.#onclose);
|
||||
this.#onclose = null;
|
||||
|
||||
if (typeof callback === "function") {
|
||||
this.addEventListener("close", callback);
|
||||
}
|
||||
this.#onclose = callback;
|
||||
}
|
||||
}
|
||||
|
||||
get onclose() {
|
||||
return this.#onclose;
|
||||
|
@ -160,6 +171,7 @@ window.WebSocket = class MockWebSocket extends EventTarget {
|
|||
}
|
||||
|
||||
mockOpen(options) {
|
||||
console.log("🤖 open mock");
|
||||
this.#protocol = options?.protocol || "";
|
||||
this.#extensions = options?.extensions || "";
|
||||
this.#readyState = MockWebSocket.OPEN;
|
||||
|
@ -174,9 +186,12 @@ window.WebSocket = class MockWebSocket extends EventTarget {
|
|||
}
|
||||
|
||||
mockMessage(data) {
|
||||
console.log("🤯 mock message");
|
||||
if (this.#readyState !== MockWebSocket.OPEN) {
|
||||
console.log("socket is not connected");
|
||||
throw new Error("MockWebSocket is not connected");
|
||||
}
|
||||
console.log("😰 dispatching `message`", { data });
|
||||
this.dispatchEvent(new MessageEvent("message", { data }));
|
||||
return this;
|
||||
}
|
||||
|
@ -188,16 +203,16 @@ window.WebSocket = class MockWebSocket extends EventTarget {
|
|||
}
|
||||
|
||||
send(data) {
|
||||
console.log(data);
|
||||
if (this.#readyState === MockWebSocket.CONNECTING) {
|
||||
throw new DOMException("InvalidStateError", "MockWebSocket is not connected");
|
||||
}
|
||||
console.log(`MockWebSocket send: ${data}`);
|
||||
this.#spyMessage && this.#spyMessage(this.url, data);
|
||||
|
||||
if (this.#spyMessage) {
|
||||
this.#spyMessage(this.url, data);
|
||||
}
|
||||
}
|
||||
|
||||
close(code, reason) {
|
||||
console.log(code, reason);
|
||||
if (code && !Number.isInteger(code) && code !== 1000 && (code < 3000 || code > 4999)) {
|
||||
throw new DOMException("InvalidAccessError", "Invalid code");
|
||||
}
|
||||
|
@ -214,7 +229,8 @@ window.WebSocket = class MockWebSocket extends EventTarget {
|
|||
}
|
||||
|
||||
this.#readyState = MockWebSocket.CLOSING;
|
||||
console.log("MockWebSocket close");
|
||||
this.#spyClose && this.#spyClose(this.url, code, reason);
|
||||
if (this.#spyClose) {
|
||||
this.#spyClose(this.url, code, reason);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
69
frontend/playwright/workspace.spec.js
Normal file
69
frontend/playwright/workspace.spec.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
import { test, expect } from "@playwright/test";
|
||||
import { interceptRPC, interceptRPCByRegex } from "./helpers/MockAPI";
|
||||
import { WebSocketManager } from "./helpers/MockWebSocket";
|
||||
import { presenceFixture } from "./fixtures/workspace/ws-notifications";
|
||||
|
||||
const anyProjectId = "c7ce0794-0992-8105-8004-38e630f7920b";
|
||||
const anyFileId = "c7ce0794-0992-8105-8004-38f280443849";
|
||||
const anyPageId = "c7ce0794-0992-8105-8004-38f28044384a";
|
||||
|
||||
const setupWorkspaceUser = (page) => {
|
||||
interceptRPC(page, "get-profile", "logged-in-user/get-profile-logged-in.json");
|
||||
interceptRPC(page, "get-team-users?file-id=*", "logged-in-user/get-team-users-single-user.json");
|
||||
interceptRPC(page, "get-comment-threads?file-id=*", "workspace/get-comment-threads-empty.json");
|
||||
interceptRPC(page, "get-project?id=*", "workspace/get-project-default.json");
|
||||
interceptRPC(page, "get-team?id=*", "workspace/get-team-default.json");
|
||||
interceptRPCByRegex(page, /get\-file\?/, "workspace/get-file-blank.json");
|
||||
interceptRPC(
|
||||
page,
|
||||
"get-file-object-thumbnails?file-id=*",
|
||||
"workspace/get-file-object-thumbnails-blank.json",
|
||||
);
|
||||
interceptRPC(
|
||||
page,
|
||||
"get-profiles-for-file-comments?file-id=*",
|
||||
"workspace/get-profile-for-file-comments.json",
|
||||
);
|
||||
interceptRPC(page, "get-font-variants?team-id=*", "workspace/get-font-variants-empty.json");
|
||||
interceptRPC(page, "get-file-fragment?file-id=*", "workspace/get-file-fragment-blank.json");
|
||||
interceptRPC(page, "get-file-libraries?file-id=*", "workspace/get-file-libraries-empty.json");
|
||||
};
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WebSocketManager.init(page);
|
||||
});
|
||||
|
||||
test("User loads worskpace with empty file", async ({ page }) => {
|
||||
await setupWorkspaceUser(page);
|
||||
|
||||
await page.goto(`/#/workspace/${anyProjectId}/${anyFileId}?page-id=${anyPageId}`);
|
||||
|
||||
await expect(page.getByTestId("page-name")).toHaveText("Page 1");
|
||||
});
|
||||
|
||||
test.only("User receives notifications updates in the workspace", async ({ page }) => {
|
||||
await setupWorkspaceUser(page);
|
||||
await page.goto(`/#/workspace/${anyProjectId}/${anyFileId}?page-id=${anyPageId}`);
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const ws = await WebSocket.waitForURL("ws://0.0.0.0:3500/ws/notifications");
|
||||
ws.mockOpen();
|
||||
});
|
||||
|
||||
await expect(page.getByTestId("page-name")).toHaveText("Page 1");
|
||||
|
||||
await page.evaluate(
|
||||
async ({ presenceFixture }) => {
|
||||
const ws = await WebSocket.waitForURL("ws://0.0.0.0:3500/ws/notifications");
|
||||
ws.mockMessage(JSON.stringify(presenceFixture));
|
||||
},
|
||||
{ presenceFixture },
|
||||
);
|
||||
|
||||
expect(page.getByTestId("active-users-list").getByAltText("Princesa Leia")).toHaveCount(2);
|
||||
|
||||
await page.evaluate(async () => {
|
||||
const ws = await WebSocket.waitForURL("ws://0.0.0.0:3500/ws/notifications");
|
||||
ws.mockClose();
|
||||
});
|
||||
});
|
|
@ -162,6 +162,7 @@
|
|||
(assoc :text-color "#000000")))
|
||||
|
||||
(update-presence [presence]
|
||||
(js/console.log "🥰 WIIIIII" (clj->js presence))
|
||||
(-> presence
|
||||
(update session-id update-session presence)
|
||||
(d/without-nils)))]
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
:class (stl/css :active-users-opened)
|
||||
:on-click on-close
|
||||
:on-blur on-close}
|
||||
[:ul {:class (stl/css :active-users-list)}
|
||||
[:ul {:class (stl/css :active-users-list) :data-testid "active-users-list"}
|
||||
(for [session sessions]
|
||||
[:& session-widget
|
||||
{:color (:color session)
|
||||
|
@ -66,7 +66,7 @@
|
|||
|
||||
[:button {:class (stl/css-case :active-users true)
|
||||
:on-click on-open}
|
||||
[:ul {:class (stl/css :active-users-list)}
|
||||
[:ul {:class (stl/css :active-users-list) :data-testid "active-users-list"}
|
||||
(when (> num-sessions 2)
|
||||
[:span {:class (stl/css :users-num)} (dm/str "+" (- num-sessions 2))])
|
||||
|
||||
|
|
|
@ -144,7 +144,7 @@
|
|||
:auto-focus true
|
||||
:default-value (:name page "")}]]
|
||||
[:*
|
||||
[:span {:class (stl/css :page-name)}
|
||||
[:span {:class (stl/css :page-name) :data-testid "page-name"}
|
||||
(:name page)]
|
||||
[:div {:class (stl/css :page-actions)}
|
||||
(when (and deletable? (not workspace-read-only?))
|
||||
|
|
Loading…
Add table
Reference in a new issue