test: support test e2e with OctoBase (#1593)

Co-authored-by: DarkSky <darksky2048@gmail.com>
This commit is contained in:
Himself65 2023-03-17 03:51:00 -04:00 committed by GitHub
parent 110cec7bf6
commit 94d535f72b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 224 additions and 51 deletions

View File

@ -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

View File

@ -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 () => {

View File

@ -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={() => {

View File

@ -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"

View File

@ -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>;
}

View File

@ -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
View File

@ -0,0 +1,5 @@
{
"name": "Alex Yang",
"email": "alex.yang@example.org",
"password": "123456"
}

5
tests/fixtures/userB.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"name": "Boying Li",
"email": "boying.li@example.org",
"password": "654321"
}

5
tests/libs/setting.ts Normal file
View File

@ -0,0 +1,5 @@
import type { Page } from '@playwright/test';
export async function clickCollaborationPanel(page: Page) {
await page.click('[data-tab-key="collaboration"]');
}

View File

@ -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
View 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');
}

View File

@ -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();

View File

@ -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();

View File

@ -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');
// });
});

View 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,
});
});
});