snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
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
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
This commit is contained in:
379
apps/dotcom/client/e2e/tests/ui.scenario.spec.ts
Normal file
379
apps/dotcom/client/e2e/tests/ui.scenario.spec.ts
Normal file
@@ -0,0 +1,379 @@
|
||||
import { expect, selectTlaMenuOption, test } from '../fixtures/scenario-test'
|
||||
|
||||
const ROOT_URL = 'http://localhost:3000'
|
||||
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
|
||||
test.describe('UI scenarios', () => {
|
||||
test('file creation and inline rename submit/cancel stay in sync', async ({
|
||||
owner,
|
||||
scenario,
|
||||
}) => {
|
||||
const originalName = scenario.name('created file')
|
||||
const cancelledName = scenario.name('cancelled file')
|
||||
const submittedName = scenario.name('submitted file')
|
||||
|
||||
await scenario.createPersonalFile(owner, originalName)
|
||||
const fileUrl = owner.page.url()
|
||||
expect(new URL(fileUrl).pathname).toMatch(/^\/f\//)
|
||||
await owner.sidebar.expectFileVisible(originalName)
|
||||
await owner.sidebar.expectFileActive(originalName)
|
||||
expect(await owner.editor.getCurrentFileName()).toBe(originalName)
|
||||
|
||||
await owner.goto(fileUrl)
|
||||
await owner.editor.ensureSidebarOpen()
|
||||
await owner.sidebar.expectFileActive(originalName)
|
||||
expect(await owner.editor.getCurrentFileName()).toBe(originalName)
|
||||
|
||||
await owner.sidebar.getFileByName(originalName).dblclick()
|
||||
const cancelInput = owner.page.getByTestId('tla-sidebar-rename-input')
|
||||
await expect(cancelInput).toBeFocused()
|
||||
await cancelInput.fill(cancelledName)
|
||||
await owner.page.keyboard.press('Escape')
|
||||
|
||||
await expect(cancelInput).not.toBeVisible()
|
||||
await owner.sidebar.expectFileVisible(originalName)
|
||||
await owner.sidebar.expectFileNotVisible(cancelledName)
|
||||
expect(await owner.editor.getCurrentFileName()).toBe(originalName)
|
||||
|
||||
await owner.sidebar.getFileByName(originalName).dblclick()
|
||||
const submitInput = owner.page.getByTestId('tla-sidebar-rename-input')
|
||||
await expect(submitInput).toBeFocused()
|
||||
await submitInput.fill(submittedName)
|
||||
await owner.page.keyboard.press('Enter')
|
||||
|
||||
await expect(submitInput).not.toBeVisible()
|
||||
await owner.sidebar.expectFileVisible(submittedName)
|
||||
await owner.sidebar.expectFileNotVisible(originalName)
|
||||
await owner.sidebar.expectFileActive(submittedName)
|
||||
expect(await owner.editor.getCurrentFileName()).toBe(submittedName)
|
||||
})
|
||||
|
||||
test('file menu commands update the file list and active file', async ({ owner, scenario }) => {
|
||||
const originalName = scenario.name('menu original')
|
||||
const renamedName = scenario.name('menu renamed')
|
||||
const duplicateName = scenario.name('menu duplicate')
|
||||
|
||||
await scenario.createPersonalFile(owner, originalName)
|
||||
await scenario.createRectangle(owner)
|
||||
// Duplicating below copies the room's server-side persisted content (see
|
||||
// TLFileDurableObject.handleFileCreateFromSource), which only reflects shapes that have
|
||||
// round-tripped over the room's own sync websocket (independent of Zero/app mutations).
|
||||
// expectShapesCount above only proves the shape rendered locally; give the push time to
|
||||
// reach the server before duplicating, or the copy can race the still-in-flight change and
|
||||
// silently omit the shape.
|
||||
// TODO: replace with a condition-based wait — needs a __test__ route exposing the room DO's
|
||||
// persisted doc state (no such signal exists today), a fixed timeout can still lose the race.
|
||||
await owner.page.waitForTimeout(1000)
|
||||
|
||||
await owner.sidebar.renameFileByName(originalName, renamedName)
|
||||
await owner.sidebar.expectFileVisible(renamedName)
|
||||
await owner.sidebar.expectFileNotVisible(originalName)
|
||||
await owner.sidebar.expectFileActive(renamedName)
|
||||
expect(await owner.editor.getCurrentFileName()).toBe(renamedName)
|
||||
|
||||
const copiedLink = await owner.sidebar.copyFileLinkByName(renamedName)
|
||||
expect(new URL(copiedLink).origin).toBe(ROOT_URL)
|
||||
expect(new URL(copiedLink).pathname).toMatch(/^\/f\//)
|
||||
|
||||
await owner.sidebar.duplicateFileByName(renamedName, duplicateName)
|
||||
await owner.sidebar.expectFileVisible(renamedName)
|
||||
await owner.sidebar.expectFileVisible(duplicateName)
|
||||
await owner.sidebar.expectFileActive(duplicateName)
|
||||
expect(await owner.editor.getCurrentFileName()).toBe(duplicateName)
|
||||
// Duplicating copies the room server-side, so the new file's shapes only render once the
|
||||
// editor navigates and loads the copied content. Allow more than the default 5s for that.
|
||||
await owner.editor.expectShapesCount(1, 15000)
|
||||
|
||||
await owner.sidebar.deleteFileByName(duplicateName)
|
||||
await owner.deleteFileDialog.expectIsVisible()
|
||||
await owner.deleteFileDialog.confirmDeletion()
|
||||
await owner.deleteFileDialog.expectIsNotVisible()
|
||||
|
||||
await owner.sidebar.expectFileNotVisible(duplicateName)
|
||||
await owner.sidebar.expectFileVisible(renamedName)
|
||||
await expect(owner.page.getByTestId('tla-editor')).toBeVisible()
|
||||
})
|
||||
|
||||
test('pinned section only appears while files are pinned', async ({ owner, scenario }) => {
|
||||
const fileName = scenario.name('pinned file')
|
||||
|
||||
await scenario.createPersonalFile(owner, fileName)
|
||||
await expect(owner.page.getByTestId('tla-file-link-pinned-0')).not.toBeVisible()
|
||||
|
||||
await owner.sidebar.pinFile(fileName)
|
||||
await owner.sidebar.expectFilePinned(fileName)
|
||||
await expect(owner.page.getByTestId('tla-file-link-pinned-0')).toContainText(fileName)
|
||||
await owner.sidebar.expectFileActive(fileName)
|
||||
|
||||
await owner.sidebar.unpinFile(fileName)
|
||||
await owner.sidebar.expectFileNotPinned(fileName)
|
||||
await expect(owner.page.getByTestId('tla-file-link-pinned-0')).not.toBeVisible()
|
||||
await owner.sidebar.expectFileActive(fileName)
|
||||
})
|
||||
|
||||
test('toggling the sidebar preserves the active editor and file selection', async ({
|
||||
owner,
|
||||
scenario,
|
||||
}) => {
|
||||
const fileName = scenario.name('sidebar toggle file')
|
||||
|
||||
await scenario.createPersonalFile(owner, fileName)
|
||||
await owner.editor.ensureSidebarClosed()
|
||||
await expect(owner.page.getByTestId('canvas')).toBeVisible()
|
||||
expect(await owner.editor.getCurrentFileName()).toBe(fileName)
|
||||
|
||||
await owner.editor.ensureSidebarOpen()
|
||||
await owner.sidebar.expectFileActive(fileName)
|
||||
expect(await owner.editor.getCurrentFileName()).toBe(fileName)
|
||||
})
|
||||
|
||||
test('workspace creation, moving, and deletion update navigation', async ({
|
||||
owner,
|
||||
scenario,
|
||||
}) => {
|
||||
const workspaceName = scenario.name('workspace nav')
|
||||
const fileName = scenario.name('workspace movable file')
|
||||
|
||||
await scenario.goToAndOpenSidebar(owner)
|
||||
await scenario.createPersonalFile(owner, fileName)
|
||||
|
||||
await owner.sidebar.createWorkspace(workspaceName)
|
||||
await owner.sidebar.expectActiveWorkspace(workspaceName)
|
||||
await owner.sidebar.expectWorkspaceVisible(workspaceName)
|
||||
|
||||
await owner.sidebar.switchToHomeWorkspace()
|
||||
await owner.sidebar.moveFileToWorkspace(fileName, workspaceName)
|
||||
await owner.sidebar.expectActiveWorkspace(workspaceName)
|
||||
await owner.sidebar.expectFileVisible(fileName)
|
||||
|
||||
await owner.sidebar.switchToHomeWorkspace()
|
||||
await owner.sidebar.expectFileNotVisible(fileName)
|
||||
await owner.sidebar.switchToWorkspace(workspaceName)
|
||||
|
||||
await owner.sidebar.deleteWorkspace(workspaceName)
|
||||
await owner.sidebar.expectWorkspaceNotVisible(workspaceName)
|
||||
await owner.sidebar.expectFileNotVisible(fileName)
|
||||
await owner.sidebar.expectActiveHomeWorkspace()
|
||||
})
|
||||
|
||||
test('share and publish controls expose current access and published URLs', async ({
|
||||
owner,
|
||||
visitor,
|
||||
scenario,
|
||||
}) => {
|
||||
await scenario.createPersonalFile(owner, scenario.name('publish controls file'))
|
||||
|
||||
await owner.shareMenu.open()
|
||||
await expect(owner.shareMenu.inviteTabButton).toBeVisible()
|
||||
await expect(owner.shareMenu.exportTabButton).toBeVisible()
|
||||
await expect(owner.shareMenu.publishTabButton).toBeVisible()
|
||||
await expect(owner.shareMenu.inviteTabPage).toBeVisible()
|
||||
await expect(owner.page.getByTestId('shared-link-shared-switch')).toBeVisible()
|
||||
await owner.page.keyboard.press('Escape')
|
||||
|
||||
const publishedUrl = await scenario.publishFile(owner)
|
||||
expect(new URL(publishedUrl).pathname).toMatch(/^\/p\//)
|
||||
|
||||
await visitor.page.goto(publishedUrl, { waitUntil: 'load' })
|
||||
await expect(visitor.page.getByTestId('tla-editor')).toBeVisible()
|
||||
await visitor.editor.expectShapesCount(0)
|
||||
|
||||
await owner.shareMenu.open()
|
||||
await owner.shareMenu.unpublishFile()
|
||||
await owner.page.keyboard.press('Escape')
|
||||
|
||||
// The unpublish applies optimistically; the visitor only sees it once the server has
|
||||
// committed, so reload until the published page stops resolving.
|
||||
await expect(async () => {
|
||||
await visitor.page.goto(publishedUrl, { waitUntil: 'load' })
|
||||
await expect(visitor.page.getByTestId('tla-error')).toBeVisible({ timeout: 3000 })
|
||||
}).toPass({ timeout: 15000 })
|
||||
})
|
||||
|
||||
test('missing files show not-found UI to signed-in and signed-out users', async ({
|
||||
owner,
|
||||
visitor,
|
||||
}) => {
|
||||
const missingFileUrl = `${ROOT_URL}/f/${Math.random().toString(36).substring(2, 15)}`
|
||||
|
||||
await owner.page.goto(missingFileUrl, { waitUntil: 'load' })
|
||||
await expect(async () => {
|
||||
await owner.errorPage.expectNotFoundVisible()
|
||||
}).toPass()
|
||||
await expect(owner.page.getByTestId('tla-error')).toBeVisible()
|
||||
|
||||
await visitor.page.goto(missingFileUrl, { waitUntil: 'load' })
|
||||
await expect(async () => {
|
||||
await visitor.errorPage.expectNotFoundVisible()
|
||||
}).toPass()
|
||||
await expect(visitor.page.getByTestId('tla-error')).toBeVisible()
|
||||
})
|
||||
|
||||
test('anonymous export downloads an image file', async ({ visitor }) => {
|
||||
await visitor.homePage.expectSignInButtonVisible()
|
||||
await visitor.page.evaluate(() => {
|
||||
const editor = (window as any).editor
|
||||
editor.createShapes([
|
||||
{
|
||||
type: 'geo',
|
||||
x: 100,
|
||||
y: 100,
|
||||
props: { geo: 'rectangle', w: 100, h: 100 },
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
await visitor.shareMenu.open()
|
||||
await visitor.shareMenu.ensureTabSelected('export')
|
||||
await expect(visitor.shareMenu.exportTabPage).toBeVisible()
|
||||
|
||||
const downloadPromise = visitor.page.waitForEvent('download')
|
||||
await visitor.shareMenu.exportImageButton.click()
|
||||
const download = await downloadPromise
|
||||
expect(download.suggestedFilename()).toMatch(/\.(png|svg)$/)
|
||||
})
|
||||
|
||||
test('workspace settings reflect role-specific controls', async ({ owner, member, scenario }) => {
|
||||
const { workspaceName, fileName, memberUserId } = await scenario.createWorkspaceWithMember({
|
||||
owner,
|
||||
member,
|
||||
workspaceName: scenario.name('settings controls workspace'),
|
||||
fileName: scenario.name('settings controls file'),
|
||||
})
|
||||
|
||||
await owner.sidebar.openWorkspaceSettings(workspaceName)
|
||||
const ownerDialog = owner.page.getByRole('dialog', { name: 'Manage workspace' })
|
||||
|
||||
// Owners see the full dialog surface and member roster.
|
||||
await expect(ownerDialog).toBeVisible()
|
||||
await expect(ownerDialog.getByPlaceholder('Workspace name')).toBeVisible()
|
||||
await expect(ownerDialog.getByText('Invite teammates')).toBeVisible()
|
||||
await expect(ownerDialog.getByRole('button', { name: 'Copy invite link' })).toBeVisible()
|
||||
await expect(ownerDialog.getByRole('tab', { name: 'Members' })).toBeVisible()
|
||||
await expect(ownerDialog.getByText(/\(you\)/)).toBeVisible()
|
||||
const memberRoleSelect = ownerDialog.locator(`[id="workspace-member-role-${memberUserId}"]`)
|
||||
await expect(memberRoleSelect).toHaveText('Member')
|
||||
|
||||
// Interacting with the portalled role select should not count as a background click.
|
||||
await selectTlaMenuOption(owner.page, memberRoleSelect, 'Member')
|
||||
await expect(ownerDialog).toBeVisible()
|
||||
|
||||
// The dialog exposes the invite link only through the Copy button (no visible URL
|
||||
// field), so read it from the clipboard. Regenerating from the Settings tab
|
||||
// replaces the link, so a later copy returns a different URL.
|
||||
await ownerDialog.getByRole('button', { name: 'Copy invite link' }).click()
|
||||
const firstInviteUrl = await owner.page.evaluate(() => navigator.clipboard.readText())
|
||||
expect(new URL(firstInviteUrl).pathname).toMatch(/^\/invite\//)
|
||||
|
||||
await ownerDialog.getByRole('tab', { name: 'Settings' }).click()
|
||||
await ownerDialog.getByRole('button', { name: 'Regenerate invite link' }).click()
|
||||
await owner.page.getByRole('button', { name: 'Regenerate', exact: true }).click()
|
||||
|
||||
// Copy again (after the 1s copy-button guard) and poll until the new link lands.
|
||||
await expect
|
||||
.poll(
|
||||
async () => {
|
||||
await owner.page.waitForTimeout(1100)
|
||||
await ownerDialog.getByRole('button', { name: 'Copy invite link' }).click()
|
||||
return owner.page.evaluate(() => navigator.clipboard.readText())
|
||||
},
|
||||
{ timeout: 15000 }
|
||||
)
|
||||
.not.toBe(firstInviteUrl)
|
||||
const regeneratedInviteUrl = await owner.page.evaluate(() => navigator.clipboard.readText())
|
||||
expect(new URL(regeneratedInviteUrl).pathname).toMatch(/^\/invite\//)
|
||||
await owner.page.keyboard.press('Escape')
|
||||
|
||||
// Non-owners can inspect settings but cannot access owner-only controls.
|
||||
await member.sidebar.openWorkspaceSettings(workspaceName)
|
||||
const memberDialog = member.page.getByRole('dialog', { name: 'Manage workspace' })
|
||||
await expect(memberDialog.getByPlaceholder('Workspace name')).toBeDisabled()
|
||||
await expect(
|
||||
memberDialog.locator(`[id="workspace-member-role-${memberUserId}"]`)
|
||||
).not.toBeVisible()
|
||||
|
||||
// Leave/Delete live on the Settings tab; members get Leave but not Delete.
|
||||
await memberDialog.getByRole('tab', { name: 'Settings' }).click()
|
||||
await expect(memberDialog.getByRole('button', { name: /Delete workspace/ })).not.toBeVisible()
|
||||
await expect(memberDialog.getByRole('button', { name: /Leave workspace/ })).toBeVisible()
|
||||
|
||||
// Leaving requires confirmation (the confirm button is just "Leave") and removes access.
|
||||
await memberDialog.getByRole('button', { name: /Leave workspace/ }).click()
|
||||
await member.page.getByRole('button', { name: 'Leave', exact: true }).click()
|
||||
await member.sidebar.expectWorkspaceNotVisible(workspaceName)
|
||||
await member.sidebar.expectFileNotVisible(fileName)
|
||||
})
|
||||
|
||||
test('home workspace settings show a private rename dialog, not an empty one', async ({
|
||||
owner,
|
||||
scenario,
|
||||
}) => {
|
||||
// Regression: the home workspace ("My workspace") used to render an empty dialog — the
|
||||
// component returned null for it while the sidebar still opened the dialog, so the page
|
||||
// just dimmed with no content. It's private: it can be renamed, but not shared or managed.
|
||||
await scenario.goToAndOpenSidebar(owner)
|
||||
await owner.sidebar.switchToHomeWorkspace()
|
||||
await owner.page.getByTestId('tla-sidebar-workspace-settings').click()
|
||||
|
||||
const dialog = owner.page.getByRole('dialog', { name: 'Manage workspace' })
|
||||
await expect(dialog).toBeVisible()
|
||||
// Renameable: the name field is present and editable.
|
||||
await expect(dialog.getByPlaceholder('Workspace name')).toBeEnabled()
|
||||
// Private: a disabled invite control with an explanatory note (no shareable link).
|
||||
await expect(
|
||||
dialog.getByText(
|
||||
'This is your private workspace. Create a new workspace to invite teammates.'
|
||||
)
|
||||
).toBeVisible()
|
||||
await expect(dialog.getByRole('button', { name: 'Copy invite link' })).toBeDisabled()
|
||||
// Nothing to manage, so the dialog has no Members/Settings tabs at all.
|
||||
await expect(dialog.getByRole('tab')).toHaveCount(0)
|
||||
|
||||
await owner.page.keyboard.press('Escape')
|
||||
})
|
||||
|
||||
test('the home workspace can be renamed from its settings dialog', async ({
|
||||
owner,
|
||||
scenario,
|
||||
}) => {
|
||||
// Regression: the home workspace is renameable (the reason its settings dialog exists at
|
||||
// all), so the rename must apply. Restore the name afterwards so later serial tests still
|
||||
// see the original.
|
||||
await scenario.goToAndOpenSidebar(owner)
|
||||
await owner.sidebar.switchToHomeWorkspace()
|
||||
const originalName = await owner.page.getByTestId('tla-active-workspace-name').innerText()
|
||||
const newName = scenario.name('home renamed')
|
||||
|
||||
await owner.page.getByTestId('tla-sidebar-workspace-settings').click()
|
||||
const dialog = owner.page.getByRole('dialog', { name: 'Manage workspace' })
|
||||
await dialog.getByPlaceholder('Workspace name').fill(newName)
|
||||
await owner.page.getByRole('button', { name: 'Close' }).click()
|
||||
await owner.sidebar.expectActiveWorkspace(newName)
|
||||
|
||||
// Restore the original home workspace name.
|
||||
await owner.page.getByTestId('tla-sidebar-workspace-settings').click()
|
||||
await dialog.getByPlaceholder('Workspace name').fill(originalName)
|
||||
await owner.page.getByRole('button', { name: 'Close' }).click()
|
||||
await owner.sidebar.expectActiveWorkspace(originalName)
|
||||
})
|
||||
|
||||
test('workspace settings rename updates labels and persists', async ({ owner, scenario }) => {
|
||||
const workspaceName = scenario.name('settings rename old')
|
||||
const newWorkspaceName = scenario.name('settings rename new')
|
||||
|
||||
await scenario.goToAndOpenSidebar(owner)
|
||||
await owner.sidebar.createWorkspace(workspaceName)
|
||||
await owner.sidebar.renameWorkspace(workspaceName, newWorkspaceName)
|
||||
|
||||
await owner.sidebar.expectActiveWorkspace(newWorkspaceName)
|
||||
await owner.sidebar.expectWorkspaceVisible(newWorkspaceName)
|
||||
await owner.sidebar.expectWorkspaceNotVisible(workspaceName)
|
||||
|
||||
await owner.page.reload()
|
||||
await owner.waitForAppReady()
|
||||
await owner.editor.ensureSidebarOpen()
|
||||
await owner.sidebar.expectWorkspaceVisible(newWorkspaceName)
|
||||
await owner.sidebar.expectWorkspaceNotVisible(workspaceName)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user