feat: online ci

This commit is contained in:
DarkSky
2024-11-07 12:20:08 +08:00
parent f5df67501c
commit e96f89c26b
27 changed files with 490 additions and 232 deletions
@@ -0,0 +1,210 @@
import { Path, skipOnboarding, test } from '@affine-test/kit/playwright';
import {
addUserToWorkspace,
createRandomUser,
enableCloudWorkspace,
loginUser,
} from '@affine-test/kit/utils/cloud';
import {
clickNewPageButton,
getBlockSuiteEditorTitle,
waitForEditorLoad,
} from '@affine-test/kit/utils/page-logic';
import { clickUserInfoCard } from '@affine-test/kit/utils/setting';
import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar';
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
import { expect } from '@playwright/test';
let user: {
id: string;
name: string;
email: string;
password: string;
};
test.beforeEach(async () => {
user = await createRandomUser();
});
test.beforeEach(async ({ page }) => {
await loginUser(page, user);
});
// SKIP until BS-671 fix
test.skip('can collaborate with other user and name should display when editing', async ({
page,
browser,
}) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
await clickNewPageButton(page);
const currentUrl = page.url();
// format: http://localhost:8080/workspace/${workspaceId}/xxx
const workspaceId = currentUrl.split('/')[4];
const userB = await createRandomUser();
const context = await browser.newContext();
await skipOnboarding(context);
const page2 = await context.newPage();
await loginUser(page2, userB);
await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */);
await page2.reload();
await waitForEditorLoad(page2);
await page2.goto(currentUrl);
{
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
}
await page2.waitForTimeout(200);
{
const title = getBlockSuiteEditorTitle(page2);
await expect(title).toHaveText('TEST TITLE');
const typingPromise = (async () => {
await page.keyboard.press('Enter', { delay: 50 });
await page.keyboard.type('TEST CONTENT', { delay: 50 });
})();
// username should be visible when editing
await expect(page2.getByText(user.name)).toBeVisible();
await typingPromise;
}
// change username
await clickSideBarSettingButton(page);
await clickUserInfoCard(page);
const input = page.getByTestId('user-name-input');
await input.clear();
await input.pressSequentially('TEST USER', {
delay: 50,
});
await page.getByTestId('save-user-name').click({
delay: 50,
});
await page.keyboard.press('Escape', {
delay: 50,
});
const title = getBlockSuiteEditorTitle(page);
await title.focus();
await page.keyboard.press('ArrowDown', { delay: 50 });
{
await expect(page2.getByText('TEST USER')).toBeVisible({
timeout: 2000,
});
}
});
test('can sync collections between different browser', async ({
page,
browser,
}) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
await page.getByTestId('explorer-bar-add-collection-button').click();
const title = page.getByTestId('prompt-modal-input');
await title.isVisible();
await title.fill('test collection');
await page.getByTestId('prompt-modal-confirm').click();
{
const context = await browser.newContext();
await skipOnboarding(context);
const page2 = await context.newPage();
await loginUser(page2, user);
await page2.goto(page.url());
const collections = page2.getByTestId('explorer-collections');
await collections.getByTestId('category-divider-collapse-button').click();
await expect(collections.getByText('test collection')).toBeVisible();
}
});
test('can sync svg between different browsers', async ({ page, browser }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
await clickNewPageButton(page);
await waitForEditorLoad(page);
// upload local svg
const slashMenu = page.locator(`.slash-menu`);
const image = page.locator('affine-image');
await page.evaluate(async () => {
// https://github.com/toeverything/blocksuite/blob/master/packages/blocks/src/_common/utils/filesys.ts#L20
(window as any).showOpenFilePicker = undefined;
});
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
await page.keyboard.press('Enter', { delay: 50 });
await page.waitForTimeout(100);
await page.keyboard.type('/', { delay: 50 });
await expect(slashMenu).toBeVisible();
await page.keyboard.type('image', { delay: 100 });
await expect(slashMenu).toBeVisible();
const fileChooserPromise = page.waitForEvent('filechooser');
await page.keyboard.press('Enter', { delay: 50 });
const fileChooser = await fileChooserPromise;
fileChooser.setFiles(Path.dir(import.meta.url).join('logo.svg').value);
await expect(image).toBeVisible();
// the user should see the svg
// get the image src under "affine-image img"
const src1 = await page.locator('affine-image img').getAttribute('src');
expect(src1).not.toBeNull();
// fetch the actual src1 resource in the browser
const svg1 = await page.evaluate(
src =>
fetch(src!)
.then(res => res.blob())
.then(blob => blob.text()),
src1
);
{
const context = await browser.newContext();
await skipOnboarding(context);
const page2 = await context.newPage();
await loginUser(page2, user);
await page2.goto(page.url());
// second user should see the svg
// get the image src under "affine-image img"
const src2 = await page2.locator('affine-image img').getAttribute('src');
expect(src2).not.toBeNull();
// fetch the actual src2 resource in the browser
const svg2 = await page2.evaluate(
src =>
fetch(src!)
.then(res => res.blob())
.then(blob => blob.text()),
src2
);
expect(svg2).toEqual(svg1);
}
});
File diff suppressed because one or more lines are too long
+110
View File
@@ -0,0 +1,110 @@
import { test } from '@affine-test/kit/playwright';
import {
createRandomUser,
enableCloudWorkspace,
loginUser,
} from '@affine-test/kit/utils/cloud';
import { openHomePage } from '@affine-test/kit/utils/load-page';
import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic';
import { clickUserInfoCard } from '@affine-test/kit/utils/setting';
import {
clickSideBarAllPageButton,
clickSideBarSettingButton,
clickSideBarUseAvatar,
} from '@affine-test/kit/utils/sidebar';
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
import { expect } from '@playwright/test';
test('can open login modal in workspace list', async ({ page }) => {
await openHomePage(page);
await waitForEditorLoad(page);
await page.getByTestId('sidebar-user-avatar').click({
delay: 200,
});
await expect(page.getByTestId('auth-modal')).toBeVisible();
});
test.describe('login first', () => {
let user: {
id: string;
name: string;
email: string;
password: string;
};
test.beforeEach(async ({ page }) => {
user = await createRandomUser();
await loginUser(page, user);
});
test('exit successfully and re-login', async ({ page }) => {
await page.reload();
await clickSideBarAllPageButton(page);
await page.waitForTimeout(200);
const url = page.url();
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
await clickSideBarSettingButton(page);
await clickUserInfoCard(page);
await page.getByTestId('sign-out-button').click();
await page.getByTestId('confirm-sign-out-button').click();
await page.waitForTimeout(5000);
expect(page.url()).toBe(url);
});
test('can sign out', async ({ page }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await clickSideBarAllPageButton(page);
const currentUrl = page.url();
await clickSideBarUseAvatar(page);
await page.getByTestId('workspace-modal-sign-out-option').click();
await page.getByTestId('confirm-sign-out-button').click();
await page.reload();
await clickSideBarUseAvatar(page);
const authModal = page.getByTestId('auth-modal');
await expect(authModal).toBeVisible();
expect(page.url()).toBe(currentUrl);
});
test('can see and change email and password in setting panel', async ({
page,
}) => {
const newName = 'test name';
{
await clickSideBarSettingButton(page);
const locator = page.getByTestId('user-info-card');
expect(locator.getByText(user.email)).toBeTruthy();
expect(locator.getByText(user.name)).toBeTruthy();
await locator.click({
delay: 50,
});
const nameInput = page.getByPlaceholder('Input account name');
await nameInput.clear();
await nameInput.pressSequentially(newName, {
delay: 50,
});
await page.getByTestId('save-user-name').click({
delay: 50,
});
}
await page.reload();
{
await clickSideBarSettingButton(page);
const locator = page.getByTestId('user-info-card');
expect(locator.getByText(user.email)).toBeTruthy();
expect(locator.getByText(newName)).toBeTruthy();
}
});
});
+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#000000;}</style></defs><g id="_20230202-pull_request"><path class="cls-1" d="m512,133.82L342.5,0,0,67.88v310.26l169.56,133.86,342.44-67.88V133.82Zm-203.39-84.81v89.67l51.84-10.27v-56.25l74.87,59.09-249.93,49.53-108.71-85.8,231.93-45.96ZM51.84,133.32l99.71,78.7v93.92l51.84-10.27v-76.15l256.78-50.89v210.06l-99.71-78.7v-93.92l-51.84,10.27v76.16l-256.78,50.89v-210.06Zm151.55,329.67v-89.67l-51.84,10.27v56.25l-74.87-59.09,249.93-49.53,108.71,85.8-231.93,45.96Z"/></g></svg>

After

Width:  |  Height:  |  Size: 608 B

@@ -0,0 +1,109 @@
import { readFile } from 'node:fs/promises';
import { Path, test } from '@affine-test/kit/playwright';
import {
createRandomUser,
deleteUser,
enableCloudWorkspace,
getLoginCookie,
loginUser,
runPrisma,
} from '@affine-test/kit/utils/cloud';
import { coreUrl } from '@affine-test/kit/utils/load-page';
import {
clickNewPageButton,
waitForEditorLoad,
} from '@affine-test/kit/utils/page-logic';
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
import { expect } from '@playwright/test';
let user: {
id: string;
name: string;
email: string;
password: string;
};
test.beforeEach(async () => {
user = await createRandomUser();
});
test.beforeEach(async ({ page, context }) => {
await loginUser(page, user, {
beforeLogin: async () => {
expect(await getLoginCookie(context)).toBeUndefined();
},
afterLogin: async () => {
expect(await getLoginCookie(context)).toBeTruthy();
await page.reload();
await waitForEditorLoad(page);
expect(await getLoginCookie(context)).toBeTruthy();
},
});
});
test.afterEach(async () => {
// if you want to keep the user in the database for debugging,
// comment this line
await deleteUser(user.email);
});
// TODO(@eyhn) mock migration from server data after page level upgrade implemented.
test.skip('migration', async ({ page, browser }) => {
let workspaceId: string;
{
// create the old cloud workspace in another browser
const context = await browser.newContext();
const page = await context.newPage();
await loginUser(page, user);
await page.reload();
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
await clickNewPageButton(page);
await waitForEditorLoad(page);
// http://localhost:8080/workspace/2bc0b6c8-f68d-4dd3-98a8-be746754f9e1/xxx
workspaceId = page.url().split('/')[4];
await runPrisma(async client => {
const sqls = (
await readFile(
Path.dir(import.meta.url).join(
'fixtures',
'0.9.0-canary.9-snapshots.sql'
).value,
'utf-8'
)
)
.replaceAll('2bc0b6c8-f68d-4dd3-98a8-be746754f9e1', workspaceId)
.split('\n');
await client.snapshot.deleteMany({
where: {
workspaceId,
},
});
for (const sql of sqls) {
await client.$executeRawUnsafe(sql);
}
});
await page.close();
}
await page.reload();
await page.waitForTimeout(1000);
await page.goto(`${coreUrl}/workspace/${workspaceId}/all`);
await page.getByTestId('upgrade-workspace-button').click();
await expect(page.getByText('Refresh Current Page')).toBeVisible({
timeout: 60000,
});
await page.goto(
// page 'F1SX6cgNxy' has edgeless mode
`${coreUrl}/workspace/${workspaceId}/F1SX6cgNxy`
);
await page.waitForTimeout(5000);
await page.reload();
await waitForEditorLoad(page);
});
@@ -0,0 +1,59 @@
import { test } from '@affine-test/kit/playwright';
import {
createRandomUser,
deleteUser,
enableCloudWorkspace,
loginUser,
} from '@affine-test/kit/utils/cloud';
import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic';
import { expect } from '@playwright/test';
let user: {
id: string;
name: string;
email: string;
password: string;
};
test.beforeEach(async ({ page }) => {
user = await createRandomUser();
await loginUser(page, user);
await enableCloudWorkspace(page);
await waitForEditorLoad(page);
await page.reload();
await waitForEditorLoad(page);
});
test.afterEach(async () => {
// if you want to keep the user in the database for debugging,
// comment this line
await deleteUser(user.email);
});
test('open in app card should be shown for cloud workspace', async ({
page,
}) => {
await expect(page.getByTestId('open-in-app-card')).toBeVisible();
await page
.getByTestId('open-in-app-card')
.getByRole('checkbox', {
name: 'Remember choice',
})
.locator('input')
.click();
await page
.getByRole('button', {
name: 'Dismiss',
})
.click();
await expect(page.getByTestId('open-in-app-card')).not.toBeInViewport();
await page.reload();
await expect(page.getByTestId('open-in-app-card')).not.toBeInViewport();
// seems there is no way to bypass the open popup blocker via playwright
});
@@ -0,0 +1,156 @@
import { test } from '@affine-test/kit/playwright';
import {
createRandomUser,
deleteUser,
enableCloudWorkspace,
loginUser,
runPrisma,
} from '@affine-test/kit/utils/cloud';
import {
getBlockSuiteEditorTitle,
waitForEditorLoad,
} from '@affine-test/kit/utils/page-logic';
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';
let user: {
id: string;
name: string;
email: string;
password: string;
};
test.beforeEach(async ({ page }) => {
user = await createRandomUser();
await loginUser(page, user);
});
test.afterEach(async () => {
// if you want to keep the user in the database for debugging,
// comment this line
await deleteUser(user.email);
});
test('newly created page shows empty history', async ({ page }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
// click the history button
await page.getByTestId('header-dropDownButton').click();
await page.getByTestId('editor-option-menu-history').click();
const modal = page.getByTestId('page-history-modal');
// expect history modal shown up
await expect(modal).toBeVisible();
await expect(page.getByTestId('empty-history-prompt')).toBeVisible();
});
const pushCurrentPageUpdates = async (page: Page) => {
const [workspaceId, guid, updates, state] = await page.evaluate(() => {
// @ts-expect-error
const Y = window.Y;
// @ts-expect-error
const spaceDoc = window.currentEditor.page.spaceDoc;
// @ts-expect-error
const workspaceId: string = window.currentWorkspace.id;
const updates: Uint8Array = Y.encodeStateAsUpdate(spaceDoc);
const state: Uint8Array = Y.encodeStateVector(spaceDoc);
return [workspaceId, spaceDoc.guid, [...updates], [...state]] as const;
});
const toBuffer = (arr: readonly number[]) => Buffer.from(arr);
await runPrisma(async client => {
await client.snapshotHistory.create({
data: {
workspaceId: workspaceId,
id: guid,
blob: toBuffer(updates),
state: toBuffer(state),
timestamp: new Date(Date.now() - 10000),
expiredAt: new Date(Date.now() + 100000),
},
});
});
};
test('can restore page to a history version', async ({ page }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
await pushCurrentPageUpdates(page);
const title = getBlockSuiteEditorTitle(page);
await title.focus();
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
// write something and push to history
await pushCurrentPageUpdates(page);
await title.selectText();
await title.press('Backspace');
await title.pressSequentially('New Title', {
delay: 50,
});
// click the history button
await page.getByTestId('header-dropDownButton').click();
await page.getByTestId('editor-option-menu-history').click();
const modal = page.getByTestId('page-history-modal');
// expect history modal shown up
await expect(modal).toBeVisible();
// expect history list to have 2 items
await expect(modal.getByTestId('version-history-item')).toHaveCount(2);
// check the first item in the preview should have title 'TEST TITLE'
await expect(modal.locator('[data-block-is-title]')).toHaveText('TEST TITLE');
// click restore
await modal
.getByRole('button', {
name: 'Restore current version',
})
.click();
const confirm = page.getByTestId('confirm-restore-history-modal');
// expect confirm dialog to show up
await expect(confirm).toBeVisible();
// click restore
await confirm
.getByRole('button', {
name: 'Restore',
})
.click();
// title should be restored to 'TEST TITLE'
await expect(title).toContainText('TEST TITLE');
});
@@ -0,0 +1,407 @@
import { skipOnboarding, test } from '@affine-test/kit/playwright';
import {
createRandomUser,
enableCloudWorkspaceFromShareButton,
enableShare,
loginUser,
} from '@affine-test/kit/utils/cloud';
import { clickEdgelessModeButton } from '@affine-test/kit/utils/editor';
import { importImage } from '@affine-test/kit/utils/image';
import {
clickNewPageButton,
getBlockSuiteEditorTitle,
waitForEditorLoad,
} from '@affine-test/kit/utils/page-logic';
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
import { expect } from '@playwright/test';
let user: {
id: string;
name: string;
email: string;
password: string;
};
test.beforeEach(async ({ page }) => {
user = await createRandomUser();
await loginUser(page, user);
});
test('can enable share page', async ({ page, browser }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspaceFromShareButton(page);
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
await page.keyboard.press('Enter', { delay: 50 });
await page.keyboard.type('TEST CONTENT', { delay: 50 });
// enable share page and copy page link
await enableShare(page);
await page.getByTestId('share-menu-copy-link-button').click();
await page.getByTestId('share-link-menu-copy-page').click();
// check share page is accessible
{
const context = await browser.newContext();
await skipOnboarding(context);
const url: string = await page.evaluate(() =>
navigator.clipboard.readText()
);
const page2 = await context.newPage();
await page2.goto(url);
await waitForEditorLoad(page2);
const title = getBlockSuiteEditorTitle(page2);
await expect(title).toContainText('TEST TITLE');
expect(page2.locator('affine-paragraph').first()).toContainText(
'TEST CONTENT'
);
}
});
test('share page should have toc', async ({ page, browser }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspaceFromShareButton(page);
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
await page.keyboard.press('Enter', { delay: 50 });
await page.keyboard.type('# Heading 1');
await page.keyboard.press('Enter');
await page.keyboard.type('# Heading 2');
await page.keyboard.press('Enter');
// enable share page and copy page link
await enableShare(page);
await page.getByTestId('share-menu-copy-link-button').click();
await page.getByTestId('share-link-menu-copy-page').click();
// check share page is accessible
{
const context = await browser.newContext();
await skipOnboarding(context);
const url: string = await page.evaluate(() =>
navigator.clipboard.readText()
);
const page2 = await context.newPage();
await page2.goto(url);
await waitForEditorLoad(page2);
const tocIndicators = page2.locator(
'affine-outline-viewer .outline-viewer-indicator'
);
await expect(tocIndicators).toHaveCount(3);
await expect(tocIndicators.nth(0)).toBeVisible();
await expect(tocIndicators.nth(1)).toBeVisible();
await expect(tocIndicators.nth(2)).toBeVisible();
const viewer = page2.locator('affine-outline-viewer');
await tocIndicators.first().hover({ force: true });
await expect(viewer).toBeVisible();
const toggleButton = viewer.locator(
'[data-testid="toggle-outline-panel-button"]'
);
await expect(toggleButton).toHaveCount(0);
}
});
test('append paragraph should be disabled in shared mode', async ({
page,
browser,
}) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspaceFromShareButton(page);
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
// enable share page and copy page link
await enableShare(page);
await page.getByTestId('share-menu-copy-link-button').click();
await page.getByTestId('share-link-menu-copy-page').click();
{
const context = await browser.newContext();
await skipOnboarding(context);
const url: string = await page.evaluate(() =>
navigator.clipboard.readText()
);
const page2 = await context.newPage();
await page2.goto(url);
await waitForEditorLoad(page2);
const paragraph = page2.locator('affine-paragraph');
const numParagraphs = await paragraph.count();
let error = null;
try {
await page2.locator('[data-testid=page-editor-blank]').click();
} catch (e) {
error = e;
}
expect(error).toBeNull();
expect(await paragraph.count()).toBe(numParagraphs);
}
});
test('share page with default edgeless', async ({ page, browser }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspaceFromShareButton(page);
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
await page.keyboard.press('Enter', { delay: 50 });
await page.keyboard.type('TEST CONTENT', { delay: 50 });
await clickEdgelessModeButton(page);
await expect(page.locator('affine-edgeless-root')).toBeVisible({
timeout: 1000,
});
// enable share page and copy page link
await enableShare(page);
await page.getByTestId('share-menu-copy-link-button').click();
await page.getByTestId('share-link-menu-copy-edgeless').click();
// check share page is accessible
{
const context = await browser.newContext();
await skipOnboarding(context);
const url: string = await page.evaluate(() =>
navigator.clipboard.readText()
);
const page2 = await context.newPage();
await page2.goto(url);
await waitForEditorLoad(page2);
await expect(page.locator('affine-edgeless-root')).toBeVisible({
timeout: 1000,
});
expect(page2.locator('affine-paragraph').first()).toContainText(
'TEST CONTENT'
);
}
});
test('image preview should be shown', async ({ page, browser }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspaceFromShareButton(page);
const title = getBlockSuiteEditorTitle(page);
await title.click();
await page.keyboard.press('Enter');
await importImage(page, 'large-image.png');
// enable share page and copy page link
await enableShare(page);
await page.getByTestId('share-menu-copy-link-button').click();
await page.getByTestId('share-link-menu-copy-page').click();
// check share page is accessible
{
const context = await browser.newContext();
await skipOnboarding(context);
const url: string = await page.evaluate(() =>
navigator.clipboard.readText()
);
const page2 = await context.newPage();
await page2.goto(url);
await waitForEditorLoad(page2);
await page.locator('affine-page-image').first().dblclick();
const locator = page.getByTestId('image-preview-modal');
await expect(locator).toBeVisible();
await page.getByTestId('image-preview-close-button').first().click();
await page.waitForTimeout(500);
await expect(locator).not.toBeVisible();
}
});
test('The reference links in the shared page should be accessible normally and can go back and forward', async ({
page,
browser,
}) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspaceFromShareButton(page);
// create linked page and share
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('Test linked doc', {
delay: 50,
});
await page.keyboard.press('Enter', { delay: 50 });
await page.keyboard.type('Test linked content', { delay: 50 });
await enableShare(page);
// create a new page and link to the shared page
await clickNewPageButton(page, 'Test Page');
await waitForEditorLoad(page);
await page.keyboard.press('Enter');
await page.keyboard.type('@', { delay: 50 });
const linkedPagePopover = page.locator('.linked-doc-popover');
await expect(linkedPagePopover).toBeVisible();
await page.keyboard.type('Test linked doc', { delay: 50 });
await page.locator('icon-button:has-text("Test linked doc")').first().click();
// enable share page and copy page link
await enableShare(page);
await page.getByTestId('share-menu-copy-link-button').click();
await page.getByTestId('share-link-menu-copy-page').click();
// check share page is accessible
{
const context = await browser.newContext();
await skipOnboarding(context);
const url: string = await page.evaluate(() =>
navigator.clipboard.readText()
);
const page2 = await context.newPage();
await page2.goto(url);
await waitForEditorLoad(page2);
const title = getBlockSuiteEditorTitle(page2);
await expect(title).toContainText('Test Page');
// check linked page
const link = page2.locator('.affine-reference');
await expect(link).toBeVisible();
await expect(link).toContainText('Test linked doc');
await link.click();
await waitForEditorLoad(page2);
await expect(
page2.locator('.doc-title-container:has-text("Test linked doc")')
).toBeVisible();
await expect(page2.locator('affine-paragraph').first()).toContainText(
'Test linked content'
);
// go back and forward
await page2.goBack();
await waitForEditorLoad(page2);
await expect(title).toContainText('Test Page');
await expect(link).toBeVisible();
await expect(link).toContainText('Test linked doc');
await page2.goForward();
await waitForEditorLoad(page2);
await expect(
page2.locator('.doc-title-container:has-text("Test linked doc")')
).toBeVisible();
await expect(page2.locator('affine-paragraph').first()).toContainText(
'Test linked content'
);
}
});
test('Should show no permission page when the share page is not found', async ({
page,
}) => {
await page.goto('http://localhost:8080/workspace/abc/123');
await expect(
page.getByText('You do not have access or this content does not exist.')
).toBeVisible();
});
test('Inline latex modal should be not shown in shared mode when clicking', async ({
page,
browser,
}) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspaceFromShareButton(page);
const title = getBlockSuiteEditorTitle(page);
await title.pressSequentially('TEST TITLE', {
delay: 50,
});
await page.keyboard.press('Enter', { delay: 50 });
await page.keyboard.type('$$E=mc^2$$');
await page.keyboard.press('Space');
// there should be a inline latex node
const latexLocator = page.locator('affine-latex-node');
await expect(latexLocator).toBeVisible();
// click the latex node
// the latex editor should be shown when the doc can be editing
await latexLocator.click();
const modalLocator = page.locator('.latex-editor-container');
await expect(modalLocator).toBeVisible();
// enable share page and copy page link
await enableShare(page);
await page.getByTestId('share-menu-copy-link-button').click();
await page.getByTestId('share-link-menu-copy-page').click();
// check share page is accessible
{
const context = await browser.newContext();
await skipOnboarding(context);
const url: string = await page.evaluate(() =>
navigator.clipboard.readText()
);
const page2 = await context.newPage();
await page2.goto(url);
await waitForEditorLoad(page2);
// click the latex node
const latexLocator = page2.locator('affine-latex-node');
await latexLocator.click();
// the latex editor should not be shown when the doc is readonly
const modalLocator = page2.locator('.latex-editor-container');
await expect(modalLocator).not.toBeVisible();
}
});
@@ -0,0 +1,33 @@
import { test } from '@affine-test/kit/playwright';
import { createRandomUser, loginUser } from '@affine-test/kit/utils/cloud';
import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic';
test.beforeEach(async ({ page }) => {
const user = await createRandomUser();
await loginUser(page, user);
});
test('import from template should work', async ({ page }) => {
await page.goto('https://affine.pro/templates', { waitUntil: 'load' });
await page.click('.template-list > a:first-child');
const importLink = page.getByText('Use this template');
const href = await importLink.evaluate((el: HTMLElement) => {
const a = el.closest('a');
if (!a) {
throw new Error('Import link not found');
}
return a.href;
});
const url = new URL(href);
await page.goto(url.pathname + url.search);
const btn = page.getByTestId('import-template-to-workspace-btn');
await btn.isVisible();
btn.click();
await waitForEditorLoad(page);
});
@@ -0,0 +1,129 @@
import { test } from '@affine-test/kit/playwright';
import {
addUserToWorkspace,
createRandomUser,
enableCloudWorkspace,
loginUser,
} from '@affine-test/kit/utils/cloud';
import { clickPageModeButton } from '@affine-test/kit/utils/editor';
import {
clickNewPageButton,
getBlockSuiteEditorTitle,
waitForEditorLoad,
waitForEmptyEditor,
} from '@affine-test/kit/utils/page-logic';
import { openSettingModal } from '@affine-test/kit/utils/setting';
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
import { expect } from '@playwright/test';
let user: {
id: string;
name: string;
email: string;
password: string;
};
test.beforeEach(async ({ page }) => {
user = await createRandomUser();
await loginUser(page, user);
});
test('should have pagination in member list', async ({ page }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await enableCloudWorkspace(page);
await clickNewPageButton(page);
const currentUrl = page.url();
// format: http://localhost:8080/workspace/${workspaceId}/xxx
const workspaceId = currentUrl.split('/')[4];
// create 10 user and add to workspace
const createUserAndAddToWorkspace = async () => {
const userB = await createRandomUser();
await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */);
};
await Promise.all(
Array.from({ length: 10 })
.fill(1)
.map(() => createUserAndAddToWorkspace())
);
await openSettingModal(page);
await page
.getByTestId('settings-sidebar')
.getByTestId('workspace-setting:members')
.click();
await page.waitForTimeout(1000);
await page.getByTestId('confirm-modal-cancel').click();
const firstPageMemberItemCount = await page
.locator('[data-testid="member-item"]')
.count();
expect(firstPageMemberItemCount).toBe(8);
const navigationItems = await page
.getByRole('navigation')
.getByRole('button')
.all();
// make sure the first member is the owner
await expect(page.getByTestId('member-item').first()).toContainText(
'Workspace Owner'
);
// There have four pagination items: < 1 2 >
expect(navigationItems.length).toBe(4);
// Click second page
await navigationItems[2].click();
await page.waitForTimeout(500);
// There should have other three members in second page
const secondPageMemberItemCount = await page
.locator('[data-testid="member-item"]')
.count();
expect(secondPageMemberItemCount).toBe(3);
// Click left arrow to back to first page
await navigationItems[0].click();
await page.waitForTimeout(500);
expect(await page.locator('[data-testid="member-item"]').count()).toBe(8);
// Click right arrow to second page
await navigationItems[3].click();
await page.waitForTimeout(500);
expect(await page.locator('[data-testid="member-item"]').count()).toBe(3);
});
test('should transform local favorites data', async ({ page }) => {
await page.reload();
await waitForEditorLoad(page);
await createLocalWorkspace(
{
name: 'test',
},
page
);
await page.getByTestId('explorer-bar-add-favorite-button').first().click();
await clickPageModeButton(page);
await waitForEmptyEditor(page);
await getBlockSuiteEditorTitle(page).fill('this is a new fav page');
await expect(
page
.getByTestId('explorer-favorites')
.locator('[draggable] >> text=this is a new fav page')
).toBeVisible();
await enableCloudWorkspace(page);
await expect(
page
.getByTestId('explorer-favorites')
.locator('[draggable] >> text=this is a new fav page')
).toBeVisible();
});