feat!: affine cloud support (#3813)

Co-authored-by: Hongtao Lye <codert.sn@gmail.com>
Co-authored-by: liuyi <forehalo@gmail.com>
Co-authored-by: LongYinan <lynweklm@gmail.com>
Co-authored-by: X1a0t <405028157@qq.com>
Co-authored-by: JimmFly <yangjinfei001@gmail.com>
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
Co-authored-by: xiaodong zuo <53252747+zuoxiaodong0815@users.noreply.github.com>
Co-authored-by: DarkSky <25152247+darkskygit@users.noreply.github.com>
Co-authored-by: Qi <474021214@qq.com>
Co-authored-by: danielchim <kahungchim@gmail.com>
This commit is contained in:
Alex Yang
2023-08-29 05:07:05 -05:00
committed by GitHub
parent d0145c6f38
commit 2f6c4e3696
414 changed files with 19469 additions and 7591 deletions

View File

@@ -0,0 +1,62 @@
import { test } from '@affine-test/kit/playwright';
import {
createRandomUser,
deleteUser,
getLoginCookie,
} from '@affine-test/kit/utils/cloud';
import { openHomePage } from '@affine-test/kit/utils/load-page';
import { waitEditorLoad } from '@affine-test/kit/utils/page-logic';
import { clickSideBarCurrentWorkspaceBanner } from '@affine-test/kit/utils/sidebar';
import { expect } from '@playwright/test';
let user: {
name: string;
email: string;
password: string;
};
test.beforeEach(async () => {
user = await createRandomUser();
});
test.afterEach(async () => {
// if you want to keep the user in the database for debugging,
// comment this line
await deleteUser(user.email);
});
test('server exist', async ({ page }) => {
await openHomePage(page);
await waitEditorLoad(page);
const json = await (await fetch('http://localhost:3010')).json();
expect(json.message).toMatch(/^AFFiNE GraphQL server/);
});
test('enable cloud success', async ({ page, context }) => {
await page.goto('http://localhost:8080');
await page.waitForSelector('v-line');
await clickSideBarCurrentWorkspaceBanner(page);
await page.getByTestId('cloud-signin-button').click({
delay: 200,
});
await page.getByPlaceholder('Enter your email address').type(user.email, {
delay: 50,
});
await page.getByTestId('continue-login-button').click({
delay: 200,
});
await page.getByTestId('sign-in-with-password').click({
delay: 200,
});
await page.getByTestId('password-input').type('123456', {
delay: 50,
});
expect(await getLoginCookie(context)).toBeUndefined();
await page.getByTestId('sign-in-button').click();
await page.waitForTimeout(1000);
await page.reload();
await waitEditorLoad(page);
expect(await getLoginCookie(context)).toBeTruthy();
});

View File

@@ -0,0 +1,17 @@
import { test } from '@affine-test/kit/playwright';
import { openHomePage } from '@affine-test/kit/utils/load-page';
import { waitEditorLoad } from '@affine-test/kit/utils/page-logic';
import { clickSideBarCurrentWorkspaceBanner } from '@affine-test/kit/utils/sidebar';
import { expect } from '@playwright/test';
test.describe('login', () => {
test('can open login modal in workspace list', async ({ page }) => {
await openHomePage(page);
await waitEditorLoad(page);
await clickSideBarCurrentWorkspaceBanner(page);
await page.getByTestId('cloud-signin-button').click({
delay: 200,
});
await expect(page.getByTestId('auth-modal')).toBeVisible();
});
});

View File

@@ -0,0 +1,12 @@
{
"name": "@affine-test/affine-cloud",
"private": true,
"scripts": {
"e2e": "yarn playwright test"
},
"devDependencies": {
"@affine-test/fixtures": "workspace:*",
"@affine-test/kit": "workspace:*",
"@playwright/test": "^1.37.0"
}
}

View File

@@ -0,0 +1,64 @@
import type {
PlaywrightTestConfig,
PlaywrightWorkerOptions,
} from '@playwright/test';
const config: PlaywrightTestConfig = {
testDir: './e2e',
fullyParallel: !process.env.CI,
timeout: process.env.CI ? 50_000 : 30_000,
use: {
baseURL: 'http://localhost:8081/',
browserName:
(process.env.BROWSER as PlaywrightWorkerOptions['browserName']) ??
'chromium',
permissions: ['clipboard-read', 'clipboard-write'],
viewport: { width: 1440, height: 800 },
actionTimeout: 5 * 1000,
locale: 'en-US',
trace: 'on-first-retry',
video: 'on-first-retry',
},
forbidOnly: !!process.env.CI,
workers: process.env.CI ? 1 : 4,
retries: 1,
reporter: process.env.CI ? 'github' : 'list',
webServer: [
// Intentionally not building the web, reminds you to run it by yourself.
{
command: 'yarn -T run start:web-static',
port: 8080,
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
env: {
COVERAGE: process.env.COVERAGE || 'false',
},
},
{
command: 'yarn workspace @affine/server start',
port: 3010,
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
stderr: 'pipe',
env: {
DATABASE_URL:
process.env.DATABASE_URL ??
'postgresql://affine@localhost:5432/affine',
NODE_ENV: 'development',
AFFINE_ENV: process.env.AFFINE_ENV ?? 'dev',
DEBUG: 'affine:*',
FORCE_COLOR: 'true',
DEBUG_COLORS: 'true',
NEXTAUTH_URL: 'http://localhost:8080',
OAUTH_EMAIL_SENDER: 'noreply@toeverything.info',
},
},
],
};
if (process.env.CI) {
config.retries = 3;
}
export default config;

View File

@@ -0,0 +1,16 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"esModuleInterop": true,
"outDir": "lib"
},
"include": ["e2e"],
"references": [
{
"path": "../kit"
},
{
"path": "../fixtures"
}
]
}

View File

@@ -1,6 +1,7 @@
import { resolve } from 'node:path';
import { expect, test } from '@playwright/test';
import { test } from '@affine-test/kit/playwright';
import { expect } from '@playwright/test';
import express from 'express';
import { createProxyMiddleware } from 'http-proxy-middleware';

View File

@@ -4,5 +4,10 @@
"esModuleInterop": true,
"outDir": "lib"
},
"include": ["e2e"]
"include": ["e2e"],
"references": [
{
"path": "../../kit"
}
]
}

View File

@@ -32,7 +32,7 @@ test('Create new workspace, then delete it', async ({ page, workspace }) => {
.getByTestId('delete-workspace-input')
.type(currentWorkspaceName as string);
const promise = page
.getByTestId('affine-toast')
.getByTestId('affine-notification')
.waitFor({ state: 'attached' });
await page.getByTestId('delete-workspace-confirm-button').click();
await promise;
@@ -46,7 +46,7 @@ test('Create new workspace, then delete it', async ({ page, workspace }) => {
expect(currentWorkspace.flavour).toContain('local');
});
//FIXME: this test is broken
test('Delete last workspace', async ({ page }) => {
await openHomePage(page);
await waitEditorLoad(page);
@@ -60,12 +60,8 @@ test('Delete last workspace', async ({ page }) => {
await page
.getByTestId('delete-workspace-input')
.type(currentWorkspaceName as string);
const promise = page
.getByTestId('affine-toast')
.waitFor({ state: 'attached' });
await page.getByTestId('delete-workspace-confirm-button').click();
await promise;
await page.reload();
await openHomePage(page);
await expect(page.getByTestId('new-workspace')).toBeVisible();
await page.getByTestId('new-workspace').click();
await page.type('[data-testid="create-workspace-input"]', 'Test Workspace');

View File

@@ -124,8 +124,9 @@ test('create multi workspace in the workspace list', async ({
await page.waitForTimeout(1000);
// check workspace list length
{
const workspaceCards1 = await page.$$('data-testid=workspace-card');
expect(workspaceCards1.length).toBe(3);
await page.waitForTimeout(1000);
const workspaceCards = page.getByTestId('workspace-card');
expect(await workspaceCards.count()).toBe(3);
}
const workspaceChangePromise = page.evaluate(() => {

View File

@@ -8,8 +8,10 @@ import {
} from '@affine-test/kit/utils/page-logic';
import { expect, type Page } from '@playwright/test';
const openQuickSearchByShortcut = async (page: Page) =>
const openQuickSearchByShortcut = async (page: Page) => {
await withCtrlOrMeta(page, () => page.keyboard.press('k', { delay: 50 }));
await page.waitForTimeout(500);
};
async function assertTitle(page: Page, text: string) {
const edgeless = page.locator('affine-edgeless-page');

View File

@@ -18,6 +18,6 @@ test('goto not found workspace', async ({ page }) => {
// if doesn't wait for timeout, data won't be saved into indexedDB
await page.waitForTimeout(1000);
await page.goto(new URL('/workspace/invalid/all', coreUrl).toString());
await page.waitForTimeout(1000);
await page.waitForTimeout(3000);
expect(page.url()).toBe(new URL('/404', coreUrl).toString());
});

View File

@@ -7,6 +7,7 @@
"./utils/*": "./utils/*.ts"
},
"devDependencies": {
"@node-rs/argon2": "^1.5.2",
"@playwright/test": "^1.37.1"
},
"peerDependencies": {

54
tests/kit/utils/cloud.ts Normal file
View File

@@ -0,0 +1,54 @@
import { faker } from '@faker-js/faker';
import { hash } from '@node-rs/argon2';
import type { BrowserContext, Cookie } from '@playwright/test';
export async function getLoginCookie(
context: BrowserContext
): Promise<Cookie | undefined> {
return (await context.cookies()).find(
c => c.name === 'next-auth.session-token'
);
}
export async function createRandomUser() {
const user = {
name: faker.internet.userName(),
email: faker.internet.email().toLowerCase(),
password: '123456',
};
const {
PrismaClient,
// eslint-disable-next-line @typescript-eslint/no-var-requires
} = require('../../../apps/server/node_modules/@prisma/client');
const client = new PrismaClient();
await client.$connect();
await client.user.create({
data: {
...user,
emailVerified: new Date(),
password: await hash(user.password),
},
});
await client.$disconnect();
return client.user.findUnique({
where: {
email: user.email,
},
});
}
export async function deleteUser(email: string) {
const {
PrismaClient,
// eslint-disable-next-line @typescript-eslint/no-var-requires
} = require('../../../apps/server/node_modules/@prisma/client');
const client = new PrismaClient();
await client.$connect();
await client.user.delete({
where: {
email,
},
});
await client.$disconnect();
}