From 1c6ddd62a546788710f51a3ba6a508fd3ae3ddf1 Mon Sep 17 00:00:00 2001 From: Hoang Pham Date: Wed, 17 Dec 2025 15:30:58 +0700 Subject: [PATCH 1/2] fix version viewer regression Signed-off-by: Hoang Pham --- playwright/e2e/version-preview.spec.ts | 278 ++++++++++++++++++++++++- playwright/support/utils.ts | 193 +++++++++++++++-- src/main.ts | 90 +++++--- 3 files changed, 509 insertions(+), 52 deletions(-) diff --git a/playwright/e2e/version-preview.spec.ts b/playwright/e2e/version-preview.spec.ts index 378dbe00..b0b7a08f 100644 --- a/playwright/e2e/version-preview.spec.ts +++ b/playwright/e2e/version-preview.spec.ts @@ -4,24 +4,263 @@ */ import { Buffer } from 'buffer' -import { expect } from '@playwright/test' +import { expect, type Page } from '@playwright/test' import { test } from '../support/fixtures/random-user' import { addTextElement, captureBoardAuthFromSave, createWhiteboard, fetchBoardContent, - getBoardAuth, openFilesApp, resolveStoredFileName, waitForCanvas, } from '../support/utils' +const versionPropfindBody = ` + + + + +` + +const extractVersionIds = (xml: string, userId: string, fileId: number | string): string[] => { + const prefix = `/remote.php/dav/versions/${userId}/versions/${fileId}/` + const hrefRegex = /<[^:>]*:href>([^<]+)<\/[^:>]*:href>/g + const versionIds = new Set() + let match: RegExpExecArray | null = null + + while ((match = hrefRegex.exec(xml)) !== null) { + const href = decodeURIComponent(match[1]) + const index = href.indexOf(prefix) + if (index === -1) { + continue + } + const remainder = href.slice(index + prefix.length).replace(/\/$/, '') + if (remainder) { + versionIds.add(remainder) + } + } + + return [...versionIds] +} + +const findVersionByContent = async ( + page: Page, + options: { + origin: string + userId: string + fileId: number + includeText: string + excludeText?: string + }, +): Promise<{ versionId: string, versionSource: string }> => { + const { origin, userId, fileId, includeText, excludeText } = options + const listUrl = `${origin}/remote.php/dav/versions/${userId}/versions/${fileId}` + const maxAttempts = 20 + const requestToken = await page.evaluate(() => (window as any).OC?.requestToken + || (document.querySelector('head meta[name="requesttoken"]') as HTMLMetaElement | null)?.content + || null) + const versionHeaders = { + Depth: '1', + Accept: 'application/xml', + 'Content-Type': 'application/xml', + ...(requestToken ? { requesttoken: requestToken } : {}), + 'X-Requested-With': 'XMLHttpRequest', + } + + for (let attempt = 0; attempt < maxAttempts; attempt++) { + const response = await page.request.fetch(listUrl, { + method: 'PROPFIND', + headers: versionHeaders, + data: versionPropfindBody, + }) + + if (!response.ok()) { + throw new Error(`Version list request failed with status ${response.status()}`) + } + + const xml = await response.text() + const versionIds = extractVersionIds(xml, userId, fileId) + + for (const versionId of versionIds) { + const versionSource = `/remote.php/dav/versions/${userId}/versions/${fileId}/${versionId}` + const versionResponse = await page.request.get(`${origin}${versionSource}`, { + headers: { + ...(requestToken ? { requesttoken: requestToken } : {}), + 'X-Requested-With': 'XMLHttpRequest', + }, + }) + if (!versionResponse.ok()) { + continue + } + const rawContent = await versionResponse.text() + if (!rawContent.includes(includeText)) { + continue + } + if (excludeText && rawContent.includes(excludeText)) { + continue + } + return { versionId, versionSource } + } + + await page.waitForTimeout(1000) + } + + throw new Error(`No matching version found for ${includeText}`) +} + +const waitForBoardContent = async (page: Page, auth: { fileId: number, jwt: string }, text: string) => { + await expect.poll(async () => JSON.stringify(await fetchBoardContent(page, auth)), { + timeout: 20000, + interval: 500, + }).toContain(text) +} + +const prepareVersionScenario = async ( + page: Page, + userId: string, + options: { boardName: string, initialText: string, updatedText: string }, +) => { + const { boardName, initialText, updatedText } = options + await createWhiteboard(page, { name: boardName }) + const authPromise = captureBoardAuthFromSave(page, { containsText: initialText }) + await addTextElement(page, initialText) + const { fileId, jwt } = await authPromise + const baseAuth = { fileId, jwt } + await waitForBoardContent(page, baseAuth, initialText) + + await addTextElement(page, updatedText, { x: 720, y: 520 }) + await waitForBoardContent(page, baseAuth, updatedText) + + const origin = new URL(await page.url()).origin + const versionEntry = await findVersionByContent(page, { + origin, + userId, + fileId: baseAuth.fileId, + includeText: initialText, + excludeText: updatedText, + }) + + await openFilesApp(page) + const storedName = await resolveStoredFileName(page, boardName) + + return { baseAuth, origin, versionEntry, storedName } +} + +const openWhiteboardInViewer = async ( + page: Page, + options: { fileId: number, fileName: string, source?: string | null, fileVersion?: string | null }, +) => { + const filePath = options.fileName.startsWith('/') ? options.fileName : `/${options.fileName}` + await page.waitForFunction(() => Boolean((window as any).OCA?.Viewer?.openWith), { timeout: 10000 }) + await page.evaluate(({ fileId, filePathValue, fileName, source, fileVersion }) => { + const viewer = (window as any).OCA?.Viewer + if (!viewer?.openWith) { + throw new Error('Viewer openWith unavailable') + } + viewer.openWith('whiteboard', { + fileInfo: { + fileid: Number(fileId), + filename: filePathValue, + basename: fileName, + source: source ?? null, + fileVersion: fileVersion ?? null, + mime: 'application/vnd.excalidraw+json', + size: 0, + type: 'file', + }, + enableSidebar: false, + }) + }, { + fileId: options.fileId, + filePathValue: filePath, + fileName: options.fileName, + source: options.source ?? null, + fileVersion: options.fileVersion ?? null, + }) +} + test.beforeEach(async ({ page }) => { await openFilesApp(page) }) -test('version preview params still load board content', async ({ page, user }) => { +test('version preview banner shows and exits to live board', async ({ + page, + user, +}) => { + test.setTimeout(120000) + const boardName = `Version preview ${Date.now()}` + const initialText = 'Version one' + const updatedText = 'Version two' + + const { baseAuth, versionEntry, storedName } = await prepareVersionScenario(page, user.userId, { + boardName, + initialText, + updatedText, + }) + await openWhiteboardInViewer(page, { + fileId: baseAuth.fileId, + fileName: storedName, + source: versionEntry.versionSource, + fileVersion: versionEntry.versionId, + }) + await waitForCanvas(page) + + const banner = page.locator('.version-preview-banner') + await expect(banner).toBeVisible({ timeout: 20000 }) + await expect(page.getByRole('button', { name: 'Restore this version' })).toBeVisible() + + const backButton = page.getByRole('button', { name: 'Back to latest version' }) + await expect(backButton).toBeVisible() + await backButton.click() + + await expect(banner).toBeHidden({ timeout: 20000 }) + await waitForBoardContent(page, baseAuth, updatedText) +}) + +test('restore version replaces current content', async ({ + page, + user, +}) => { + test.setTimeout(120000) + const boardName = `Version restore ${Date.now()}` + const initialText = 'Restore one' + const updatedText = 'Restore two' + + const { baseAuth, versionEntry, storedName } = await prepareVersionScenario(page, user.userId, { + boardName, + initialText, + updatedText, + }) + await openWhiteboardInViewer(page, { + fileId: baseAuth.fileId, + fileName: storedName, + source: versionEntry.versionSource, + fileVersion: versionEntry.versionId, + }) + await waitForCanvas(page) + + const restoreButton = page.getByRole('button', { name: 'Restore this version' }) + await expect(restoreButton).toBeVisible() + await restoreButton.click() + + const banner = page.locator('.version-preview-banner') + await expect(banner).toBeHidden({ timeout: 20000 }) + + await expect.poll(async () => JSON.stringify(await fetchBoardContent(page, baseAuth)), { + timeout: 30000, + interval: 500, + }).toContain(initialText) + await expect.poll(async () => JSON.stringify(await fetchBoardContent(page, baseAuth)), { + timeout: 30000, + interval: 500, + }).not.toContain(updatedText) +}) + +test('version preview params still load board content', async ({ + page, + user, +}) => { test.setTimeout(90000) const boardName = `Version preview ${Date.now()}` @@ -32,7 +271,9 @@ test('version preview params still load board content', async ({ page, user }) = try { return await getBoardAuth(page) } catch { - const { fileId, jwt } = await captureBoardAuthFromSave(page, { containsText: 'Live content' }) + const { fileId, jwt } = await captureBoardAuthFromSave(page, { + containsText: 'Live content', + }) return { fileId, jwt } } } @@ -52,19 +293,34 @@ test('version preview params still load board content', async ({ page, user }) = const escapedName = storedName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const row = page.getByRole('row', { name: new RegExp(escapedName, 'i') }) await expect(row).toBeVisible({ timeout: 30000 }) - await row.click() + await openWhiteboardInViewer(page, { + fileId: baseAuth.fileId, + fileName: storedName, + source: versionSource, + fileVersion: '1.0', + }) await waitForCanvas(page) - const tokenResponse = await page.request.get(`apps/whiteboard/${baseAuth.fileId}/token`) + const tokenResponse = await page.request.get( + `apps/whiteboard/${baseAuth.fileId}/token`, + ) expect(tokenResponse.ok()).toBeTruthy() const token = (await tokenResponse.json()).token const previewAuth = { fileId: baseAuth.fileId, jwt: token } - const payload = JSON.parse(Buffer.from(token.split('.')[1], 'base64').toString()) + const payload = JSON.parse( + Buffer.from(token.split('.')[1], 'base64').toString(), + ) expect(payload?.isFileReadOnly).toBeFalsy() - await expect.poll(async () => JSON.stringify(await fetchBoardContent(page, previewAuth)), { - timeout: 20000, - interval: 500, - }).toContain('Live content') + await expect + .poll( + async () => + JSON.stringify(await fetchBoardContent(page, previewAuth)), + { + timeout: 20000, + interval: 500, + }, + ) + .toContain('Live content') }) diff --git a/playwright/support/utils.ts b/playwright/support/utils.ts index 71b38d0c..c2847ed5 100644 --- a/playwright/support/utils.ts +++ b/playwright/support/utils.ts @@ -6,6 +6,13 @@ import { expect } from '@playwright/test' import type { Browser, Page } from '@playwright/test' +const fileIdPropfindBody = ` + + + + +` + export async function openFilesApp(page: Page) { await page.goto('apps/files') await page.waitForURL(/apps\/files/) @@ -111,8 +118,10 @@ export async function addTextElement(page: Page, text: string, point: Point = { export async function openWhiteboardFromFiles(page: Page, name: string, options: OpenWhiteboardFromFilesOptions = {}) { const escaped = name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') const viewOrder = options.preferSharedView - ? ['apps/files?view=sharingin', 'apps/files/shareoverview', 'apps/files'] + ? ['apps/files/sharingin', 'apps/files?view=sharingin', 'apps/files/shareoverview', 'apps/files'] : ['apps/files', 'apps/files/shareoverview'] + let activeViewId = 'files' + let activeDir = '/' const attemptFindEntry = async () => { const searchBox = page.getByRole('searchbox', { name: /search here/i }).first() @@ -140,6 +149,11 @@ export async function openWhiteboardFromFiles(page: Page, name: string, options: const visitView = async (path: string) => { await page.goto(path) await page.waitForURL(/apps\/files/, { timeout: 20000 }).catch(() => {}) + const currentUrl = new URL(await page.url()) + const viewParam = currentUrl.searchParams.get('view') + const viewSegment = currentUrl.pathname.split('/').filter(Boolean).pop() || 'files' + activeViewId = viewParam || (viewSegment === 'files' ? 'files' : viewSegment) + activeDir = currentUrl.searchParams.get('dir') || '/' return attemptFindEntry() } @@ -151,26 +165,122 @@ export async function openWhiteboardFromFiles(page: Page, name: string, options: } } - if (!entry) { throw new Error(`Whiteboard file not found: ${name}`) } await expect(entry).toBeVisible({ timeout: 15000 }) + await entry.scrollIntoViewIfNeeded() - const viewButton = entry.getByRole('button', { name: /view|open/i }).first() - if (await viewButton.count()) { - await viewButton.click() + const resolvedFileId = await entry.evaluate((row) => { + const element = row as HTMLElement + const direct = element.getAttribute('data-cy-files-list-row-fileid') + || element.getAttribute('data-fileid') + || element.getAttribute('data-id') + if (direct) { + return direct + } + const nested = element.querySelector('[data-cy-files-list-row-fileid], [data-fileid], [data-id]') as HTMLElement | null + return nested?.getAttribute('data-cy-files-list-row-fileid') + || nested?.getAttribute('data-fileid') + || nested?.getAttribute('data-id') + || null + }) + const resolvedFileName = await entry.evaluate((row) => { + const element = row as HTMLElement + const direct = element.getAttribute('data-cy-files-list-row-name') + || element.getAttribute('data-entryname') + || element.getAttribute('data-file') + if (direct) { + return direct + } + const ariaLabel = element.getAttribute('aria-label') || '' + const ariaMatch = ariaLabel.match(/file \"([^\"]+)\"/) + if (ariaMatch?.[1]) { + return ariaMatch[1] + } + const text = element.textContent || '' + const textMatch = text.match(/([\w\s.-]+\.(whiteboard|excalidraw))/i) + if (textMatch?.[1]) { + return textMatch[1] + } + return null + }) + + const openViaViewer = async () => { + const fileNameToOpen = resolvedFileName || name + if (!fileNameToOpen) { + return false + } + const normalizedDir = activeDir && activeDir !== '/' ? activeDir.replace(/\/$/, '') : '' + const filePath = normalizedDir ? `${normalizedDir}/${fileNameToOpen}` : `/${fileNameToOpen}` + await page.waitForFunction(() => Boolean((window as any).OCA?.Viewer), { timeout: 10000 }).catch(() => {}) + const result = await page.evaluate(({ path }) => { + const viewer = (window as any).OCA?.Viewer + if (!viewer) { + return { ok: false, reason: 'viewer-missing' } + } + const handlers = viewer.availableHandlers || [] + const hasWhiteboard = Array.isArray(handlers) && handlers.some((handler) => handler?.id === 'whiteboard') + if (viewer.openWith && hasWhiteboard) { + viewer.openWith('whiteboard', { path }) + return { ok: true } + } + if (viewer.open) { + viewer.open({ path }) + return { ok: true } + } + return { ok: false, reason: 'open-missing' } + }, { path: filePath }) + return Boolean(result?.ok) + } + + const nameLink = entry.locator('[data-cy-files-list-row-name-link]').first() + if (await nameLink.count()) { + await nameLink.click() } else { - const target = entry.getByRole('link', { name: new RegExp(escaped, 'i') }).first() - if (await target.count()) { - await target.click() + const viewButton = entry.getByRole('button', { name: /view|open/i }).first() + if (await viewButton.count()) { + await viewButton.click() } else { - await entry.dblclick() + const target = entry.getByRole('link', { name: new RegExp(escaped, 'i') }).first() + if (await target.count()) { + await target.click() + } else { + const nameCell = entry.locator('[data-cy-files-list-row-name]').first() + if (await nameCell.count()) { + await nameCell.click() + } else { + await entry.click() + } + } } } - await waitForCanvas(page) + try { + await waitForCanvas(page) + } catch (error) { + await entry.dblclick() + try { + await waitForCanvas(page) + } catch (retryError) { + const viewerOpened = await openViaViewer() + if (viewerOpened) { + try { + await waitForCanvas(page) + return + } catch { + // fallback below + } + } + const fallbackFileId = resolvedFileId || await resolveFileIdByDav(page, name) + if (!fallbackFileId) { + throw retryError + } + await openWhiteboardById(page, fallbackFileId, { viewId: activeViewId, dir: activeDir }) + return + } + } } export async function newLoggedInPage(sourcePage: Page, browser: Browser) { @@ -224,11 +334,70 @@ export async function getBoardAuth(page: Page): Promise<{ fileId: number, jwt: s return { fileId, jwt } } -export async function openWhiteboardById(page: Page, fileId: number | string) { - await page.goto(`apps/whiteboard/${fileId}`) +export async function openWhiteboardById( + page: Page, + fileId: number | string, + { viewId = 'files', dir = '/' }: { viewId?: string, dir?: string } = {}, +) { + const normalizedView = viewId.replace(/^\/+/, '').replace(/\/+$/, '') || 'files' + const dirParam = encodeURIComponent(dir || '/') + await page.goto(`apps/files/${normalizedView}/${fileId}?dir=${dirParam}&openfile=true`) await waitForCanvas(page) } +async function resolveFileIdByDav(page: Page, name: string): Promise { + 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' }, + }) + if (!userResponse.ok()) { + return null + } + const userPayload = await userResponse.json().catch(() => null) + const userId = userPayload?.ocs?.data?.id + if (!userId) { + return null + } + + const requestToken = await page.evaluate(() => (window as any).OC?.requestToken + || (document.querySelector('head meta[name="requesttoken"]') as HTMLMetaElement | null)?.content + || null) + + const candidates = (() => { + const lower = name.toLowerCase() + if (lower.endsWith('.whiteboard') || lower.endsWith('.excalidraw')) { + return [name] + } + return [`${name}.whiteboard`, `${name}.excalidraw`, name] + })() + + for (const candidate of candidates) { + const filePath = encodeURIComponent(candidate) + const response = await page.request.fetch(`${origin}/remote.php/dav/files/${userId}/${filePath}`, { + method: 'PROPFIND', + headers: { + Depth: '0', + Accept: 'application/xml', + 'Content-Type': 'application/xml', + ...(requestToken ? { requesttoken: requestToken } : {}), + 'X-Requested-With': 'XMLHttpRequest', + }, + data: fileIdPropfindBody, + }) + + if (!response.ok()) { + continue + } + const xml = await response.text() + const match = xml.match(/<(?:oc:)?fileid>([^<]+)<\/(?:oc:)?fileid>/) + if (match?.[1]) { + return match[1] + } + } + + return null +} + export async function fetchBoardContent(page: Page, auth: { fileId: number | string, jwt: string }) { const token = auth.jwt.startsWith('Bearer ') ? auth.jwt : `Bearer ${auth.jwt}` const maxAttempts = 5 diff --git a/src/main.ts b/src/main.ts index 50226e4c..6f77b6da 100644 --- a/src/main.ts +++ b/src/main.ts @@ -12,7 +12,10 @@ import { getSharingToken, isPublicShare } from '@nextcloud/sharing/public' import './styles/index.scss' import logger from './utils/logger' -import { matchesComparisonRequest, renderWhiteboardView } from './utils/renderWhiteboardView' +import { + matchesComparisonRequest, + renderWhiteboardView, +} from './utils/renderWhiteboardView' import type { WhiteboardRootHandle } from './utils/renderWhiteboardView' declare global { @@ -65,7 +68,9 @@ const bootstrapWhiteboardRuntime = (): void => { } const detectRuntime = (): RuntimeDescriptor => { - const fileId = normalizeNumericState(loadState('whiteboard', 'file_id', '0')) + const fileId = normalizeNumericState( + loadState('whiteboard', 'file_id', '0'), + ) const collabBackendUrl = loadState('whiteboard', 'collabBackendUrl', '') if (loadState('whiteboard', 'isRecording', false)) { @@ -220,15 +225,16 @@ type ViewerComponentOptions = { type WhiteboardComponentData = { root: WhiteboardRootHandle | null } -type WhiteboardComponentInstance = Vue & WhiteboardComponentData & { - fileid?: number | null - fileId?: number | null - fileVersion?: string | null - source?: string | null - isEmbedded?: boolean - isComparisonView?: boolean - basename?: string -} +type WhiteboardComponentInstance = Vue & + WhiteboardComponentData & { + fileid?: number | null + fileId?: number | null + fileVersion?: string | null + source?: string | null + isEmbedded?: boolean + isComparisonView?: boolean + basename?: string + } type VueComponentDefinition = ComponentOptions & { data: () => WhiteboardComponentData @@ -277,9 +283,14 @@ type WindowWithViewer = Window & { } } -const createWhiteboardComponent = (options: ViewerComponentOptions): VueComponentDefinition => ({ +const createWhiteboardComponent = ( + options: ViewerComponentOptions, +): VueComponentDefinition => ({ name: 'Whiteboard', - render(this: WhiteboardComponentInstance, createElement: CreateElement): VNode { + render( + this: WhiteboardComponentInstance, + createElement: CreateElement, + ): VNode { this.$emit('update:loaded', true) const containerId = generateWhiteboardElementId() @@ -289,26 +300,41 @@ const createWhiteboardComponent = (options: ViewerComponentOptions): VueComponen return } - rootElement.addEventListener('keydown', event => { + rootElement.addEventListener('keydown', (event) => { if (event.key === 'Escape') { event.stopPropagation() } }) - const normalizedFileId = Number(this.fileid ?? this.fileId ?? 0) || 0 + const normalizedFileId + = Number(this.fileid ?? this.fileId ?? 0) || 0 const isComparisonView = Boolean(this.isComparisonView) const isEmbedded = Boolean(this.isEmbedded) const rawVersionSource = this.source ?? null const rawFileVersion = this.fileVersion ?? null - const shouldUseVersionPreview = isComparisonView - || matchesComparisonRequest(rawVersionSource, rawFileVersion ?? null) + const isVersionsDavSource + = rawVersionSource?.includes('/dav/versions/') + || rawVersionSource?.includes('/dav/trashbin/') + || false + const shouldUseVersionPreview + = isComparisonView + || (rawFileVersion !== null && isVersionsDavSource) + || matchesComparisonRequest( + rawVersionSource, + rawFileVersion ?? null, + ) const versionSource = isEmbedded ? rawVersionSource - : (shouldUseVersionPreview ? rawVersionSource : null) + : shouldUseVersionPreview + ? rawVersionSource + : null const fileVersion = isEmbedded ? rawFileVersion - : (shouldUseVersionPreview ? rawFileVersion : null) - const fileName = typeof this.basename === 'string' ? this.basename : '' + : shouldUseVersionPreview + ? rawFileVersion + : null + const fileName + = typeof this.basename === 'string' ? this.basename : '' this.root = renderWhiteboardView(rootElement, { fileId: normalizedFileId, @@ -328,7 +354,11 @@ const createWhiteboardComponent = (options: ViewerComponentOptions): VueComponen attrs: { id: containerId }, class: [ 'whiteboard', - { 'whiteboard-viewer__embedding': Boolean(this.isEmbedded) }, + { + 'whiteboard-viewer__embedding': Boolean( + this.isEmbedded, + ), + }, ], }, '', @@ -380,7 +410,8 @@ const registerViewerHandler = ( ) } -const getViewerApi = (): ViewerApi | undefined => (window as WindowWithViewer).OCA?.Viewer +const getViewerApi = (): ViewerApi | undefined => + (window as WindowWithViewer).OCA?.Viewer const runWhenDomReady = (callback: () => void | Promise): void => { if (document.readyState === 'loading') { @@ -395,7 +426,10 @@ const runWhenDomReady = (callback: () => void | Promise): void => { callback() } -const primeRecordingJwt = async (fileId: number, jwt: string): Promise => { +const primeRecordingJwt = async ( + fileId: number, + jwt: string, +): Promise => { if (!jwt) { return } @@ -426,12 +460,10 @@ const normalizeNumericState = (value: unknown): number => { } const generateWhiteboardElementId = () => - `whiteboard-${ - Math.random() - .toString(36) - .replace(/[^a-z]+/g, '') - .substr(2, 10) - }` + `whiteboard-${Math.random() + .toString(36) + .replace(/[^a-z]+/g, '') + .substr(2, 10)}` const createWhiteboardElement = (id = generateWhiteboardElementId()) => { const element = document.createElement('div') From f7fcdc1b59ca41f2ff7689b2fcdf84a0368007a7 Mon Sep 17 00:00:00 2001 From: Hoang Pham Date: Mon, 12 Jan 2026 19:13:54 +0700 Subject: [PATCH 2/2] test: fix version preview polling Signed-off-by: Hoang Pham --- playwright/e2e/version-preview.spec.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/playwright/e2e/version-preview.spec.ts b/playwright/e2e/version-preview.spec.ts index b0b7a08f..b4ca7512 100644 --- a/playwright/e2e/version-preview.spec.ts +++ b/playwright/e2e/version-preview.spec.ts @@ -11,6 +11,7 @@ import { captureBoardAuthFromSave, createWhiteboard, fetchBoardContent, + getBoardAuth, openFilesApp, resolveStoredFileName, waitForCanvas, @@ -112,7 +113,7 @@ const findVersionByContent = async ( const waitForBoardContent = async (page: Page, auth: { fileId: number, jwt: string }, text: string) => { await expect.poll(async () => JSON.stringify(await fetchBoardContent(page, auth)), { timeout: 20000, - interval: 500, + intervals: [500], }).toContain(text) } @@ -249,11 +250,11 @@ test('restore version replaces current content', async ({ await expect.poll(async () => JSON.stringify(await fetchBoardContent(page, baseAuth)), { timeout: 30000, - interval: 500, + intervals: [500], }).toContain(initialText) await expect.poll(async () => JSON.stringify(await fetchBoardContent(page, baseAuth)), { timeout: 30000, - interval: 500, + intervals: [500], }).not.toContain(updatedText) }) @@ -319,7 +320,7 @@ test('version preview params still load board content', async ({ JSON.stringify(await fetchBoardContent(page, previewAuth)), { timeout: 20000, - interval: 500, + intervals: [500], }, ) .toContain('Live content')