mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-04 19:11:57 +08:00
test(electron): add cloud test (#4184)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
This commit is contained in:
@@ -101,6 +101,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
playwright-install: true
|
playwright-install: true
|
||||||
hard-link-nm: false
|
hard-link-nm: false
|
||||||
|
|
||||||
- name: Build AFFiNE native
|
- name: Build AFFiNE native
|
||||||
uses: ./.github/actions/build-rust
|
uses: ./.github/actions/build-rust
|
||||||
with:
|
with:
|
||||||
@@ -125,21 +126,15 @@ jobs:
|
|||||||
- name: Build Desktop Layers
|
- name: Build Desktop Layers
|
||||||
run: yarn workspace @affine/electron build
|
run: yarn workspace @affine/electron build
|
||||||
|
|
||||||
- name: Upload desktop dist
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: dist-${{ matrix.spec.platform }}-${{ matrix.spec.arch }}
|
|
||||||
path: ./apps/electron/dist
|
|
||||||
|
|
||||||
- name: Run desktop tests
|
- name: Run desktop tests
|
||||||
if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }}
|
if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }}
|
||||||
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine/electron test
|
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop e2e
|
||||||
env:
|
env:
|
||||||
COVERAGE: true
|
COVERAGE: true
|
||||||
|
|
||||||
- name: Run desktop tests
|
- name: Run desktop tests
|
||||||
if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }}
|
if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }}
|
||||||
run: yarn workspace @affine/electron test
|
run: yarn workspace @affine-test/affine-desktop e2e
|
||||||
env:
|
env:
|
||||||
COVERAGE: true
|
COVERAGE: true
|
||||||
|
|
||||||
@@ -149,7 +144,7 @@ jobs:
|
|||||||
SKIP_BUNDLE: true
|
SKIP_BUNDLE: true
|
||||||
run: yarn workspace @affine/electron make --platform=darwin --arch=arm64
|
run: yarn workspace @affine/electron make --platform=darwin --arch=arm64
|
||||||
|
|
||||||
- name: Bundle output check
|
- name: Output check
|
||||||
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
|
if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }}
|
||||||
run: |
|
run: |
|
||||||
yarn ts-node-esm ./scripts/macos-arm64-output-check.mts
|
yarn ts-node-esm ./scripts/macos-arm64-output-check.mts
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ jobs:
|
|||||||
path: ./apps/server
|
path: ./apps/server
|
||||||
|
|
||||||
- name: Run playwright tests
|
- name: Run playwright tests
|
||||||
run: yarn workspace @affine-test/affine-cloud e2e --forbid-only
|
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-cloud e2e --forbid-only
|
||||||
env:
|
env:
|
||||||
COVERAGE: true
|
COVERAGE: true
|
||||||
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||||
@@ -208,3 +208,103 @@ jobs:
|
|||||||
name: test-results-e2e-server
|
name: test-results-e2e-server
|
||||||
path: ./tests/affine-cloud/test-results
|
path: ./tests/affine-cloud/test-results
|
||||||
if-no-files-found: ignore
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
server-desktop-e2e-test:
|
||||||
|
name: Server Desktop E2E Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment: development
|
||||||
|
needs: build-storage
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres
|
||||||
|
env:
|
||||||
|
POSTGRES_PASSWORD: affine
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
mailer:
|
||||||
|
image: mailhog/mailhog
|
||||||
|
ports:
|
||||||
|
- 1025:1025
|
||||||
|
- 8025:8025
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Setup Node.js
|
||||||
|
uses: ./.github/actions/setup-node
|
||||||
|
with:
|
||||||
|
playwright-install: true
|
||||||
|
hard-link-nm: false
|
||||||
|
|
||||||
|
- name: Build AFFiNE native
|
||||||
|
uses: ./.github/actions/build-rust
|
||||||
|
with:
|
||||||
|
target: x86_64-unknown-linux-gnu
|
||||||
|
package: '@affine/native'
|
||||||
|
nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
|
||||||
|
|
||||||
|
- name: Initialize database
|
||||||
|
run: |
|
||||||
|
psql -h localhost -U postgres -c "CREATE DATABASE affine;"
|
||||||
|
psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';"
|
||||||
|
psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;"
|
||||||
|
env:
|
||||||
|
PGPASSWORD: affine
|
||||||
|
|
||||||
|
- name: Generate prisma client
|
||||||
|
run: |
|
||||||
|
yarn exec prisma generate
|
||||||
|
yarn exec prisma db push
|
||||||
|
working-directory: apps/server
|
||||||
|
env:
|
||||||
|
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||||
|
|
||||||
|
- name: Run init-db script
|
||||||
|
run: yarn exec ts-node-esm ./scripts/init-db.ts
|
||||||
|
working-directory: apps/server
|
||||||
|
env:
|
||||||
|
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||||
|
|
||||||
|
- name: Download storage.node
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: storage.node
|
||||||
|
path: ./apps/server
|
||||||
|
|
||||||
|
- name: Build Plugins
|
||||||
|
run: yarn run build:plugins
|
||||||
|
|
||||||
|
- name: Build Desktop Layers
|
||||||
|
run: yarn workspace @affine/electron build:dev
|
||||||
|
|
||||||
|
- name: Run playwright tests
|
||||||
|
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" yarn workspace @affine-test/affine-desktop-cloud e2e
|
||||||
|
env:
|
||||||
|
COVERAGE: true
|
||||||
|
DEV_SERVER_URL: http://localhost:8080
|
||||||
|
DATABASE_URL: postgresql://affine:affine@localhost:5432/affine
|
||||||
|
ENABLE_LOCAL_EMAIL: true
|
||||||
|
|
||||||
|
- name: Collect code coverage report
|
||||||
|
run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov
|
||||||
|
|
||||||
|
- name: Upload e2e test coverage results
|
||||||
|
uses: codecov/codecov-action@v3
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
files: ./.coverage/lcov.info
|
||||||
|
flags: server-e2etest
|
||||||
|
name: affine
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
||||||
|
- name: Upload test results
|
||||||
|
if: ${{ failure() }}
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: test-results-e2e-server
|
||||||
|
path: ./tests/affine-cloud/test-results
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { execSync } from 'node:child_process';
|
|
||||||
import { join } from 'node:path';
|
|
||||||
|
|
||||||
export default async function () {
|
|
||||||
execSync('yarn ts-node-esm scripts/', {
|
|
||||||
cwd: join(__dirname, '..'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -14,10 +14,10 @@
|
|||||||
"dev": "yarn cross-env DEV_SERVER_URL=http://localhost:8080 node scripts/dev.mjs",
|
"dev": "yarn cross-env DEV_SERVER_URL=http://localhost:8080 node scripts/dev.mjs",
|
||||||
"dev:prod": "yarn node scripts/dev.mjs",
|
"dev:prod": "yarn node scripts/dev.mjs",
|
||||||
"build": "NODE_ENV=production zx scripts/build-layers.mjs",
|
"build": "NODE_ENV=production zx scripts/build-layers.mjs",
|
||||||
|
"build:dev": "NODE_ENV=development zx scripts/build-layers.mjs",
|
||||||
"generate-assets": "zx scripts/generate-assets.mjs",
|
"generate-assets": "zx scripts/generate-assets.mjs",
|
||||||
"package": "electron-forge package",
|
"package": "electron-forge package",
|
||||||
"make": "electron-forge make",
|
"make": "electron-forge make",
|
||||||
"test": "DEBUG=pw:browser yarn -T run playwright test -c ./playwright.config.ts",
|
|
||||||
"make-squirrel": "yarn ts-node-esm -T scripts/make-squirrel.mts"
|
"make-squirrel": "yarn ts-node-esm -T scripts/make-squirrel.mts"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
@@ -43,7 +43,6 @@
|
|||||||
"@electron/remote": "2.0.11",
|
"@electron/remote": "2.0.11",
|
||||||
"@reforged/maker-appimage": "^3.3.1",
|
"@reforged/maker-appimage": "^3.3.1",
|
||||||
"@toeverything/infra": "workspace:*",
|
"@toeverything/infra": "workspace:*",
|
||||||
"@types/fs-extra": "^11.0.1",
|
|
||||||
"@types/uuid": "^9.0.3",
|
"@types/uuid": "^9.0.3",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"electron": "^26.1.0",
|
"electron": "^26.1.0",
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { setTimeout } from 'node:timers/promises';
|
import { setTimeout } from 'node:timers/promises';
|
||||||
|
|
||||||
|
import { removeWithRetry } from '@affine-test/kit/utils/utils';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
||||||
|
|
||||||
import { removeWithRetry } from '../../../../tests/utils';
|
|
||||||
|
|
||||||
const tmpDir = path.join(__dirname, 'tmp');
|
const tmpDir = path.join(__dirname, 'tmp');
|
||||||
const appDataPath = path.join(tmpDir, 'app-data');
|
const appDataPath = path.join(tmpDir, 'app-data');
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
import { SqliteConnection } from '@affine/native';
|
import { SqliteConnection } from '@affine/native';
|
||||||
|
import { removeWithRetry } from '@affine-test/kit/utils/utils';
|
||||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { applyUpdate, Doc as YDoc } from 'yjs';
|
import { applyUpdate, Doc as YDoc } from 'yjs';
|
||||||
|
|
||||||
import { removeWithRetry } from '../../../../tests/utils';
|
|
||||||
import { copyToTemp, migrateToSubdocAndReplaceDatabase } from '../migration';
|
import { copyToTemp, migrateToSubdocAndReplaceDatabase } from '../migration';
|
||||||
|
|
||||||
const tmpDir = path.join(__dirname, 'tmp');
|
const tmpDir = path.join(__dirname, 'tmp');
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import { removeWithRetry } from '@affine-test/kit/utils/utils';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { afterEach, expect, test, vi } from 'vitest';
|
import { afterEach, expect, test, vi } from 'vitest';
|
||||||
import { Doc as YDoc, encodeStateAsUpdate } from 'yjs';
|
import { Doc as YDoc, encodeStateAsUpdate } from 'yjs';
|
||||||
|
|
||||||
import { removeWithRetry } from '../../../../tests/utils';
|
|
||||||
import { dbSubjects } from '../subjects';
|
import { dbSubjects } from '../subjects';
|
||||||
|
|
||||||
const tmpDir = path.join(__dirname, 'tmp');
|
const tmpDir = path.join(__dirname, 'tmp');
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import { removeWithRetry } from '@affine-test/kit/utils/utils';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import { v4 } from 'uuid';
|
import { v4 } from 'uuid';
|
||||||
import { afterEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||||
|
|
||||||
import { removeWithRetry } from '../../../../tests/utils';
|
|
||||||
|
|
||||||
const tmpDir = path.join(__dirname, 'tmp');
|
const tmpDir = path.join(__dirname, 'tmp');
|
||||||
const appDataPath = path.join(tmpDir, 'app-data');
|
const appDataPath = path.join(tmpDir, 'app-data');
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import assert from 'node:assert';
|
import assert from 'node:assert';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import { removeWithRetry } from '@affine-test/kit/utils/utils';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||||
|
|
||||||
import { removeWithRetry } from '../../../tests/utils';
|
|
||||||
import type { MainIPCHandlerMap } from '../exposed';
|
import type { MainIPCHandlerMap } from '../exposed';
|
||||||
|
|
||||||
const registeredHandlers = new Map<
|
const registeredHandlers = new Map<
|
||||||
|
|||||||
@@ -1,26 +0,0 @@
|
|||||||
import { setTimeout } from 'node:timers/promises';
|
|
||||||
|
|
||||||
import fs from 'fs-extra';
|
|
||||||
|
|
||||||
export async function removeWithRetry(
|
|
||||||
filePath: string,
|
|
||||||
maxRetries = 5,
|
|
||||||
delay = 500
|
|
||||||
) {
|
|
||||||
for (let i = 0; i < maxRetries; i++) {
|
|
||||||
try {
|
|
||||||
await fs.remove(filePath);
|
|
||||||
console.log(`File ${filePath} successfully deleted.`);
|
|
||||||
return true;
|
|
||||||
} catch (err: any) {
|
|
||||||
if (err.code === 'EBUSY' || err.code === 'EPERM') {
|
|
||||||
console.log(`File ${filePath} is busy or locked, retrying...`);
|
|
||||||
await setTimeout(delay);
|
|
||||||
} else {
|
|
||||||
console.error(`Failed to delete file ${filePath}:`, err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add a return statement here to ensure that a value is always returned
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"types": ["node"],
|
"types": ["node"],
|
||||||
"outDir": "dist",
|
"outDir": "lib",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
"noImplicitOverride": true
|
"noImplicitOverride": true
|
||||||
},
|
},
|
||||||
"include": ["./src"],
|
"include": ["./src"],
|
||||||
"exclude": ["node_modules", "out", "dist", "**/__tests__/**/*"],
|
"exclude": ["node_modules", "lib", "dist", "**/__tests__/**/*"],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "../../packages/infra"
|
"path": "../../packages/infra"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"composite": true
|
"composite": true
|
||||||
},
|
},
|
||||||
"include": ["**/__tests__/**/*", "./tests", "./e2e"],
|
"include": ["**/__tests__/**/*", "./tests"],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "./tsconfig.json"
|
"path": "./tsconfig.json"
|
||||||
|
|||||||
@@ -14,6 +14,8 @@
|
|||||||
"tests/kit",
|
"tests/kit",
|
||||||
"tests/affine-legacy/*",
|
"tests/affine-legacy/*",
|
||||||
"tests/affine-local",
|
"tests/affine-local",
|
||||||
|
"tests/affine-desktop",
|
||||||
|
"tests/affine-desktop-cloud",
|
||||||
"tests/affine-plugin",
|
"tests/affine-plugin",
|
||||||
"tests/affine-prototype",
|
"tests/affine-prototype",
|
||||||
"tests/affine-cloud"
|
"tests/affine-cloud"
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { test } from '@affine-test/kit/electron';
|
||||||
|
import {
|
||||||
|
createRandomUser,
|
||||||
|
enableCloudWorkspace,
|
||||||
|
loginUser,
|
||||||
|
} from '@affine-test/kit/utils/cloud';
|
||||||
|
import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic';
|
||||||
|
import { createLocalWorkspace } from '@affine-test/kit/utils/workspace';
|
||||||
|
|
||||||
|
let user: {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
test.beforeEach(async () => {
|
||||||
|
user = await createRandomUser();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await loginUser(page, user.email);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('new page', async ({ page }) => {
|
||||||
|
await page.reload();
|
||||||
|
await waitForEditorLoad(page);
|
||||||
|
await createLocalWorkspace(
|
||||||
|
{
|
||||||
|
name: 'test',
|
||||||
|
},
|
||||||
|
page
|
||||||
|
);
|
||||||
|
await enableCloudWorkspace(page);
|
||||||
|
});
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "@affine-test/affine-desktop-cloud",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"e2e": "DEBUG=pw:browser yarn playwright test"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@affine-test/fixtures": "workspace:*",
|
||||||
|
"@affine-test/kit": "workspace:*",
|
||||||
|
"@playwright/test": "^1.37.1",
|
||||||
|
"@types/fs-extra": "^11.0.1",
|
||||||
|
"fs-extra": "^11.1.1"
|
||||||
|
},
|
||||||
|
"version": "0.9.0-canary.8"
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
import type { PlaywrightTestConfig } from '@playwright/test';
|
||||||
|
// import { devices } from '@playwright/test';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read environment variables from file.
|
||||||
|
* https://github.com/motdotla/dotenv
|
||||||
|
*/
|
||||||
|
// require('dotenv').config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
testDir: './e2e',
|
||||||
|
fullyParallel: true,
|
||||||
|
timeout: process.env.CI ? 50_000 : 30_000,
|
||||||
|
use: {
|
||||||
|
viewport: { width: 1440, height: 800 },
|
||||||
|
},
|
||||||
|
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:affine@localhost:5432/affine',
|
||||||
|
NODE_ENV: 'development',
|
||||||
|
AFFINE_ENV: process.env.AFFINE_ENV ?? 'dev',
|
||||||
|
DEBUG: 'affine:*',
|
||||||
|
FORCE_COLOR: 'true',
|
||||||
|
DEBUG_COLORS: 'true',
|
||||||
|
ENABLE_LOCAL_EMAIL: process.env.ENABLE_LOCAL_EMAIL ?? 'true',
|
||||||
|
NEXTAUTH_URL: 'http://localhost:8080',
|
||||||
|
OAUTH_EMAIL_SENDER: 'noreply@toeverything.info',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.CI) {
|
||||||
|
config.retries = 3;
|
||||||
|
config.workers = '50%';
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["e2e"],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "../kit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../fixtures"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import { platform } from 'node:os';
|
import { platform } from 'node:os';
|
||||||
|
|
||||||
|
import { test } from '@affine-test/kit/electron';
|
||||||
import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar';
|
import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar';
|
||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
|
|
||||||
import { test } from './fixture';
|
|
||||||
|
|
||||||
test('new page', async ({ page, workspace }) => {
|
test('new page', async ({ page, workspace }) => {
|
||||||
await page.getByTestId('new-page-button').click({
|
await page.getByTestId('new-page-button').click({
|
||||||
delay: 100,
|
delay: 100,
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import { test } from '@affine-test/kit/electron';
|
||||||
import { expect } from '@playwright/test';
|
import { expect } from '@playwright/test';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
import { test } from './fixture';
|
|
||||||
|
|
||||||
test('check workspace has a DB file', async ({ appInfo, workspace }) => {
|
test('check workspace has a DB file', async ({ appInfo, workspace }) => {
|
||||||
const w = await workspace.current();
|
const w = await workspace.current();
|
||||||
const dbPath = path.join(
|
const dbPath = path.join(
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "@affine-test/affine-desktop",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"e2e": "DEBUG=pw:browser yarn playwright test"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@affine-test/fixtures": "workspace:*",
|
||||||
|
"@affine-test/kit": "workspace:*",
|
||||||
|
"@playwright/test": "^1.37.1",
|
||||||
|
"@types/fs-extra": "^11.0.1",
|
||||||
|
"fs-extra": "^11.1.1"
|
||||||
|
},
|
||||||
|
"version": "0.9.0-canary.8"
|
||||||
|
}
|
||||||
@@ -12,7 +12,6 @@ import type { PlaywrightTestConfig } from '@playwright/test';
|
|||||||
*/
|
*/
|
||||||
const config: PlaywrightTestConfig = {
|
const config: PlaywrightTestConfig = {
|
||||||
testDir: './e2e',
|
testDir: './e2e',
|
||||||
testIgnore: '**/lib/**',
|
|
||||||
fullyParallel: true,
|
fullyParallel: true,
|
||||||
timeout: process.env.CI ? 50_000 : 30_000,
|
timeout: process.env.CI ? 50_000 : 30_000,
|
||||||
use: {
|
use: {
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"extends": "../../tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"outDir": "lib"
|
||||||
|
},
|
||||||
|
"include": ["e2e"],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "../kit"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../fixtures"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,19 +1,21 @@
|
|||||||
/* eslint-disable no-empty-pattern */
|
|
||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
import { join, resolve } from 'node:path';
|
import { join, resolve } from 'node:path';
|
||||||
|
|
||||||
|
import type { Page } from '@playwright/test';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import type { ElectronApplication } from 'playwright';
|
||||||
|
import { _electron as electron } from 'playwright';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
enableCoverage,
|
enableCoverage,
|
||||||
istanbulTempDir,
|
istanbulTempDir,
|
||||||
test as base,
|
test as base,
|
||||||
testResultDir,
|
testResultDir,
|
||||||
} from '@affine-test/kit/playwright';
|
} from './playwright';
|
||||||
import type { Page } from '@playwright/test';
|
import { removeWithRetry } from './utils/utils';
|
||||||
import fs from 'fs-extra';
|
|
||||||
import type { ElectronApplication } from 'playwright';
|
|
||||||
import { _electron as electron } from 'playwright';
|
|
||||||
|
|
||||||
import { removeWithRetry } from '../tests/utils';
|
const projectRoot = resolve(__dirname, '..', '..');
|
||||||
|
const electronRoot = resolve(projectRoot, 'apps', 'electron');
|
||||||
|
|
||||||
function generateUUID() {
|
function generateUUID() {
|
||||||
return crypto.randomUUID();
|
return crypto.randomUUID();
|
||||||
@@ -37,14 +39,7 @@ export const test = base.extend<{
|
|||||||
await page.getByTestId('onboarding-modal-close-button').click({
|
await page.getByTestId('onboarding-modal-close-button').click({
|
||||||
delay: 100,
|
delay: 100,
|
||||||
});
|
});
|
||||||
if (!process.env.CI) {
|
// wait for blocksuite to be loaded
|
||||||
await electronApp.evaluate(({ BrowserWindow }) => {
|
|
||||||
BrowserWindow.getAllWindows()[0].webContents.openDevTools({
|
|
||||||
mode: 'detach',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// wat for blocksuite to be loaded
|
|
||||||
await page.waitForSelector('v-line');
|
await page.waitForSelector('v-line');
|
||||||
if (enableCoverage) {
|
if (enableCoverage) {
|
||||||
await fs.promises.mkdir(istanbulTempDir, { recursive: true });
|
await fs.promises.mkdir(istanbulTempDir, { recursive: true });
|
||||||
@@ -71,15 +66,16 @@ export const test = base.extend<{
|
|||||||
}
|
}
|
||||||
await page.close();
|
await page.close();
|
||||||
},
|
},
|
||||||
|
// eslint-disable-next-line no-empty-pattern
|
||||||
electronApp: async ({}, use) => {
|
electronApp: async ({}, use) => {
|
||||||
// a random id to avoid conflicts between tests
|
// a random id to avoid conflicts between tests
|
||||||
const id = generateUUID();
|
const id = generateUUID();
|
||||||
const ext = process.platform === 'win32' ? '.cmd' : '';
|
const ext = process.platform === 'win32' ? '.cmd' : '';
|
||||||
const dist = resolve(__dirname, '..', 'dist');
|
const dist = resolve(electronRoot, 'dist');
|
||||||
const clonedDist = resolve(__dirname, '../e2e-dist-' + id);
|
const clonedDist = resolve(electronRoot, 'e2e-dist-' + id);
|
||||||
await fs.copy(dist, clonedDist);
|
await fs.copy(dist, clonedDist);
|
||||||
const packageJson = await fs.readJSON(
|
const packageJson = await fs.readJSON(
|
||||||
resolve(__dirname, '..', 'package.json')
|
resolve(electronRoot, 'package.json')
|
||||||
);
|
);
|
||||||
// overwrite the app name
|
// overwrite the app name
|
||||||
packageJson.name = 'affine-test-' + id;
|
packageJson.name = 'affine-test-' + id;
|
||||||
@@ -88,15 +84,27 @@ export const test = base.extend<{
|
|||||||
// write to the cloned dist
|
// write to the cloned dist
|
||||||
await fs.writeJSON(resolve(clonedDist, 'package.json'), packageJson);
|
await fs.writeJSON(resolve(clonedDist, 'package.json'), packageJson);
|
||||||
|
|
||||||
|
const env: Record<string, string> = {};
|
||||||
|
for (const [key, value] of Object.entries(process.env)) {
|
||||||
|
if (value) {
|
||||||
|
env[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.DEV_SERVER_URL) {
|
||||||
|
env.DEV_SERVER_URL = process.env.DEV_SERVER_URL;
|
||||||
|
}
|
||||||
|
|
||||||
const electronApp = await electron.launch({
|
const electronApp = await electron.launch({
|
||||||
args: [clonedDist],
|
args: [clonedDist],
|
||||||
|
env,
|
||||||
executablePath: resolve(
|
executablePath: resolve(
|
||||||
__dirname,
|
electronRoot,
|
||||||
'..',
|
|
||||||
'node_modules',
|
'node_modules',
|
||||||
'.bin',
|
'.bin',
|
||||||
`electron${ext}`
|
`electron${ext}`
|
||||||
),
|
),
|
||||||
|
cwd: clonedDist,
|
||||||
recordVideo: {
|
recordVideo: {
|
||||||
dir: testResultDir,
|
dir: testResultDir,
|
||||||
},
|
},
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
"type": "module",
|
"type": "module",
|
||||||
"version": "0.9.0-canary.12",
|
"version": "0.9.0-canary.12",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
"./electron": "./electron.ts",
|
||||||
"./playwright": "./playwright.ts",
|
"./playwright": "./playwright.ts",
|
||||||
"./utils/*": "./utils/*.ts"
|
"./utils/*": "./utils/*.ts"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import { setTimeout } from 'node:timers/promises';
|
||||||
|
|
||||||
import type { Page } from '@playwright/test';
|
import type { Page } from '@playwright/test';
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
|
||||||
export async function waitForLogMessage(
|
export async function waitForLogMessage(
|
||||||
page: Page,
|
page: Page,
|
||||||
@@ -12,3 +15,26 @@ export async function waitForLogMessage(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function removeWithRetry(
|
||||||
|
filePath: string,
|
||||||
|
maxRetries = 5,
|
||||||
|
delay = 500
|
||||||
|
) {
|
||||||
|
for (let i = 0; i < maxRetries; i++) {
|
||||||
|
try {
|
||||||
|
await fs.remove(filePath);
|
||||||
|
console.log(`File ${filePath} successfully deleted.`);
|
||||||
|
return true;
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.code === 'EBUSY' || err.code === 'EPERM') {
|
||||||
|
console.log(`File ${filePath} is busy or locked, retrying...`);
|
||||||
|
await setTimeout(delay);
|
||||||
|
} else {
|
||||||
|
console.error(`Failed to delete file ${filePath}:`, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Add a return statement here to ensure that a value is always returned
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,12 +19,20 @@ export async function createLocalWorkspace(
|
|||||||
// open create workspace modal
|
// open create workspace modal
|
||||||
await page.getByTestId('new-workspace').click();
|
await page.getByTestId('new-workspace').click();
|
||||||
|
|
||||||
|
const isDesktop: boolean = await page.evaluate(() => {
|
||||||
|
return !!window.appInfo?.electron;
|
||||||
|
}, []);
|
||||||
|
|
||||||
// input workspace name
|
// input workspace name
|
||||||
await page.getByPlaceholder('Set a Workspace name').click();
|
await page.getByPlaceholder('Set a Workspace name').click();
|
||||||
await page.getByPlaceholder('Set a Workspace name').fill(params.name);
|
await page.getByPlaceholder('Set a Workspace name').fill(params.name);
|
||||||
|
|
||||||
// click create button
|
// click create button
|
||||||
return page.getByRole('button', { name: 'Create' }).click({
|
await page.getByRole('button', { name: 'Create' }).click({
|
||||||
delay: 500,
|
delay: 500,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (isDesktop) {
|
||||||
|
await page.getByTestId('create-workspace-continue-button').click();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -181,6 +181,9 @@
|
|||||||
{
|
{
|
||||||
"path": "./tests/affine-cloud"
|
"path": "./tests/affine-cloud"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "./tests/affine-desktop"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "./tests/affine-legacy/0.8.0-canary.7"
|
"path": "./tests/affine-legacy/0.8.0-canary.7"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -80,6 +80,30 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"@affine-test/affine-desktop-cloud@workspace:tests/affine-desktop-cloud":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@affine-test/affine-desktop-cloud@workspace:tests/affine-desktop-cloud"
|
||||||
|
dependencies:
|
||||||
|
"@affine-test/fixtures": "workspace:*"
|
||||||
|
"@affine-test/kit": "workspace:*"
|
||||||
|
"@playwright/test": ^1.37.1
|
||||||
|
"@types/fs-extra": ^11.0.1
|
||||||
|
fs-extra: ^11.1.1
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
|
"@affine-test/affine-desktop@workspace:tests/affine-desktop":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@affine-test/affine-desktop@workspace:tests/affine-desktop"
|
||||||
|
dependencies:
|
||||||
|
"@affine-test/fixtures": "workspace:*"
|
||||||
|
"@affine-test/kit": "workspace:*"
|
||||||
|
"@playwright/test": ^1.37.1
|
||||||
|
"@types/fs-extra": ^11.0.1
|
||||||
|
fs-extra: ^11.1.1
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"@affine-test/affine-local@workspace:tests/affine-local":
|
"@affine-test/affine-local@workspace:tests/affine-local":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@affine-test/affine-local@workspace:tests/affine-local"
|
resolution: "@affine-test/affine-local@workspace:tests/affine-local"
|
||||||
@@ -388,7 +412,6 @@ __metadata:
|
|||||||
"@electron/remote": 2.0.11
|
"@electron/remote": 2.0.11
|
||||||
"@reforged/maker-appimage": ^3.3.1
|
"@reforged/maker-appimage": ^3.3.1
|
||||||
"@toeverything/infra": "workspace:*"
|
"@toeverything/infra": "workspace:*"
|
||||||
"@types/fs-extra": ^11.0.1
|
|
||||||
"@types/uuid": ^9.0.3
|
"@types/uuid": ^9.0.3
|
||||||
async-call-rpc: ^6.3.1
|
async-call-rpc: ^6.3.1
|
||||||
cross-env: 7.0.3
|
cross-env: 7.0.3
|
||||||
|
|||||||
Reference in New Issue
Block a user