Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
358 changes: 335 additions & 23 deletions playwright/e2e/collaboration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,367 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { expect } from '@playwright/test'
import type { Page } from '@playwright/test'
import { test } from '../support/fixtures/random-user'
import {
addTextElement,
createWhiteboard,
newLoggedInPage,
openWhiteboardById,
openFilesApp,
openWhiteboardFromFiles,
fetchBoardContent,
getBoardAuth,
resolveFileIdByDav,
} from '../support/utils'

type CapturedSceneMessage = {
transport: 'room' | 'direct'
type: string
syncAll: boolean
elementsCount: number
}

type ReceivedSceneMessage = {
type: string
syncAll: boolean
elementsCount: number
}

type CollaborationSocketHook = {
connected?: boolean
emit: (eventName: string, ...args: unknown[]) => unknown
}

type WhiteboardTestHooks = {
collaborationStore?: {
getState?: () => {
socket?: CollaborationSocketHook
isInRoom?: boolean
}
}
excalidrawStore?: {
getState?: () => {
excalidrawAPI?: {
getSceneElementsIncludingDeleted?: () => Array<{
type?: string
text?: string
isDeleted?: boolean
}>
}
}
}
emittedSceneMessages?: CapturedSceneMessage[]
receivedSceneMessages?: ReceivedSceneMessage[]
sceneEmitSpyInstalled?: boolean
sceneReceiveSpyInstalled?: boolean
}

type WhiteboardTestWindow = Window & {
__whiteboardTest?: boolean
__whiteboardTestHooks?: WhiteboardTestHooks
}

async function enableWhiteboardTestHooks(page: Page) {
await page.addInitScript(() => {
const win = window as WhiteboardTestWindow
win.__whiteboardTest = true
win.__whiteboardTestHooks = win.__whiteboardTestHooks || {}
win.__whiteboardTestHooks.emittedSceneMessages = []
})
await page.evaluate(() => {
const win = window as WhiteboardTestWindow
win.__whiteboardTest = true
win.__whiteboardTestHooks = win.__whiteboardTestHooks || {}
win.__whiteboardTestHooks.emittedSceneMessages = win.__whiteboardTestHooks.emittedSceneMessages || []
})
}

async function waitForCollaborationReady(page: Page) {
await page.waitForFunction(() => {
const win = window as WhiteboardTestWindow
const store = win.__whiteboardTestHooks?.collaborationStore
const state = store?.getState?.()
return Boolean(state?.socket?.connected && state?.isInRoom)
}, { timeout: 30000 })
}

async function installSceneEmitSpy(page: Page) {
await waitForCollaborationReady(page)
await page.evaluate(() => {
const win = window as WhiteboardTestWindow
win.__whiteboardTestHooks = win.__whiteboardTestHooks || {}
const hooks = win.__whiteboardTestHooks
const emittedSceneMessages = hooks.emittedSceneMessages || []
hooks.emittedSceneMessages = emittedSceneMessages
if (hooks.sceneEmitSpyInstalled) {
return
}

const store = hooks.collaborationStore
const socket = store?.getState?.().socket
if (!socket) {
throw new Error('Collaboration socket not available')
}

const decodePayload = (payload: unknown) => {
if (typeof payload === 'string') {
return payload
}
if (payload instanceof ArrayBuffer) {
return new TextDecoder().decode(new Uint8Array(payload))
}
if (ArrayBuffer.isView(payload)) {
return new TextDecoder().decode(
new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength),
)
}
return ''
}

const originalEmit = socket.emit.bind(socket)
socket.emit = (eventName: string, ...args: unknown[]) => {
if (eventName === 'server-broadcast' || eventName === 'server-direct-broadcast') {
const decoded = decodePayload(eventName === 'server-direct-broadcast' ? args[2] : args[1])
if (decoded) {
try {
const parsed = JSON.parse(decoded)
if (parsed?.type === 'SCENE_INIT' || parsed?.type === 'SCENE_UPDATE') {
emittedSceneMessages.push({
transport: eventName === 'server-direct-broadcast' ? 'direct' : 'room',
type: parsed.type,
syncAll: parsed.payload?.syncAll === true,
elementsCount: Array.isArray(parsed.payload?.elements)
? parsed.payload.elements.length
: 0,
})
}
} catch {
// Ignore frames that are not JSON scene payloads.
}
}
}
return originalEmit(eventName, ...args)
}

hooks.sceneEmitSpyInstalled = true
})
}

async function clearCapturedSceneMessages(page: Page) {
await page.evaluate(() => {
const win = window as WhiteboardTestWindow
if (win.__whiteboardTestHooks) {
win.__whiteboardTestHooks.emittedSceneMessages = []
}
})
}

async function getCapturedSceneMessages(page: Page): Promise<CapturedSceneMessage[]> {
return page.evaluate(() => {
const win = window as WhiteboardTestWindow
return win.__whiteboardTestHooks?.emittedSceneMessages || []
})
}

async function installSceneReceiveSpy(page: Page) {
await waitForCollaborationReady(page)
await page.evaluate(() => {
const win = window as WhiteboardTestWindow
win.__whiteboardTestHooks = win.__whiteboardTestHooks || {}
const hooks = win.__whiteboardTestHooks
const receivedSceneMessages = hooks.receivedSceneMessages || []
hooks.receivedSceneMessages = receivedSceneMessages
if (hooks.sceneReceiveSpyInstalled) {
return
}

const store = hooks.collaborationStore
const socket = store?.getState?.().socket
if (!socket) {
throw new Error('Collaboration socket not available')
}

const decodePayload = (payload: unknown) => {
if (typeof payload === 'string') {
return payload
}
if (payload instanceof ArrayBuffer) {
return new TextDecoder().decode(new Uint8Array(payload))
}
if (ArrayBuffer.isView(payload)) {
return new TextDecoder().decode(
new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength),
)
}
return ''
}

socket.onAny((eventName: string, ...args: unknown[]) => {
if (eventName !== 'client-broadcast') {
return
}

const decoded = decodePayload(args[0])
if (!decoded) {
return
}

try {
const parsed = JSON.parse(decoded)
if (parsed?.type === 'SCENE_INIT' || parsed?.type === 'SCENE_UPDATE') {
receivedSceneMessages.push({
type: parsed.type,
syncAll: parsed.payload?.syncAll === true,
elementsCount: Array.isArray(parsed.payload?.elements)
? parsed.payload.elements.length
: 0,
})
}
} catch {
// Ignore frames that are not JSON scene payloads.
}
})

hooks.sceneReceiveSpyInstalled = true
})
}

async function clearReceivedSceneMessages(page: Page) {
await page.evaluate(() => {
const win = window as WhiteboardTestWindow
if (win.__whiteboardTestHooks) {
win.__whiteboardTestHooks.receivedSceneMessages = []
}
})
}

async function getReceivedSceneMessages(page: Page): Promise<ReceivedSceneMessage[]> {
return page.evaluate(() => {
const win = window as WhiteboardTestWindow
return win.__whiteboardTestHooks?.receivedSceneMessages || []
})
}

async function resolveBoardFileId(page: Page, boardName: string): Promise<string> {
await expect.poll(async () => resolveFileIdByDav(page, boardName), {
timeout: 30000,
intervals: [500],
}).not.toBeNull()

const fileId = await resolveFileIdByDav(page, boardName)
if (!fileId) {
throw new Error(`Failed to resolve file id for board: ${boardName}`)
}
return fileId
}

test.beforeEach(async ({ page }) => {
await openFilesApp(page)
})

test('whiteboard changes sync across sessions', async ({ page, browser, user }) => {
test('whiteboard changes sync across sessions', async ({ page, browser }) => {
test.setTimeout(90000)
const boardName = `Collab board ${Date.now()}`

await enableWhiteboardTestHooks(page)
await createWhiteboard(page, { name: boardName })
await addTextElement(page, 'First session text')
const fileId = await resolveBoardFileId(page, boardName)
await installSceneReceiveSpy(page)
await clearReceivedSceneMessages(page)

let auth
try {
auth = await getBoardAuth(page)
} catch {
const saveResp = await page.waitForResponse((response) => response.request().method() === 'PUT' && response.url().includes('/apps/whiteboard/'), { timeout: 60000 })
const authHeader = saveResp.request().headers()['authorization'] || ''
const apiPath = new URL(saveResp.url()).pathname.replace('/index.php/', '')
const fileId = Number(apiPath.split('/').pop())
auth = { fileId, jwt: authHeader }
}
const pageB = await newLoggedInPage(page, browser)
await enableWhiteboardTestHooks(pageB)
await openWhiteboardById(pageB, fileId)

await expect.poll(async () => JSON.stringify(await fetchBoardContent(page, auth)), {
timeout: 20000,
interval: 500,
}).toContain('First session text')
await addTextElement(pageB, 'Second session text')
await page.waitForFunction(() => {

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud master

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud master

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud master

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable32

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable32

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable32

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable31

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable31

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable31

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable30

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable30

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable30

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable29

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #2 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable29

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13

Check failure on line 278 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable29

[chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions

1) [chromium] › playwright/e2e/collaboration.spec.ts:262:1 › whiteboard changes sync across sessions Error: page.waitForFunction: Target page, context or browser has been closed 276 | 277 | await addTextElement(pageB, 'Second session text') > 278 | await page.waitForFunction(() => { | ^ 279 | const win = window as WhiteboardTestWindow 280 | const messages = win.__whiteboardTestHooks?.receivedSceneMessages || [] 281 | return messages.some((message: ReceivedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:278:13
const win = window as WhiteboardTestWindow
const messages = win.__whiteboardTestHooks?.receivedSceneMessages || []
return messages.some((message: ReceivedSceneMessage) => (
message.type === 'SCENE_UPDATE'
&& message.syncAll === false
&& message.elementsCount === 1
))
}, { timeout: 30000 })

const receivedMessages = await getReceivedSceneMessages(page)
expect(receivedMessages.some((message) => (
message.type === 'SCENE_UPDATE'
&& message.syncAll === false
&& message.elementsCount === 1
))).toBe(true)

await pageB.close()
})

test('incremental scene sync sends only changed elements after targeted bootstrap', async ({ page, browser }) => {
test.setTimeout(120000)
const boardName = `Incremental collab board ${Date.now()}`
const bootstrapText = 'Incremental bootstrap text'
const deltaText = 'Incremental delta text'

await enableWhiteboardTestHooks(page)
await createWhiteboard(page, { name: boardName })
await addTextElement(page, bootstrapText)
const fileId = await resolveBoardFileId(page, boardName)
await installSceneEmitSpy(page)
await clearCapturedSceneMessages(page)
await installSceneReceiveSpy(page)
await clearReceivedSceneMessages(page)

const pageB = await newLoggedInPage(page, browser)
await openWhiteboardFromFiles(pageB, boardName)
const fetchContent = async (targetPage) => JSON.stringify(await fetchBoardContent(targetPage, auth))
await enableWhiteboardTestHooks(pageB)
await openWhiteboardById(pageB, fileId)

await expect.poll(async () => fetchContent(pageB), { timeout: 20000, interval: 500 }).toContain('First session text')
await page.waitForFunction(() => {

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud master

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud master

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable32

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable32

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable31

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable31

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable30

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable30

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable29

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Retry #1 ─────────────────────────────────────────────────────────────────────────────────────── Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13

Check failure on line 317 in playwright/e2e/collaboration.spec.ts

View workflow job for this annotation

GitHub Actions / Playwright Tests on Nextcloud stable29

[chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap

2) [chromium] › playwright/e2e/collaboration.spec.ts:298:1 › incremental scene sync sends only changed elements after targeted bootstrap Error: page.waitForFunction: Target page, context or browser has been closed 315 | await openWhiteboardById(pageB, fileId) 316 | > 317 | await page.waitForFunction(() => { | ^ 318 | const win = window as WhiteboardTestWindow 319 | const messages = win.__whiteboardTestHooks?.emittedSceneMessages || [] 320 | return messages.some((message: CapturedSceneMessage) => ( at /home/runner/actions-runner/_work/whiteboard/whiteboard/playwright/e2e/collaboration.spec.ts:317:13
const win = window as WhiteboardTestWindow
const messages = win.__whiteboardTestHooks?.emittedSceneMessages || []
return messages.some((message: CapturedSceneMessage) => (
message.transport === 'direct'
&& message.type === 'SCENE_INIT'
))
}, { timeout: 30000 })

await addTextElement(pageB, 'Second session text')
const bootstrapMessages = await getCapturedSceneMessages(page)
expect(bootstrapMessages.some((message) => (
message.transport === 'direct'
&& message.type === 'SCENE_INIT'
))).toBe(true)
expect(bootstrapMessages.some((message) => (
message.transport === 'room'
&& message.type === 'SCENE_INIT'
))).toBe(false)

await installSceneEmitSpy(pageB)
await clearCapturedSceneMessages(pageB)

await addTextElement(pageB, deltaText)

await pageB.waitForFunction(() => {
const win = window as WhiteboardTestWindow
const messages = win.__whiteboardTestHooks?.emittedSceneMessages || []
return messages.some((message: { type?: string, syncAll?: boolean, elementsCount?: number }) => (
message.type === 'SCENE_UPDATE'
&& message.syncAll === false
&& message.elementsCount === 1
))
}, { timeout: 30000 })

const messages = await getCapturedSceneMessages(pageB)
const incrementalMessages = messages.filter((message) => message.type === 'SCENE_UPDATE')
const receivedMessages = await getReceivedSceneMessages(page)

await expect.poll(async () => fetchContent(page), { timeout: 30000, interval: 500 }).toContain('Second session text')
expect(incrementalMessages.length).toBeGreaterThan(0)
expect(incrementalMessages.some((message) => message.syncAll)).toBe(false)
expect(incrementalMessages.every((message) => message.transport === 'room')).toBe(true)
expect(messages.some((message) => message.type === 'SCENE_INIT')).toBe(false)
expect(incrementalMessages.every((message) => message.elementsCount === 1)).toBe(true)
expect(receivedMessages.some((message) => (
message.type === 'SCENE_UPDATE'
&& message.syncAll === false
&& message.elementsCount === 1
))).toBe(true)

await pageB.close()
})
2 changes: 1 addition & 1 deletion playwright/support/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ export async function openWhiteboardById(
await waitForCanvas(page)
}

async function resolveFileIdByDav(page: Page, name: string): Promise<string | null> {
export async function resolveFileIdByDav(page: Page, name: string): Promise<string | null> {
const origin = new URL(await page.url()).origin
const userResponse = await page.request.get(`${origin}/ocs/v2.php/cloud/user?format=json`, {
headers: { 'OCS-APIREQUEST': 'true' },
Expand Down
Loading
Loading