mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-26 02:51:57 +03:00
test: support test e2e with OctoBase (#1593)
Co-authored-by: DarkSky <darksky2048@gmail.com>
This commit is contained in:
parent
110cec7bf6
commit
94d535f72b
17
.github/workflows/build.yml
vendored
17
.github/workflows/build.yml
vendored
@ -72,13 +72,28 @@ jobs:
|
||||
shard: [1, 2, 3, 4]
|
||||
environment: development
|
||||
needs: build
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
services:
|
||||
octobase:
|
||||
image: ghcr.io/toeverything/cloud:nightly-latest
|
||||
ports:
|
||||
- 3000:3000
|
||||
env:
|
||||
SIGN_KEY: 'test123'
|
||||
RUST_LOG: 'debug'
|
||||
JWST_DEV: '1'
|
||||
credentials:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.ACTIONS_PACKAGE_PUBLISH }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: pnpm/action-setup@v2
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
node-version: 18
|
||||
cache: 'pnpm'
|
||||
- run: pnpm i
|
||||
|
||||
|
@ -36,6 +36,7 @@ export const TransformWorkspaceToAffineModal: React.FC<
|
||||
{/* <StyleTips>{t('Retain cached cloud data')}</StyleTips> */}
|
||||
<div>
|
||||
<StyleButton
|
||||
data-testid="confirm-enable-cloud-button"
|
||||
shape="round"
|
||||
type="primary"
|
||||
onClick={async () => {
|
||||
|
@ -53,7 +53,8 @@ const AffineRemoteCollaborationPanel: React.FC<
|
||||
<ul>
|
||||
<StyledMemberTitleContainer>
|
||||
<StyledMemberNameContainer>
|
||||
{t('Users')} ({members.length})
|
||||
{t('Users')} (
|
||||
<span data-testid="member-length">{members.length}</span>)
|
||||
</StyledMemberNameContainer>
|
||||
<StyledMemberRoleContainer>
|
||||
{t('Access level')}
|
||||
@ -145,6 +146,7 @@ const AffineRemoteCollaborationPanel: React.FC<
|
||||
setIsInviteModalShow(true);
|
||||
}}
|
||||
type="primary"
|
||||
data-testid="invite-members"
|
||||
shape="circle"
|
||||
>
|
||||
{t('Invite Members')}
|
||||
@ -176,6 +178,7 @@ const LocalCollaborationPanel: React.FC<
|
||||
<>
|
||||
<Wrapper marginBottom="42px">{t('Collaboration Description')}</Wrapper>
|
||||
<Button
|
||||
data-testid="local-workspace-enable-cloud-button"
|
||||
type="light"
|
||||
shape="circle"
|
||||
onClick={() => {
|
||||
|
@ -18,7 +18,8 @@ interface LoginModalProps {
|
||||
onInviteSuccess: () => void;
|
||||
}
|
||||
|
||||
const gmailReg = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@gmail\.com$/;
|
||||
const gmailReg =
|
||||
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@(gmail|example)\.(com|org)$/;
|
||||
|
||||
const Result: React.FC<{
|
||||
workspaceId: string;
|
||||
@ -75,6 +76,7 @@ export const InviteMemberModal = ({
|
||||
<ContentTitle>{t('Invite Members')}</ContentTitle>
|
||||
<InviteBox>
|
||||
<Input
|
||||
data-testid="invite-member-input"
|
||||
width={360}
|
||||
value={email}
|
||||
onChange={inputChange}
|
||||
@ -95,6 +97,7 @@ export const InviteMemberModal = ({
|
||||
</Content>
|
||||
<Footer>
|
||||
<Button
|
||||
data-testid="invite-member-button"
|
||||
disabled={!gmailReg.test(email)}
|
||||
shape="circle"
|
||||
type="primary"
|
||||
|
@ -31,3 +31,12 @@ const bareAuth = createBareClient(prefixUrl);
|
||||
const googleAuth = new GoogleAuth(bareAuth);
|
||||
export const clientAuth = createAuthClient(bareAuth, googleAuth);
|
||||
export const apis = getApis(bareAuth, clientAuth, googleAuth);
|
||||
|
||||
if (!globalThis.AFFINE_APIS) {
|
||||
globalThis.AFFINE_APIS = apis;
|
||||
}
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var AFFINE_APIS: ReturnType<typeof getApis>;
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import type {
|
||||
PlaywrightTestConfig,
|
||||
PlaywrightWorkerOptions,
|
||||
@ -18,6 +20,7 @@ const config: PlaywrightTestConfig = {
|
||||
fullyParallel: true,
|
||||
timeout: process.env.CI ? 50_000 : 30_000,
|
||||
use: {
|
||||
baseURL: 'http://localhost:8080/',
|
||||
browserName:
|
||||
(process.env.BROWSER as PlaywrightWorkerOptions['browserName']) ??
|
||||
'chromium',
|
||||
@ -39,16 +42,31 @@ const config: PlaywrightTestConfig = {
|
||||
// See https://playwright.dev/docs/test-reporters#github-actions-annotations
|
||||
reporter: process.env.CI ? 'github' : 'list',
|
||||
|
||||
webServer: {
|
||||
command: 'pnpm build && pnpm start -p 8080',
|
||||
port: 8080,
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
env: {
|
||||
COVERAGE: process.env.COVERAGE || 'false',
|
||||
ENABLE_DEBUG_PAGE: '1',
|
||||
webServer: [
|
||||
{
|
||||
command: 'cargo run -p affine-cloud',
|
||||
port: 3000,
|
||||
timeout: 10 * 1000,
|
||||
reuseExistingServer: true,
|
||||
cwd: process.env.OCTOBASE_CWD ?? resolve(process.cwd(), 'apps', 'server'),
|
||||
env: {
|
||||
SIGN_KEY: 'test123',
|
||||
RUST_LOG: 'debug',
|
||||
JWST_DEV: '1',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
command: 'pnpm build && pnpm start -p 8080',
|
||||
port: 8080,
|
||||
timeout: 120 * 1000,
|
||||
reuseExistingServer: !process.env.CI,
|
||||
env: {
|
||||
COVERAGE: process.env.COVERAGE || 'false',
|
||||
ENABLE_DEBUG_PAGE: '1',
|
||||
NODE_API_SERVER: 'local',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
if (process.env.CI) {
|
||||
|
5
tests/fixtures/userA.json
vendored
Normal file
5
tests/fixtures/userA.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Alex Yang",
|
||||
"email": "alex.yang@example.org",
|
||||
"password": "123456"
|
||||
}
|
5
tests/fixtures/userB.json
vendored
Normal file
5
tests/fixtures/userB.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Boying Li",
|
||||
"email": "boying.li@example.org",
|
||||
"password": "654321"
|
||||
}
|
5
tests/libs/setting.ts
Normal file
5
tests/libs/setting.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
export async function clickCollaborationPanel(page: Page) {
|
||||
await page.click('[data-tab-key="collaboration"]');
|
||||
}
|
@ -1,5 +1,17 @@
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
export async function clickSideBarSettingButton(page: Page) {
|
||||
await page.getByTestId('slider-bar-workspace-setting-button').click();
|
||||
return page.getByTestId('slider-bar-workspace-setting-button').click();
|
||||
}
|
||||
|
||||
export async function clickSideBarAllPageButton(page: Page) {
|
||||
return page.getByTestId('all-pages').click();
|
||||
}
|
||||
|
||||
export async function clickSideBarCurrentWorkspaceBanner(page: Page) {
|
||||
return page.getByTestId('current-workspace').click();
|
||||
}
|
||||
|
||||
export async function clickNewPageButton(page: Page) {
|
||||
return page.getByTestId('new-page-button').click();
|
||||
}
|
||||
|
84
tests/libs/utils.ts
Normal file
84
tests/libs/utils.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import type { Page } from '@playwright/test';
|
||||
|
||||
import userA from '../fixtures/userA.json';
|
||||
import userB from '../fixtures/userB.json';
|
||||
|
||||
export async function createFakeUser() {
|
||||
try {
|
||||
const response = await Promise.all([
|
||||
fetch('http://127.0.0.1:3000/api/user/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'DebugLoginUser',
|
||||
email: userA.email,
|
||||
password: userA.password,
|
||||
}),
|
||||
}),
|
||||
fetch('http://127.0.0.1:3000/api/user/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'DebugLoginUser',
|
||||
email: userB.email,
|
||||
password: userB.password,
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
return Promise.all(
|
||||
response.map(res => {
|
||||
if (!res.ok) {
|
||||
throw new Error('User not found');
|
||||
}
|
||||
return res.json();
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
const response = await Promise.all([
|
||||
// Register user A
|
||||
fetch('http://localhost:3000/api/user/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'DebugCreateUser',
|
||||
...userA,
|
||||
}),
|
||||
}),
|
||||
// Register user B
|
||||
fetch('http://localhost:3000/api/user/token', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
type: 'DebugCreateUser',
|
||||
...userB,
|
||||
}),
|
||||
}),
|
||||
]);
|
||||
return Promise.all(response.map(res => res.json()));
|
||||
}
|
||||
}
|
||||
|
||||
export async function loginUser(
|
||||
page: Page,
|
||||
token: {
|
||||
refresh: string;
|
||||
token: string;
|
||||
}
|
||||
) {
|
||||
await page.evaluate(async token => {
|
||||
// @ts-ignore
|
||||
globalThis.AFFINE_APIS.auth.setLogin(token);
|
||||
}, token);
|
||||
}
|
||||
|
||||
export async function openHomePage(page: Page) {
|
||||
return page.goto('http://localhost:8080');
|
||||
}
|
@ -2,6 +2,7 @@ import { expect } from '@playwright/test';
|
||||
|
||||
import { loadPage } from './libs/load-page';
|
||||
import { test } from './libs/playwright';
|
||||
import { clickSideBarAllPageButton } from './libs/sidebar';
|
||||
import { createWorkspace } from './libs/workspace-logic';
|
||||
loadPage();
|
||||
|
||||
@ -32,8 +33,7 @@ test.describe('Local first workspace list', () => {
|
||||
//check page list length
|
||||
const closeWorkspaceModal = page.getByTestId('close-workspace-modal');
|
||||
await closeWorkspaceModal.click();
|
||||
const allPageButton = page.getByTestId('all-pages');
|
||||
await allPageButton.click();
|
||||
await clickSideBarAllPageButton(page);
|
||||
await page.waitForTimeout(1000);
|
||||
const pageList = page.locator('[data-testid=page-list-item]');
|
||||
const result = await pageList.count();
|
||||
|
@ -2,6 +2,7 @@ import { expect } from '@playwright/test';
|
||||
|
||||
import { loadPage } from './libs/load-page';
|
||||
import { test } from './libs/playwright';
|
||||
import { clickSideBarCurrentWorkspaceBanner } from './libs/sidebar';
|
||||
|
||||
loadPage();
|
||||
|
||||
@ -20,7 +21,7 @@ test.describe('Local first default workspace', () => {
|
||||
});
|
||||
test.describe('Language switch', () => {
|
||||
test('Open language switch menu', async ({ page }) => {
|
||||
await page.getByTestId('current-workspace').click();
|
||||
await clickSideBarCurrentWorkspaceBanner(page);
|
||||
const languageMenuButton = page.getByTestId('language-menu-button');
|
||||
await expect(languageMenuButton).toBeVisible();
|
||||
const actual = await languageMenuButton.innerText();
|
||||
|
@ -1,35 +0,0 @@
|
||||
import { loadPage } from './libs/load-page';
|
||||
import { test } from './libs/playwright';
|
||||
|
||||
loadPage();
|
||||
|
||||
test.describe('Login Flow', () => {
|
||||
test.skip('Open login modal by click current workspace', async ({ page }) => {
|
||||
await page.getByTestId('current-workspace').click();
|
||||
await page.waitForTimeout(800);
|
||||
// why don't we use waitForSelector, It seems that waitForSelector not stable?
|
||||
await page.getByTestId('open-login-modal').click();
|
||||
await page.waitForTimeout(800);
|
||||
await page
|
||||
.getByRole('heading', { name: 'Currently not logged in' })
|
||||
.click();
|
||||
});
|
||||
// not stable
|
||||
// test.skip('Open google firebase page', async ({ page }) => {
|
||||
// await page.getByTestId('current-workspace').click();
|
||||
// await page.waitForTimeout(800);
|
||||
// // why don't we use waitForSelector, It seems that waitForSelector not stable?
|
||||
// await page.getByTestId('open-login-modal').click();
|
||||
// await page.waitForTimeout(800);
|
||||
// const [firebasePage] = await Promise.all([
|
||||
// page.waitForEvent('popup'),
|
||||
// page
|
||||
// .getByRole('button', {
|
||||
// name: 'Google Continue with Google Set up an AFFiNE account to sync data',
|
||||
// })
|
||||
// .click(),
|
||||
// ]);
|
||||
|
||||
// expect(firebasePage.url()).toContain('.firebaseapp.com/__/auth/handler');
|
||||
// });
|
||||
});
|
47
tests/parallels/affine-workspace.spec.ts
Normal file
47
tests/parallels/affine-workspace.spec.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { expect } from '@playwright/test';
|
||||
|
||||
import userA from '../fixtures/userA.json';
|
||||
import { test } from '../libs/playwright';
|
||||
import { clickCollaborationPanel } from '../libs/setting';
|
||||
import {
|
||||
clickNewPageButton,
|
||||
clickSideBarAllPageButton,
|
||||
clickSideBarCurrentWorkspaceBanner,
|
||||
clickSideBarSettingButton,
|
||||
} from '../libs/sidebar';
|
||||
import { createFakeUser, loginUser, openHomePage } from '../libs/utils';
|
||||
import { createWorkspace } from '../libs/workspace-logic';
|
||||
|
||||
test.describe('affine workspace', () => {
|
||||
test('should login with user A', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
const [a] = await createFakeUser();
|
||||
await loginUser(page, a);
|
||||
await clickSideBarCurrentWorkspaceBanner(page);
|
||||
const footer = page.locator('[data-testid="workspace-list-modal-footer"]');
|
||||
expect(await footer.getByText(userA.name).isVisible()).toBe(true);
|
||||
expect(await footer.getByText(userA.email).isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
// fixme: skip this because of duplicated creation of workspace may cause error
|
||||
test.skip('should enable affine workspace successfully', async ({ page }) => {
|
||||
await openHomePage(page);
|
||||
const [a] = await createFakeUser();
|
||||
await loginUser(page, a);
|
||||
await createWorkspace({ name: 'test1' }, page);
|
||||
await page.waitForTimeout(50);
|
||||
await clickSideBarSettingButton(page);
|
||||
await page.waitForTimeout(50);
|
||||
await clickCollaborationPanel(page);
|
||||
await page.getByTestId('local-workspace-enable-cloud-button').click();
|
||||
await page.getByTestId('confirm-enable-cloud-button').click();
|
||||
await page.waitForSelector("[data-testid='member-length']", {
|
||||
timeout: 10000,
|
||||
});
|
||||
await clickSideBarAllPageButton(page);
|
||||
await clickNewPageButton(page);
|
||||
await page.locator('[data-block-is-title="true"]').type('Hello, world!', {
|
||||
delay: 50,
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue
Block a user