From cdad7edf151de0ef7713538f08721bb9ae088c8d Mon Sep 17 00:00:00 2001 From: Alex Yang Date: Sun, 17 Sep 2023 13:26:06 -0700 Subject: [PATCH] test(electron): add cloud test (#4184) Co-authored-by: Peng Xiao --- .github/workflows/build-desktop.yml | 13 +-- .github/workflows/build-server.yml | 102 +++++++++++++++++- apps/electron/e2e/setup.ts | 8 -- apps/electron/package.json | 3 +- .../src/helper/db/__tests__/ensure-db.spec.ts | 3 +- .../src/helper/db/__tests__/migration.spec.ts | 2 +- .../db/__tests__/workspace-db-adapter.spec.ts | 2 +- .../workspace/__tests__/handlers.spec.ts | 3 +- .../src/main/__tests__/integration.spec.ts | 2 +- apps/electron/tests/utils.ts | 26 ----- apps/electron/tsconfig.json | 4 +- apps/electron/tsconfig.tests.json | 2 +- package.json | 2 + tests/affine-desktop-cloud/e2e/basic.spec.ts | 34 ++++++ tests/affine-desktop-cloud/package.json | 15 +++ .../affine-desktop-cloud/playwright.config.ts | 61 +++++++++++ tests/affine-desktop-cloud/tsconfig.json | 16 +++ .../affine-desktop}/e2e/basic.spec.ts | 3 +- .../affine-desktop}/e2e/workspace.spec.ts | 3 +- tests/affine-desktop/package.json | 15 +++ .../affine-desktop}/playwright.config.ts | 1 - tests/affine-desktop/tsconfig.json | 16 +++ .../e2e/fixture.ts => tests/kit/electron.ts | 48 +++++---- tests/kit/package.json | 1 + tests/kit/utils/utils.ts | 26 +++++ tests/kit/utils/workspace.ts | 10 +- tsconfig.json | 3 + yarn.lock | 25 ++++- 28 files changed, 366 insertions(+), 83 deletions(-) delete mode 100644 apps/electron/e2e/setup.ts create mode 100644 tests/affine-desktop-cloud/e2e/basic.spec.ts create mode 100644 tests/affine-desktop-cloud/package.json create mode 100644 tests/affine-desktop-cloud/playwright.config.ts create mode 100644 tests/affine-desktop-cloud/tsconfig.json rename {apps/electron => tests/affine-desktop}/e2e/basic.spec.ts (99%) rename {apps/electron => tests/affine-desktop}/e2e/workspace.spec.ts (98%) create mode 100644 tests/affine-desktop/package.json rename {apps/electron => tests/affine-desktop}/playwright.config.ts (95%) create mode 100644 tests/affine-desktop/tsconfig.json rename apps/electron/e2e/fixture.ts => tests/kit/electron.ts (79%) diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 94b28e89a..81a596c00 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -101,6 +101,7 @@ jobs: with: playwright-install: true hard-link-nm: false + - name: Build AFFiNE native uses: ./.github/actions/build-rust with: @@ -125,21 +126,15 @@ jobs: - name: Build Desktop Layers 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 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: COVERAGE: true - name: Run desktop tests if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }} - run: yarn workspace @affine/electron test + run: yarn workspace @affine-test/affine-desktop e2e env: COVERAGE: true @@ -149,7 +144,7 @@ jobs: SKIP_BUNDLE: true 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' }} run: | yarn ts-node-esm ./scripts/macos-arm64-output-check.mts diff --git a/.github/workflows/build-server.yml b/.github/workflows/build-server.yml index 91e134360..5c8989ba6 100644 --- a/.github/workflows/build-server.yml +++ b/.github/workflows/build-server.yml @@ -184,7 +184,7 @@ jobs: path: ./apps/server - 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: COVERAGE: true DATABASE_URL: postgresql://affine:affine@localhost:5432/affine @@ -208,3 +208,103 @@ jobs: name: test-results-e2e-server path: ./tests/affine-cloud/test-results 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 diff --git a/apps/electron/e2e/setup.ts b/apps/electron/e2e/setup.ts deleted file mode 100644 index 55ee73db6..000000000 --- a/apps/electron/e2e/setup.ts +++ /dev/null @@ -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, '..'), - }); -} diff --git a/apps/electron/package.json b/apps/electron/package.json index be7292ab6..f97550628 100644 --- a/apps/electron/package.json +++ b/apps/electron/package.json @@ -14,10 +14,10 @@ "dev": "yarn cross-env DEV_SERVER_URL=http://localhost:8080 node scripts/dev.mjs", "dev:prod": "yarn node scripts/dev.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", "package": "electron-forge package", "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" }, "config": { @@ -43,7 +43,6 @@ "@electron/remote": "2.0.11", "@reforged/maker-appimage": "^3.3.1", "@toeverything/infra": "workspace:*", - "@types/fs-extra": "^11.0.1", "@types/uuid": "^9.0.3", "cross-env": "7.0.3", "electron": "^26.1.0", diff --git a/apps/electron/src/helper/db/__tests__/ensure-db.spec.ts b/apps/electron/src/helper/db/__tests__/ensure-db.spec.ts index 3dc36ed6e..3734a22bc 100644 --- a/apps/electron/src/helper/db/__tests__/ensure-db.spec.ts +++ b/apps/electron/src/helper/db/__tests__/ensure-db.spec.ts @@ -1,11 +1,10 @@ import path from 'node:path'; import { setTimeout } from 'node:timers/promises'; +import { removeWithRetry } from '@affine-test/kit/utils/utils'; import { v4 } from 'uuid'; import { afterEach, beforeEach, expect, test, vi } from 'vitest'; -import { removeWithRetry } from '../../../../tests/utils'; - const tmpDir = path.join(__dirname, 'tmp'); const appDataPath = path.join(tmpDir, 'app-data'); diff --git a/apps/electron/src/helper/db/__tests__/migration.spec.ts b/apps/electron/src/helper/db/__tests__/migration.spec.ts index b066b78be..f59bad5ed 100644 --- a/apps/electron/src/helper/db/__tests__/migration.spec.ts +++ b/apps/electron/src/helper/db/__tests__/migration.spec.ts @@ -1,10 +1,10 @@ import path from 'node:path'; import { SqliteConnection } from '@affine/native'; +import { removeWithRetry } from '@affine-test/kit/utils/utils'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { applyUpdate, Doc as YDoc } from 'yjs'; -import { removeWithRetry } from '../../../../tests/utils'; import { copyToTemp, migrateToSubdocAndReplaceDatabase } from '../migration'; const tmpDir = path.join(__dirname, 'tmp'); diff --git a/apps/electron/src/helper/db/__tests__/workspace-db-adapter.spec.ts b/apps/electron/src/helper/db/__tests__/workspace-db-adapter.spec.ts index e44c2dbd4..8afa93bd1 100644 --- a/apps/electron/src/helper/db/__tests__/workspace-db-adapter.spec.ts +++ b/apps/electron/src/helper/db/__tests__/workspace-db-adapter.spec.ts @@ -1,11 +1,11 @@ import path from 'node:path'; +import { removeWithRetry } from '@affine-test/kit/utils/utils'; import fs from 'fs-extra'; import { v4 } from 'uuid'; import { afterEach, expect, test, vi } from 'vitest'; import { Doc as YDoc, encodeStateAsUpdate } from 'yjs'; -import { removeWithRetry } from '../../../../tests/utils'; import { dbSubjects } from '../subjects'; const tmpDir = path.join(__dirname, 'tmp'); diff --git a/apps/electron/src/helper/workspace/__tests__/handlers.spec.ts b/apps/electron/src/helper/workspace/__tests__/handlers.spec.ts index 94251dd6c..9031763c3 100644 --- a/apps/electron/src/helper/workspace/__tests__/handlers.spec.ts +++ b/apps/electron/src/helper/workspace/__tests__/handlers.spec.ts @@ -1,11 +1,10 @@ import path from 'node:path'; +import { removeWithRetry } from '@affine-test/kit/utils/utils'; import fs from 'fs-extra'; import { v4 } from 'uuid'; import { afterEach, describe, expect, test, vi } from 'vitest'; -import { removeWithRetry } from '../../../../tests/utils'; - const tmpDir = path.join(__dirname, 'tmp'); const appDataPath = path.join(tmpDir, 'app-data'); diff --git a/apps/electron/src/main/__tests__/integration.spec.ts b/apps/electron/src/main/__tests__/integration.spec.ts index 4c6cf23a4..b4c90a370 100644 --- a/apps/electron/src/main/__tests__/integration.spec.ts +++ b/apps/electron/src/main/__tests__/integration.spec.ts @@ -1,10 +1,10 @@ import assert from 'node:assert'; import path from 'node:path'; +import { removeWithRetry } from '@affine-test/kit/utils/utils'; import fs from 'fs-extra'; import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; -import { removeWithRetry } from '../../../tests/utils'; import type { MainIPCHandlerMap } from '../exposed'; const registeredHandlers = new Map< diff --git a/apps/electron/tests/utils.ts b/apps/electron/tests/utils.ts index e92a6c2f7..e69de29bb 100644 --- a/apps/electron/tests/utils.ts +++ b/apps/electron/tests/utils.ts @@ -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; -} diff --git a/apps/electron/tsconfig.json b/apps/electron/tsconfig.json index bb2f2d3d9..02c608b89 100644 --- a/apps/electron/tsconfig.json +++ b/apps/electron/tsconfig.json @@ -8,13 +8,13 @@ "emitDecoratorMetadata": true, "experimentalDecorators": true, "types": ["node"], - "outDir": "dist", + "outDir": "lib", "moduleResolution": "node", "resolveJsonModule": true, "noImplicitOverride": true }, "include": ["./src"], - "exclude": ["node_modules", "out", "dist", "**/__tests__/**/*"], + "exclude": ["node_modules", "lib", "dist", "**/__tests__/**/*"], "references": [ { "path": "../../packages/infra" diff --git a/apps/electron/tsconfig.tests.json b/apps/electron/tsconfig.tests.json index 4123c61bc..4af2e315d 100644 --- a/apps/electron/tsconfig.tests.json +++ b/apps/electron/tsconfig.tests.json @@ -3,7 +3,7 @@ "compilerOptions": { "composite": true }, - "include": ["**/__tests__/**/*", "./tests", "./e2e"], + "include": ["**/__tests__/**/*", "./tests"], "references": [ { "path": "./tsconfig.json" diff --git a/package.json b/package.json index 63f82b6ad..9cab43d2a 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "tests/kit", "tests/affine-legacy/*", "tests/affine-local", + "tests/affine-desktop", + "tests/affine-desktop-cloud", "tests/affine-plugin", "tests/affine-prototype", "tests/affine-cloud" diff --git a/tests/affine-desktop-cloud/e2e/basic.spec.ts b/tests/affine-desktop-cloud/e2e/basic.spec.ts new file mode 100644 index 000000000..ffeae0008 --- /dev/null +++ b/tests/affine-desktop-cloud/e2e/basic.spec.ts @@ -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); +}); diff --git a/tests/affine-desktop-cloud/package.json b/tests/affine-desktop-cloud/package.json new file mode 100644 index 000000000..7da4340dc --- /dev/null +++ b/tests/affine-desktop-cloud/package.json @@ -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" +} diff --git a/tests/affine-desktop-cloud/playwright.config.ts b/tests/affine-desktop-cloud/playwright.config.ts new file mode 100644 index 000000000..2a274a0d1 --- /dev/null +++ b/tests/affine-desktop-cloud/playwright.config.ts @@ -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; diff --git a/tests/affine-desktop-cloud/tsconfig.json b/tests/affine-desktop-cloud/tsconfig.json new file mode 100644 index 000000000..c7587f158 --- /dev/null +++ b/tests/affine-desktop-cloud/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "esModuleInterop": true, + "outDir": "lib" + }, + "include": ["e2e"], + "references": [ + { + "path": "../kit" + }, + { + "path": "../fixtures" + } + ] +} diff --git a/apps/electron/e2e/basic.spec.ts b/tests/affine-desktop/e2e/basic.spec.ts similarity index 99% rename from apps/electron/e2e/basic.spec.ts rename to tests/affine-desktop/e2e/basic.spec.ts index 96f37c4a6..31acf3a28 100644 --- a/apps/electron/e2e/basic.spec.ts +++ b/tests/affine-desktop/e2e/basic.spec.ts @@ -1,10 +1,9 @@ import { platform } from 'node:os'; +import { test } from '@affine-test/kit/electron'; import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar'; import { expect } from '@playwright/test'; -import { test } from './fixture'; - test('new page', async ({ page, workspace }) => { await page.getByTestId('new-page-button').click({ delay: 100, diff --git a/apps/electron/e2e/workspace.spec.ts b/tests/affine-desktop/e2e/workspace.spec.ts similarity index 98% rename from apps/electron/e2e/workspace.spec.ts rename to tests/affine-desktop/e2e/workspace.spec.ts index 708c4f02c..ee0e47253 100644 --- a/apps/electron/e2e/workspace.spec.ts +++ b/tests/affine-desktop/e2e/workspace.spec.ts @@ -1,10 +1,9 @@ import path from 'node:path'; +import { test } from '@affine-test/kit/electron'; import { expect } from '@playwright/test'; import fs from 'fs-extra'; -import { test } from './fixture'; - test('check workspace has a DB file', async ({ appInfo, workspace }) => { const w = await workspace.current(); const dbPath = path.join( diff --git a/tests/affine-desktop/package.json b/tests/affine-desktop/package.json new file mode 100644 index 000000000..243c2ed49 --- /dev/null +++ b/tests/affine-desktop/package.json @@ -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" +} diff --git a/apps/electron/playwright.config.ts b/tests/affine-desktop/playwright.config.ts similarity index 95% rename from apps/electron/playwright.config.ts rename to tests/affine-desktop/playwright.config.ts index 94b4c898c..e3f0ea6ba 100644 --- a/apps/electron/playwright.config.ts +++ b/tests/affine-desktop/playwright.config.ts @@ -12,7 +12,6 @@ import type { PlaywrightTestConfig } from '@playwright/test'; */ const config: PlaywrightTestConfig = { testDir: './e2e', - testIgnore: '**/lib/**', fullyParallel: true, timeout: process.env.CI ? 50_000 : 30_000, use: { diff --git a/tests/affine-desktop/tsconfig.json b/tests/affine-desktop/tsconfig.json new file mode 100644 index 000000000..c7587f158 --- /dev/null +++ b/tests/affine-desktop/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "esModuleInterop": true, + "outDir": "lib" + }, + "include": ["e2e"], + "references": [ + { + "path": "../kit" + }, + { + "path": "../fixtures" + } + ] +} diff --git a/apps/electron/e2e/fixture.ts b/tests/kit/electron.ts similarity index 79% rename from apps/electron/e2e/fixture.ts rename to tests/kit/electron.ts index 9868bcccb..8253a9b55 100644 --- a/apps/electron/e2e/fixture.ts +++ b/tests/kit/electron.ts @@ -1,19 +1,21 @@ -/* eslint-disable no-empty-pattern */ import crypto from 'node:crypto'; 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 { enableCoverage, istanbulTempDir, test as base, testResultDir, -} from '@affine-test/kit/playwright'; -import type { Page } from '@playwright/test'; -import fs from 'fs-extra'; -import type { ElectronApplication } from 'playwright'; -import { _electron as electron } from 'playwright'; +} from './playwright'; +import { removeWithRetry } from './utils/utils'; -import { removeWithRetry } from '../tests/utils'; +const projectRoot = resolve(__dirname, '..', '..'); +const electronRoot = resolve(projectRoot, 'apps', 'electron'); function generateUUID() { return crypto.randomUUID(); @@ -37,14 +39,7 @@ export const test = base.extend<{ await page.getByTestId('onboarding-modal-close-button').click({ delay: 100, }); - if (!process.env.CI) { - await electronApp.evaluate(({ BrowserWindow }) => { - BrowserWindow.getAllWindows()[0].webContents.openDevTools({ - mode: 'detach', - }); - }); - } - // wat for blocksuite to be loaded + // wait for blocksuite to be loaded await page.waitForSelector('v-line'); if (enableCoverage) { await fs.promises.mkdir(istanbulTempDir, { recursive: true }); @@ -71,15 +66,16 @@ export const test = base.extend<{ } await page.close(); }, + // eslint-disable-next-line no-empty-pattern electronApp: async ({}, use) => { // a random id to avoid conflicts between tests const id = generateUUID(); const ext = process.platform === 'win32' ? '.cmd' : ''; - const dist = resolve(__dirname, '..', 'dist'); - const clonedDist = resolve(__dirname, '../e2e-dist-' + id); + const dist = resolve(electronRoot, 'dist'); + const clonedDist = resolve(electronRoot, 'e2e-dist-' + id); await fs.copy(dist, clonedDist); const packageJson = await fs.readJSON( - resolve(__dirname, '..', 'package.json') + resolve(electronRoot, 'package.json') ); // overwrite the app name packageJson.name = 'affine-test-' + id; @@ -88,15 +84,27 @@ export const test = base.extend<{ // write to the cloned dist await fs.writeJSON(resolve(clonedDist, 'package.json'), packageJson); + const env: Record = {}; + 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({ args: [clonedDist], + env, executablePath: resolve( - __dirname, - '..', + electronRoot, 'node_modules', '.bin', `electron${ext}` ), + cwd: clonedDist, recordVideo: { dir: testResultDir, }, diff --git a/tests/kit/package.json b/tests/kit/package.json index 43e563e8c..a7aa138e6 100644 --- a/tests/kit/package.json +++ b/tests/kit/package.json @@ -4,6 +4,7 @@ "type": "module", "version": "0.9.0-canary.12", "exports": { + "./electron": "./electron.ts", "./playwright": "./playwright.ts", "./utils/*": "./utils/*.ts" }, diff --git a/tests/kit/utils/utils.ts b/tests/kit/utils/utils.ts index 260899657..2345a8945 100644 --- a/tests/kit/utils/utils.ts +++ b/tests/kit/utils/utils.ts @@ -1,4 +1,7 @@ +import { setTimeout } from 'node:timers/promises'; + import type { Page } from '@playwright/test'; +import fs from 'fs-extra'; export async function waitForLogMessage( 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; +} diff --git a/tests/kit/utils/workspace.ts b/tests/kit/utils/workspace.ts index a5d8fc23e..eb77aaddb 100644 --- a/tests/kit/utils/workspace.ts +++ b/tests/kit/utils/workspace.ts @@ -19,12 +19,20 @@ export async function createLocalWorkspace( // open create workspace modal await page.getByTestId('new-workspace').click(); + const isDesktop: boolean = await page.evaluate(() => { + return !!window.appInfo?.electron; + }, []); + // input workspace name await page.getByPlaceholder('Set a Workspace name').click(); await page.getByPlaceholder('Set a Workspace name').fill(params.name); // click create button - return page.getByRole('button', { name: 'Create' }).click({ + await page.getByRole('button', { name: 'Create' }).click({ delay: 500, }); + + if (isDesktop) { + await page.getByTestId('create-workspace-continue-button').click(); + } } diff --git a/tsconfig.json b/tsconfig.json index 4bc570294..f9780644c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -181,6 +181,9 @@ { "path": "./tests/affine-cloud" }, + { + "path": "./tests/affine-desktop" + }, { "path": "./tests/affine-legacy/0.8.0-canary.7" }, diff --git a/yarn.lock b/yarn.lock index 4540a1120..83ab101df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -80,6 +80,30 @@ __metadata: languageName: unknown 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": version: 0.0.0-use.local resolution: "@affine-test/affine-local@workspace:tests/affine-local" @@ -388,7 +412,6 @@ __metadata: "@electron/remote": 2.0.11 "@reforged/maker-appimage": ^3.3.1 "@toeverything/infra": "workspace:*" - "@types/fs-extra": ^11.0.1 "@types/uuid": ^9.0.3 async-call-rpc: ^6.3.1 cross-env: 7.0.3