2023-09-02 08:57:04 +03:00
|
|
|
import { test } from '@affine-test/kit/playwright';
|
2023-09-02 09:06:47 +03:00
|
|
|
import {
|
|
|
|
addUserToWorkspace,
|
|
|
|
createRandomUser,
|
|
|
|
enableCloudWorkspace,
|
2023-10-12 06:26:13 +03:00
|
|
|
enableCloudWorkspaceFromShareButton,
|
2023-09-02 09:06:47 +03:00
|
|
|
loginUser,
|
|
|
|
} from '@affine-test/kit/utils/cloud';
|
2023-11-10 08:32:51 +03:00
|
|
|
import { dropFile } from '@affine-test/kit/utils/drop-file';
|
2023-09-02 08:57:04 +03:00
|
|
|
import {
|
|
|
|
clickNewPageButton,
|
|
|
|
getBlockSuiteEditorTitle,
|
|
|
|
waitForEditorLoad,
|
|
|
|
} from '@affine-test/kit/utils/page-logic';
|
2023-09-12 06:37:59 +03:00
|
|
|
import {
|
|
|
|
clickUserInfoCard,
|
|
|
|
openSettingModal,
|
|
|
|
openWorkspaceSettingPanel,
|
|
|
|
} from '@affine-test/kit/utils/setting';
|
2023-09-06 09:58:05 +03:00
|
|
|
import {
|
|
|
|
clickSideBarAllPageButton,
|
2023-10-16 11:44:09 +03:00
|
|
|
clickSideBarCurrentWorkspaceBanner,
|
2023-09-06 09:58:05 +03:00
|
|
|
clickSideBarSettingButton,
|
|
|
|
} from '@affine-test/kit/utils/sidebar';
|
2023-09-02 08:57:04 +03:00
|
|
|
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
|
|
|
|
import { expect } from '@playwright/test';
|
|
|
|
|
|
|
|
let user: {
|
2023-09-02 09:06:47 +03:00
|
|
|
id: string;
|
2023-09-02 08:57:04 +03:00
|
|
|
name: string;
|
|
|
|
email: string;
|
|
|
|
password: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
test.beforeEach(async () => {
|
|
|
|
user = await createRandomUser();
|
|
|
|
});
|
|
|
|
|
|
|
|
test.beforeEach(async ({ page }) => {
|
2023-09-02 09:06:47 +03:00
|
|
|
await loginUser(page, user.email);
|
2023-09-02 08:57:04 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
test.describe('collaboration', () => {
|
|
|
|
test('can enable share page', async ({ page, browser }) => {
|
|
|
|
await page.reload();
|
|
|
|
await waitForEditorLoad(page);
|
|
|
|
await createLocalWorkspace(
|
|
|
|
{
|
|
|
|
name: 'test',
|
|
|
|
},
|
|
|
|
page
|
|
|
|
);
|
2023-10-12 06:26:13 +03:00
|
|
|
await enableCloudWorkspaceFromShareButton(page);
|
2023-09-02 08:57:04 +03:00
|
|
|
const title = getBlockSuiteEditorTitle(page);
|
2023-10-11 09:05:06 +03:00
|
|
|
await title.pressSequentially('TEST TITLE', {
|
2023-09-02 08:57:04 +03:00
|
|
|
delay: 50,
|
|
|
|
});
|
|
|
|
await page.keyboard.press('Enter', { delay: 50 });
|
|
|
|
await page.keyboard.type('TEST CONTENT', { delay: 50 });
|
2023-10-12 06:26:13 +03:00
|
|
|
await page.getByTestId('cloud-share-menu-button').click();
|
2023-09-02 08:57:04 +03:00
|
|
|
await page.getByTestId('share-menu-create-link-button').click();
|
|
|
|
await page.getByTestId('share-menu-copy-link-button').click();
|
|
|
|
|
|
|
|
// check share page is accessible
|
|
|
|
{
|
|
|
|
const context = await browser.newContext();
|
|
|
|
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);
|
|
|
|
expect(await title.innerText()).toBe('TEST TITLE');
|
|
|
|
expect(await page2.textContent('affine-paragraph')).toContain(
|
|
|
|
'TEST CONTENT'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
});
|
2023-09-02 09:06:47 +03:00
|
|
|
|
2023-09-07 08:02:21 +03:00
|
|
|
test('can collaborate with other user and name should display when editing', async ({
|
|
|
|
page,
|
|
|
|
browser,
|
|
|
|
}) => {
|
2023-09-02 09:06:47 +03:00
|
|
|
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();
|
|
|
|
const page2 = await context.newPage();
|
|
|
|
await loginUser(page2, userB.email);
|
|
|
|
await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */);
|
|
|
|
await page2.reload();
|
|
|
|
await waitForEditorLoad(page2);
|
|
|
|
await page2.goto(currentUrl);
|
|
|
|
{
|
|
|
|
const title = getBlockSuiteEditorTitle(page);
|
2023-10-11 09:05:06 +03:00
|
|
|
await title.pressSequentially('TEST TITLE', {
|
2023-09-02 09:06:47 +03:00
|
|
|
delay: 50,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
await page2.waitForTimeout(200);
|
|
|
|
{
|
|
|
|
const title = getBlockSuiteEditorTitle(page2);
|
|
|
|
expect(await title.innerText()).toBe('TEST TITLE');
|
2023-09-07 08:02:21 +03:00
|
|
|
const typingPromise = Promise.all([
|
|
|
|
page.keyboard.press('Enter', { delay: 50 }),
|
|
|
|
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();
|
2023-10-11 09:05:06 +03:00
|
|
|
await input.pressSequentially('TEST USER', {
|
2023-09-07 08:02:21 +03:00
|
|
|
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 expect(page2.getByText('TEST USER')).toBeVisible({
|
|
|
|
timeout: 2000,
|
|
|
|
});
|
2023-09-02 09:06:47 +03:00
|
|
|
}
|
|
|
|
});
|
2023-09-06 09:58:05 +03:00
|
|
|
|
2023-09-09 01:02:22 +03:00
|
|
|
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('slider-bar-add-collection-button').click();
|
|
|
|
const title = page.getByTestId('input-collection-title');
|
|
|
|
await title.isVisible();
|
|
|
|
await title.fill('test collection');
|
|
|
|
await page.getByTestId('save-collection').click();
|
|
|
|
|
|
|
|
{
|
|
|
|
const context = await browser.newContext();
|
|
|
|
const page2 = await context.newPage();
|
|
|
|
await loginUser(page2, user.email);
|
|
|
|
await page2.goto(page.url());
|
|
|
|
const collections = page2.getByTestId('collections');
|
|
|
|
await expect(collections.getByText('test collection')).toBeVisible();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-09-06 09:58:05 +03:00
|
|
|
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();
|
2023-10-16 11:44:09 +03:00
|
|
|
await page.getByTestId('confirm-sign-out-button').click();
|
2023-09-06 09:58:05 +03:00
|
|
|
await page.waitForTimeout(5000);
|
|
|
|
expect(page.url()).toBe(url);
|
|
|
|
});
|
2023-09-02 08:57:04 +03:00
|
|
|
});
|
2023-09-12 06:37:59 +03:00
|
|
|
|
|
|
|
test.describe('collaboration members', () => {
|
|
|
|
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(
|
|
|
|
new Array(10).fill(1).map(() => createUserAndAddToWorkspace())
|
|
|
|
);
|
|
|
|
|
|
|
|
await openSettingModal(page);
|
|
|
|
await openWorkspaceSettingPanel(page, 'test');
|
|
|
|
|
2023-09-22 23:18:41 +03:00
|
|
|
await page.waitForTimeout(1000);
|
2023-09-12 06:37:59 +03:00
|
|
|
|
|
|
|
const firstPageMemberItemCount = await page
|
|
|
|
.locator('[data-testid="member-item"]')
|
|
|
|
.count();
|
|
|
|
|
|
|
|
expect(firstPageMemberItemCount).toBe(8);
|
|
|
|
|
|
|
|
const navigationItems = await page
|
|
|
|
.getByRole('navigation')
|
|
|
|
.getByRole('button')
|
|
|
|
.all();
|
|
|
|
|
2023-09-15 22:24:00 +03:00
|
|
|
// make sure the first member is the owner
|
2023-10-11 09:05:06 +03:00
|
|
|
await expect(page.getByTestId('member-item').first()).toContainText(
|
2023-09-15 22:24:00 +03:00
|
|
|
'Workspace Owner'
|
|
|
|
);
|
|
|
|
|
2023-09-12 06:37:59 +03:00
|
|
|
// 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);
|
|
|
|
});
|
|
|
|
});
|
2023-10-16 11:44:09 +03:00
|
|
|
|
|
|
|
test.describe('sign out', () => {
|
|
|
|
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 clickSideBarCurrentWorkspaceBanner(page);
|
|
|
|
await page.getByTestId('workspace-modal-account-option').click();
|
|
|
|
await page.getByTestId('workspace-modal-sign-out-option').click();
|
|
|
|
await page.getByTestId('confirm-sign-out-button').click();
|
|
|
|
await clickSideBarCurrentWorkspaceBanner(page);
|
|
|
|
const signInButton = page.getByTestId('cloud-signin-button');
|
|
|
|
await expect(signInButton).toBeVisible();
|
|
|
|
expect(page.url()).toBe(currentUrl);
|
|
|
|
});
|
|
|
|
});
|
2023-11-10 08:32:51 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
// drop an svg file
|
|
|
|
const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
|
|
|
|
<rect x="0" y="0" width="200" height="200" fill="red" />
|
|
|
|
</svg>`;
|
|
|
|
|
|
|
|
await dropFile(page, 'affine-paragraph', svg, 'test.svg', 'image/svg+xml');
|
|
|
|
|
|
|
|
{
|
|
|
|
const context = await browser.newContext();
|
|
|
|
const page2 = await context.newPage();
|
|
|
|
await loginUser(page2, user.email);
|
|
|
|
await page2.goto(page.url());
|
|
|
|
|
|
|
|
// the user should see the svg
|
|
|
|
// get the image src under "affine-image img"
|
|
|
|
const src = await page2.locator('affine-image img').getAttribute('src');
|
|
|
|
|
|
|
|
expect(src).not.toBeNull();
|
|
|
|
|
|
|
|
// fetch the src resource in the browser
|
|
|
|
const svg2 = await page2.evaluate(async src => {
|
|
|
|
async function blobToString(blob: Blob) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
const reader = new FileReader();
|
|
|
|
reader.onloadend = () => resolve(reader.result);
|
|
|
|
reader.onerror = reject;
|
|
|
|
reader.readAsText(blob);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
const blob = fetch(src!).then(res => res.blob());
|
|
|
|
return blobToString(await blob);
|
|
|
|
}, src);
|
|
|
|
|
|
|
|
// turn the blob into string and check if it contains the svg
|
|
|
|
expect(svg2).toContain(svg);
|
|
|
|
}
|
|
|
|
});
|