Files
upstream-mirror a774ac673a
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
2026-09-23 16:12:52 +05:00

384 lines
14 KiB
TypeScript

import fs from 'fs'
import path from 'path'
import { getRandomName, openNewTab } from '../../fixtures/helpers'
import { expect, test } from '../../fixtures/tla-test'
test.beforeEach(async ({ context }) => {
await context.grantPermissions(['clipboard-read', 'clipboard-write'])
})
/* --------------------- Sharing -------------------- */
test.describe('signed in user on own file', () => {
test('default share state', async ({ shareMenu }) => {
await shareMenu.open()
expect(await shareMenu.isToggleChecked()).toBe(true)
expect(await shareMenu.getShareType()).toBe('Editor')
})
test('tabs work correctly', async ({ shareMenu }) => {
await shareMenu.open()
expect(await shareMenu.publishTabButton.isVisible()).toBe(true)
expect(await shareMenu.inviteTabButton.isVisible()).toBe(true)
expect(await shareMenu.exportTabButton.isVisible()).toBe(true)
expect(await shareMenu.anonShareTabButton.isVisible()).toBe(false)
// Starts on the invite tab
expect(await shareMenu.inviteTabPage.isVisible()).toBe(true)
expect(await shareMenu.exportTabPage.isVisible()).toBe(false)
expect(await shareMenu.publishTabPage.isVisible()).toBe(false)
expect(await shareMenu.anonShareTabPage.isVisible()).toBe(false)
// Can switch between tabs (export)
await shareMenu.exportTabButton.click()
expect(await shareMenu.inviteTabPage.isVisible()).toBe(false)
expect(await shareMenu.exportTabPage.isVisible()).toBe(true)
expect(await shareMenu.publishTabPage.isVisible()).toBe(false)
// Can switch between tabs (publish)
await shareMenu.publishTabButton.click()
expect(await shareMenu.inviteTabPage.isVisible()).toBe(false)
expect(await shareMenu.exportTabPage.isVisible()).toBe(false)
expect(await shareMenu.publishTabPage.isVisible()).toBe(true)
})
test('a select inside the menu closes without closing the menu', async ({ page, shareMenu }) => {
await shareMenu.open()
await shareMenu.exportTabButton.click()
await expect(shareMenu.exportTabPage).toBeVisible()
const themeSelect = page.locator('#tla-export-theme-select')
// Selecting an option closes the select but leaves the share menu open.
await themeSelect.click()
await expect(page.getByRole('listbox')).toBeVisible()
await page.getByRole('option').last().click()
await expect(page.getByRole('listbox')).toHaveCount(0)
await expect(shareMenu.exportTabPage).toBeVisible()
// Re-clicking the open select's trigger also closes only the select. The trigger
// is non-interactive while the select is open (Radix disables outside pointer
// events), so click its position directly rather than via the locator.
await themeSelect.click()
await expect(page.getByRole('listbox')).toBeVisible()
const box = await themeSelect.boundingBox()
if (!box) throw new Error('theme select not found')
await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2)
await expect(page.getByRole('listbox')).toHaveCount(0)
await expect(shareMenu.exportTabPage).toBeVisible()
})
test('can unshare and reshare', async ({ page, browser, shareMenu }) => {
// Copy the link to the current file
await shareMenu.open()
expect(await shareMenu.isInviteButtonVisible()).toBe(true)
const url = await shareMenu.copyLink()
// Open link in an incognito window
const { newPage, newShareMenu, newContext } = await openNewTab(browser, {
url,
allowClipboard: true,
userProps: undefined,
})
// The second page should have the share button and not the error
await expect(newShareMenu.shareButton).toBeVisible()
await expect(newPage.getByTestId('tla-error')).not.toBeVisible()
// Now unshare it it...
await page.getByTestId('shared-link-shared-switch').click()
expect(await shareMenu.isToggleChecked()).toBe(false)
// Reload the second page
newPage.reload()
// The second page should have an error and not the share button
await expect(newShareMenu.shareButton).not.toBeVisible()
await expect(newPage.getByTestId('tla-error')).toBeVisible()
// Now reshare it...
await page.getByTestId('shared-link-shared-switch').click()
expect(await shareMenu.isToggleChecked()).toBe(true)
// Reload the second page again
newPage.reload()
// The second page should have the share button and not the error again
await expect(newShareMenu.shareButton).toBeVisible()
await expect(newPage.getByTestId('tla-error')).not.toBeVisible()
await newContext.close()
})
})
test.describe('signed in user on someone elses file', () => {
test('tabs work correctly', async ({ browser, sidebar, shareMenu }) => {
const newName = getRandomName()
await sidebar.renameFile(0, newName)
await shareMenu.open()
const url = await shareMenu.copyLink()
const parallelIndex = test.info().parallelIndex
// Open link in an incognito window
const { newShareMenu, newPage } = await openNewTab(browser, {
url,
allowClipboard: true,
userProps: { user: 'suppy', index: parallelIndex },
})
await expect(newPage.getByTestId('tla-sidebar').getByText(newName)).toBeVisible()
await newShareMenu.open()
expect(await newShareMenu.publishTabButton.isVisible()).toBe(false)
expect(await newShareMenu.inviteTabButton.isVisible()).toBe(false)
expect(await newShareMenu.exportTabButton.isVisible()).toBe(true)
expect(await newShareMenu.anonShareTabButton.isVisible()).toBe(true)
// Starts on the anon share tab
expect(await newShareMenu.inviteTabPage.isVisible()).toBe(false)
expect(await newShareMenu.exportTabPage.isVisible()).toBe(false)
expect(await newShareMenu.publishTabPage.isVisible()).toBe(false)
expect(await newShareMenu.anonShareTabPage.isVisible()).toBe(true)
// Can switch between tabs (export)
await newShareMenu.exportTabButton.click()
expect(await newShareMenu.inviteTabPage.isVisible()).toBe(false)
expect(await newShareMenu.exportTabPage.isVisible()).toBe(true)
expect(await newShareMenu.publishTabPage.isVisible()).toBe(false)
expect(await newShareMenu.anonShareTabPage.isVisible()).toBe(false)
})
})
test.describe('signed in user on published file', () => {
test('tabs work correctly', async ({ shareMenu, browser }) => {
// Publish the user's current file
await shareMenu.open()
expect(await shareMenu.isInviteButtonVisible()).toBe(true)
await shareMenu.publishFile()
const url = await shareMenu.copyLink()
// Open published file link in other user window
const { newShareMenu, newContext } = await openNewTab(browser, {
url,
allowClipboard: true,
userProps: { user: 'huppy', index: test.info().parallelIndex },
})
await expect(newShareMenu.shareButton).toBeVisible()
await newShareMenu.open()
expect(await newShareMenu.publishTabButton.isVisible()).toBe(false)
expect(await newShareMenu.inviteTabButton.isVisible()).toBe(false)
expect(await newShareMenu.exportTabButton.isVisible()).toBe(true)
expect(await newShareMenu.anonShareTabButton.isVisible()).toBe(true)
expect(await newShareMenu.publishTabPage.isVisible()).toBe(false)
expect(await newShareMenu.inviteTabPage.isVisible()).toBe(false)
expect(await newShareMenu.exportTabPage.isVisible()).toBe(false)
expect(await newShareMenu.anonShareTabPage.isVisible()).toBe(true) // show anon share tab page
// can copy the url
const copiedUrl = await newShareMenu.copyLink()
expect(new URL(copiedUrl).pathname).toBe(new URL(url).pathname)
// Switch to the export share tab
await newShareMenu.exportTabButton.click()
expect(await newShareMenu.exportTabPage.isVisible()).toBe(true)
expect(await newShareMenu.anonShareTabPage.isVisible()).toBe(false)
// Does not see the sidebar or the sidebar button
expect(await newShareMenu.page.getByTestId('tla-sidebar').count()).toBe(0)
expect(await newShareMenu.page.getByTestId('tla-sidebar-toggle').count()).toBe(0)
// Main user does see sidebar
expect(await shareMenu.page.getByTestId('tla-sidebar').count()).toBe(1)
expect(await shareMenu.page.getByTestId('tla-sidebar-toggle').count()).toBe(1)
await newContext.close()
})
})
test.describe('logged out user on scratch file', () => {
test('tabs work correctly', async ({ browser }) => {
const { newShareMenu, newContext } = await openNewTab(browser, {
allowClipboard: true,
userProps: undefined,
})
// In the incognito window, we are on the scratchpad
await expect(newShareMenu.shareButton).toBeVisible()
await newShareMenu.open()
expect(await newShareMenu.inviteTabButton.isVisible()).toBe(false)
expect(await newShareMenu.exportTabButton.isVisible()).toBe(true)
expect(await newShareMenu.publishTabButton.isVisible()).toBe(false)
expect(await newShareMenu.anonShareTabButton.isVisible()).toBe(false) // not shown on scratch
await newContext.close()
})
})
test.describe('logged out user on guest file', () => {
test('tabs work correctly', async ({ shareMenu, browser }) => {
// Share the signed in user's current file
await shareMenu.open()
expect(await shareMenu.isInviteButtonVisible()).toBe(true)
await shareMenu.shareFile()
const url = await shareMenu.copyLink()
// Open file link in an incognito window
const { newShareMenu, newContext } = await openNewTab(browser, {
url,
allowClipboard: true,
userProps: undefined,
})
await expect(newShareMenu.shareButton).toBeVisible()
await newShareMenu.open()
expect(await newShareMenu.publishTabButton.isVisible()).toBe(false)
expect(await newShareMenu.inviteTabButton.isVisible()).toBe(false)
expect(await newShareMenu.exportTabButton.isVisible()).toBe(true)
expect(await newShareMenu.anonShareTabButton.isVisible()).toBe(true)
expect(await newShareMenu.publishTabPage.isVisible()).toBe(false)
expect(await newShareMenu.inviteTabPage.isVisible()).toBe(false)
expect(await newShareMenu.exportTabPage.isVisible()).toBe(false)
expect(await newShareMenu.anonShareTabPage.isVisible()).toBe(true) // show anon share tab page
// can copy the url
const copiedUrl = await shareMenu.copyLink()
expect(copiedUrl).toBe(url)
// Switch to the export share tab
await newShareMenu.exportTabButton.click()
expect(await newShareMenu.exportTabPage.isVisible()).toBe(true)
expect(await newShareMenu.anonShareTabPage.isVisible()).toBe(false)
await newContext.close()
})
})
test.describe('logged out user on published file', () => {
test('tabs work correctly', async ({ shareMenu, browser }) => {
// Publish the user's current file
await shareMenu.open()
expect(await shareMenu.isInviteButtonVisible()).toBe(true)
await shareMenu.publishFile()
const url = await shareMenu.copyLink()
// Open published file link in an incognito window
const { newShareMenu, newContext, newEditor } = await openNewTab(browser, {
url,
allowClipboard: true,
userProps: undefined,
})
await expect(newShareMenu.shareButton).toBeVisible()
await newShareMenu.open()
expect(await newShareMenu.publishTabButton.isVisible()).toBe(false)
expect(await newShareMenu.inviteTabButton.isVisible()).toBe(false)
expect(await newShareMenu.exportTabButton.isVisible()).toBe(true)
expect(await newShareMenu.anonShareTabButton.isVisible()).toBe(true)
expect(await newShareMenu.publishTabPage.isVisible()).toBe(false)
expect(await newShareMenu.inviteTabPage.isVisible()).toBe(false)
expect(await newShareMenu.exportTabPage.isVisible()).toBe(false)
expect(await newShareMenu.anonShareTabPage.isVisible()).toBe(true) // show anon share tab page
// can copy the url
const copiedUrl = await shareMenu.copyLink()
expect(copiedUrl).toBe(url)
// Switch to the export share tab
await newShareMenu.exportTabButton.click()
expect(await newShareMenu.exportTabPage.isVisible()).toBe(true)
expect(await newShareMenu.anonShareTabPage.isVisible()).toBe(false)
// download the file
await newEditor.openPageMenu()
const downloadButton = newEditor.page.getByText('Download')
await expect(downloadButton).toBeVisible()
// check that the file downloaded
const downloadPromise = newEditor.page.waitForEvent('download')
await downloadButton.click()
const download = await downloadPromise
expect(download.suggestedFilename().endsWith('.tldr')).toBe(true)
await newContext.close()
})
})
/* ------------------- Exporting ------------------- */
test('can export a file as an image', async ({ page, shareMenu }) => {
await page.getByTestId('tools.rectangle').click()
await page.locator('.tl-background').click()
const downloadPromise = page.waitForEvent('download')
await shareMenu.open()
await shareMenu.exportFile()
const download = await downloadPromise
const suggestedFilename = download.suggestedFilename()
expect(suggestedFilename.endsWith('.png')).toBe(true)
const filePath = path.join('./test-results/', suggestedFilename)
await download.saveAs(filePath)
expect(fs.existsSync(filePath)).toBeTruthy()
const stats = fs.statSync(filePath)
expect(stats.size).toBeGreaterThan(0)
})
test('can follow a deep link to a never-seen file', async ({ editor, browser, shareMenu }) => {
const text = Math.random().toString(36).substring(2, 15)
await editor.createNewPage()
await editor.createTextShape(text)
await shareMenu.open()
expect(await shareMenu.isInviteButtonVisible()).toBe(true)
const url = await shareMenu.copyLink()
// close the share menu
await shareMenu.page.keyboard.press('Escape')
// create a new empty page
await editor.createNewPage()
const { newEditor } = await openNewTab(browser, {
url,
allowClipboard: true,
userProps: { user: 'suppy', index: test.info().parallelIndex },
})
await newEditor.expectShapesCount(1)
expect(newEditor.page.getByText(text).last()).toBeVisible()
})
test('can follow a deep link to an already-seen file', async ({ editor, shareMenu, browser }) => {
const text = Math.random().toString(36).substring(2, 15)
await shareMenu.open()
await shareMenu.inviteTabButton.click()
// close the share menu
await shareMenu.page.keyboard.press('Escape')
const { newEditor, newShareMenu } = await openNewTab(browser, {
url: editor.page.url(),
allowClipboard: true,
userProps: { user: 'suppy', index: test.info().parallelIndex },
})
await newEditor.createNewPage()
await newEditor.createTextShape(text)
await newEditor.expectShapesCount(1)
await newShareMenu.open()
await newShareMenu.anonShareTabButton.click()
const url = await newShareMenu.copyLink()
await newEditor.createNewPage()
await editor.page.goto(url)
await editor.expectShapesCount(1)
await expect(editor.page.getByText(text).last()).toBeVisible()
})