123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503 |
- import { expect } from '@playwright/test'
- import { test } from './fixtures'
- import { modKey, renamePage } from './utils'
- test('enable whiteboards', async ({ page }) => {
- if (await page.$('.nav-header .whiteboard') === null) {
- await page.click('#head .toolbar-dots-btn')
- await page.click('#head .dropdown-wrapper >> text=Settings')
- await page.click('.settings-modal a[data-id=features]')
- await page.click('text=Whiteboards >> .. >> .ui__toggle')
- await page.waitForTimeout(1000)
- await page.keyboard.press('Escape')
- }
- await expect(page.locator('.nav-header .whiteboard')).toBeVisible()
- })
- test('should display onboarding tour', async ({ page }) => {
- // ensure onboarding tour is going to be triggered locally
- await page.evaluate(`window.localStorage.removeItem('whiteboard-onboarding-tour?')`)
- await page.click('.nav-header .whiteboard')
- await expect(page.locator('.cp__whiteboard-welcome')).toBeVisible()
- await page.click('.cp__whiteboard-welcome button.bg-gray-600')
- await expect(page.locator('.cp__whiteboard-welcome')).toBeHidden()
- })
- test('create new whiteboard', async ({ page }) => {
- await page.click('#tl-create-whiteboard')
- await expect(page.locator('.logseq-tldraw')).toBeVisible()
- })
- test('can right click title to show context menu', async ({ page }) => {
- await page.click('.whiteboard-page-title', {
- button: 'right',
- })
- await expect(page.locator('#custom-context-menu')).toBeVisible()
- await page.keyboard.press('Escape')
- await expect(page.locator('#custom-context-menu')).toHaveCount(0)
- })
- test('newly created whiteboard should have a default title', async ({ page }) => {
- await expect(page.locator('.whiteboard-page-title .title')).toContainText(
- 'Untitled'
- )
- })
- test('set whiteboard title', async ({ page }) => {
- const title = 'my-whiteboard'
- await page.click('.nav-header .whiteboard')
- await page.click('#tl-create-whiteboard')
- await page.click('.whiteboard-page-title')
- await page.fill('.whiteboard-page-title input', title)
- await page.keyboard.press('Enter')
- await expect(page.locator('.whiteboard-page-title .title')).toContainText(
- title
- )
- })
- test('update whiteboard title', async ({ page }) => {
- const title = 'my-whiteboard'
- await page.click('.whiteboard-page-title')
- await page.fill('.whiteboard-page-title input', title + '-2')
- await page.keyboard.press('Enter')
- // Updating non-default title should pop up a confirmation dialog
- await expect(page.locator('.ui__confirm-modal >> .headline')).toContainText(
- `Do you really want to change the page name to “${title}-2”?`
- )
- await page.click('.ui__confirm-modal button')
- await expect(page.locator('.whiteboard-page-title .title')).toContainText(
- title + '-2'
- )
- })
- test('draw a rectangle', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.keyboard.type('wr')
- await page.mouse.move(bounds.x + 105, bounds.y + 105)
- await page.mouse.down()
- await page.mouse.move(bounds.x + 150, bounds.y + 150 )
- await page.mouse.up()
- await page.keyboard.press('Escape')
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(1)
- })
- test('undo the rectangle action', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-positioned-svg rect')).toHaveCount(0)
- })
- test('redo the rectangle action', async ({ page }) => {
- await page.keyboard.press(modKey + '+Shift+z')
- await page.keyboard.press('Escape')
- await page.waitForTimeout(100)
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(1)
- })
- test('clone the rectangle', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.mouse.move(bounds.x + 400, bounds.y + 400)
- await page.mouse.move(bounds.x + 120, bounds.y + 120, {steps: 5})
- await page.keyboard.down('Alt')
- await page.mouse.down()
- await page.mouse.move(bounds.x + 200, bounds.y + 200, {steps: 5})
- await page.mouse.up()
- await page.keyboard.up('Alt')
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(2)
- })
- test('group the rectangles', async ({ page }) => {
- await page.keyboard.press(modKey + '+a')
- await page.keyboard.press(modKey + '+g')
- await expect(page.locator('.logseq-tldraw .tl-group-container')).toHaveCount(1)
- })
- test('delete the group', async ({ page }) => {
- await page.keyboard.press(modKey + '+a')
- await page.keyboard.press('Delete')
- await expect(page.locator('.logseq-tldraw .tl-group-container')).toHaveCount(0)
- // should also delete the grouped shapes
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(0)
- })
- test('undo the group deletion', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-group-container')).toHaveCount(1)
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(2)
- })
- test('undo the group action', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-group-container')).toHaveCount(0)
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(2)
- })
- test('connect rectangles with an arrow', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.keyboard.type('wc')
- await page.mouse.move(bounds.x + 120, bounds.y + 120)
- await page.mouse.down()
- await page.mouse.move(bounds.x + 200, bounds.y + 200, {steps: 5}) // will fail without steps
- await page.mouse.up()
- await page.keyboard.press('Escape')
- await expect(page.locator('.logseq-tldraw .tl-line-container')).toHaveCount(1)
- })
- test('delete the first rectangle', async ({ page }) => {
- await page.keyboard.press('Escape')
- await page.waitForTimeout(1000)
- await page.click('.logseq-tldraw .tl-box-container:first-of-type')
- await page.keyboard.press('Delete')
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(1)
- await expect(page.locator('.logseq-tldraw .tl-line-container')).toHaveCount(0)
- })
- test('undo the delete action', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(2)
- await expect(page.locator('.logseq-tldraw .tl-line-container')).toHaveCount(1)
- })
- test('convert the first rectangle to ellipse', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.keyboard.press('Escape')
- await page.mouse.move(bounds.x + 220, bounds.y + 220)
- await page.mouse.down()
- await page.mouse.up()
- await page.mouse.move(bounds.x + 520, bounds.y + 520)
- await page.click('.tl-context-bar .tl-geometry-tools-pane-anchor')
- await page.click('.tl-context-bar .tl-geometry-toolbar [data-tool=ellipse]')
- await expect(page.locator('.logseq-tldraw .tl-ellipse-container')).toHaveCount(1)
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(1)
- })
- test('change the color of the ellipse', async ({ page }) => {
- await page.click('.tl-context-bar .tl-color-bg')
- await page.click('.tl-context-bar .tl-color-palette .bg-red-500')
- await expect(page.locator('.logseq-tldraw .tl-ellipse-container ellipse:last-of-type')).toHaveAttribute('fill', 'var(--ls-wb-background-color-red)')
- })
- test('undo the color switch', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-ellipse-container ellipse:last-of-type')).toHaveAttribute('fill', 'var(--ls-wb-background-color-default)')
- })
- test('undo the shape conversion', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(2)
- await expect(page.locator('.logseq-tldraw .tl-ellipse-container')).toHaveCount(0)
- })
- test('locked elements should not be removed', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.keyboard.press('Escape')
- await page.mouse.move(bounds.x + 220, bounds.y + 220)
- await page.mouse.down()
- await page.mouse.up()
- await page.mouse.move(bounds.x + 520, bounds.y + 520)
- await page.keyboard.press(`${modKey}+l`)
- await page.keyboard.press('Delete')
- await page.keyboard.press(`${modKey}+Shift+l`)
- await expect(page.locator('.logseq-tldraw .tl-box-container')).toHaveCount(2)
- })
- test('move arrow to back', async ({ page }) => {
- await page.keyboard.press('Escape')
- await page.waitForTimeout(1000)
- await page.click('.logseq-tldraw .tl-line-container')
- await page.keyboard.press('Shift+[')
- await expect(page.locator('.logseq-tldraw .tl-canvas .tl-layer > div:first-of-type > div:first-of-type')).toHaveClass('tl-line-container')
- })
- test('move arrow to front', async ({ page }) => {
- await page.keyboard.press('Escape')
- await page.waitForTimeout(1000)
- await page.click('.logseq-tldraw .tl-line-container')
- await page.keyboard.press('Shift+]')
- await expect(page.locator('.logseq-tldraw .tl-canvas .tl-layer > div:first-of-type > div:first-of-type')).not.toHaveClass('tl-line-container')
- })
- test('undo the move action', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-canvas .tl-layer > div:first-of-type > div:first-of-type')).toHaveClass('tl-line-container')
- })
- test('cleanup the shapes', async ({ page }) => {
- await page.keyboard.press(`${modKey}+a`)
- await page.keyboard.press('Delete')
- await expect(page.locator('[data-type=Shape]')).toHaveCount(0)
- })
- test('create a block', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.keyboard.type('ws')
- await page.mouse.dblclick(bounds.x + 105, bounds.y + 105)
- await page.waitForTimeout(100)
- await page.keyboard.type('a')
- await page.keyboard.press('Enter')
- await expect(page.locator('.logseq-tldraw .tl-logseq-portal-container')).toHaveCount(1)
- })
- // TODO: Fix the failing test
- test.skip('expand the block', async ({ page }) => {
- await page.keyboard.press('Escape')
- await page.keyboard.press(modKey + '+ArrowDown')
- await page.waitForTimeout(100)
- await expect(page.locator('.logseq-tldraw .tl-logseq-portal-container .tl-logseq-portal-header')).toHaveCount(1)
- })
- // TODO: Depends on the previous test
- test.skip('undo the expand action', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-logseq-portal-container .tl-logseq-portal-header')).toHaveCount(0)
- })
- test('undo the block action', async ({ page }) => {
- await page.keyboard.press(modKey + '+z')
- await expect(page.locator('.logseq-tldraw .tl-logseq-portal-container')).toHaveCount(0)
- })
- test('copy/paste url to create an iFrame shape', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.keyboard.type('wt')
- await page.mouse.move(bounds.x + 105, bounds.y + 105)
- await page.mouse.down()
- await page.waitForTimeout(100)
- await page.keyboard.type('https://logseq.com')
- await page.keyboard.press(modKey + '+a')
- await page.keyboard.press(modKey + '+c')
- await page.keyboard.press('Escape')
- await page.keyboard.press(modKey + '+v')
- await expect( page.locator('.logseq-tldraw .tl-iframe-container')).toHaveCount(1)
- })
- test('copy/paste twitter status url to create a Tweet shape', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.keyboard.type('wt')
- await page.mouse.move(bounds.x + 105, bounds.y + 105)
- await page.mouse.down()
- await page.waitForTimeout(100)
- await page.keyboard.type('https://twitter.com/logseq/status/1605224589046386689')
- await page.keyboard.press(modKey + '+a')
- await page.keyboard.press(modKey + '+c')
- await page.keyboard.press('Escape')
- await page.keyboard.press(modKey + '+v')
- await expect( page.locator('.logseq-tldraw .tl-tweet-container')).toHaveCount(1)
- })
- test('copy/paste youtube video url to create a Youtube shape', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- const bounds = (await canvas.boundingBox())!
- await page.keyboard.type('wt')
- await page.mouse.move(bounds.x + 105, bounds.y + 105)
- await page.mouse.down()
- await page.waitForTimeout(100)
- await page.keyboard.type('https://www.youtube.com/watch?v=hz2BacySDXE')
- await page.keyboard.press(modKey + '+a')
- await page.keyboard.press(modKey + '+c')
- await page.keyboard.press('Escape')
- await page.keyboard.press(modKey + '+v')
- await expect(page.locator('.logseq-tldraw .tl-youtube-container')).toHaveCount(1)
- })
- test('zoom in', async ({ page }) => {
- await page.keyboard.press('Shift+0') // reset zoom
- await page.waitForTimeout(1500) // wait for the zoom animation to finish
- await page.keyboard.press('Shift+=')
- await page.waitForTimeout(1500) // wait for the zoom animation to finish
- await expect(page.locator('#tl-zoom')).toContainText('125%')
- })
- test('zoom out', async ({ page }) => {
- await page.keyboard.press('Shift+0')
- await page.waitForTimeout(1500) // wait for the zoom animation to finish
- await page.keyboard.press('Shift+-')
- await page.waitForTimeout(1500) // wait for the zoom animation to finish
- await expect(page.locator('#tl-zoom')).toContainText('80%')
- })
- test('open context menu', async ({ page }) => {
- await page.locator('.logseq-tldraw').click({ button: 'right' })
- await expect(page.locator('.tl-context-menu')).toBeVisible()
- })
- test('close context menu on esc', async ({ page }) => {
- await page.keyboard.press('Escape')
- await expect(page.locator('.tl-context-menu')).toBeHidden()
- })
- test('quick add another whiteboard', async ({ page }) => {
- // create a new board first
- await page.click('.nav-header .whiteboard')
- await page.click('#tl-create-whiteboard')
- await page.click('.whiteboard-page-title')
- await page.fill('.whiteboard-page-title input', 'my-whiteboard-3')
- await page.keyboard.press('Enter')
- const canvas = await page.waitForSelector('.logseq-tldraw')
- await canvas.dblclick({
- position: {
- x: 200,
- y: 200,
- },
- })
- const quickAdd$ = page.locator('.tl-quick-search')
- await expect(quickAdd$).toBeVisible()
- await page.fill('.tl-quick-search input', 'my-whiteboard')
- await quickAdd$
- .locator('.tl-quick-search-option >> text=my-whiteboard-2')
- .first()
- .click()
- await expect(quickAdd$).toBeHidden()
- await expect(
- page.locator('.tl-logseq-portal-container >> text=my-whiteboard-2')
- ).toBeVisible()
- })
- test('go to another board and check reference', async ({ page }) => {
- await page
- .locator('.tl-logseq-portal-container >> text=my-whiteboard-2')
- .click()
- await expect(page.locator('.whiteboard-page-title .title')).toContainText(
- 'my-whiteboard-2'
- )
- const pageRefCount$ = page.locator('.whiteboard-page-refs-count')
- await expect(pageRefCount$.locator('.open-page-ref-link')).toContainText('1')
- })
- test('Create an embedded whiteboard', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- await canvas.dblclick({
- position: {
- x: 110,
- y: 110,
- },
- })
- const quickAdd$ = page.locator('.tl-quick-search')
- await expect(quickAdd$).toBeVisible()
- await page.fill('.tl-quick-search input', 'My embedded whiteboard')
- await quickAdd$
- .locator('div[data-index="2"] .tl-quick-search-option')
- .first()
- .click()
- await expect(quickAdd$).toBeHidden()
- await expect(page.locator('.tl-logseq-portal-header a')).toContainText('My embedded whiteboard')
- })
- test('New whiteboard should have the correct name', async ({ page }) => {
- page.locator('.tl-logseq-portal-header a').click()
- await expect(page.locator('.whiteboard-page-title')).toContainText('My embedded whiteboard')
- })
- test('Create an embedded page', async ({ page }) => {
- const canvas = await page.waitForSelector('.logseq-tldraw')
- await canvas.dblclick({
- position: {
- x: 150,
- y: 150,
- },
- })
- const quickAdd$ = page.locator('.tl-quick-search')
- await expect(quickAdd$).toBeVisible()
- await page.fill('.tl-quick-search input', 'My page')
- await quickAdd$
- .locator('div[data-index="1"] .tl-quick-search-option')
- .first()
- .click()
- await expect(quickAdd$).toBeHidden()
- await expect(page.locator('.tl-logseq-portal-header a')).toContainText('My page')
- })
- test('New page should have the correct name', async ({ page }) => {
- page.locator('.tl-logseq-portal-header a').click()
- await expect(page.locator('.ls-page-title')).toContainText('My page')
- })
- test('Renaming a page to an existing whiteboard name should be prohibited', async ({ page }) => {
- await renamePage(page, "My embedded whiteboard")
- await expect(page.locator('.page-title input')).toHaveValue('My page')
- })
|