diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 488d427d85..ef880fbda9 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -17,16 +17,6 @@ jobs:
uses: ./.github/actions/setup-node
- run: yarn lint --max-warnings=0
- install-all:
- name: Install All Dependencies
- runs-on: ubuntu-latest
- environment: development
-
- steps:
- - uses: actions/checkout@v3
- - name: Install All Dependencies
- uses: ./.github/actions/setup-node
-
build-storybook:
name: Build Storybook
runs-on: ubuntu-latest
@@ -44,6 +34,23 @@ jobs:
path: ./packages/component/storybook-static
if-no-files-found: error
+ build-electron:
+ name: Build @affine/electron
+ runs-on: ubuntu-latest
+ environment: development
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Node.js
+ uses: ./.github/actions/setup-node
+ - name: Build Electron
+ working-directory: apps/electron
+ run: yarn exec ts-node-esm ./scripts/build-ci.mts
+ - name: Upload Ubuntu desktop artifact
+ uses: actions/upload-artifact@v3
+ with:
+ name: affine-ubuntu
+ path: ./apps/electron/dist
+
build:
name: Build @affine/web
runs-on: ubuntu-latest
@@ -230,6 +237,48 @@ jobs:
path: ./test-results
if-no-files-found: ignore
+ dekstop-test:
+ name: Desktop Test
+ runs-on: ubuntu-latest
+ environment: development
+ needs: [build, build-electron]
+ steps:
+ - uses: actions/checkout@v3
+ - name: Setup Node.js
+ uses: ./.github/actions/setup-node
+ with:
+ playwright-install: true
+ - name: Download Ubuntu desktop artifact
+ uses: actions/download-artifact@v3
+ with:
+ name: affine-ubuntu
+ path: ./apps/electron/dist
+
+ - name: Download artifact
+ uses: actions/download-artifact@v3
+ with:
+ name: next-js
+ path: ./apps/web/.next
+
+ - name: Generate static files
+ run: yarn export
+ working-directory: ./apps/web
+
+ - name: Move static files to electron
+ run: mv ./apps/web/out ./apps/electron/resources/web-static
+
+ - name: Run desktop tests
+ run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn test
+ working-directory: apps/electron
+
+ - name: Upload test results
+ if: ${{ failure() }}
+ uses: actions/upload-artifact@v2
+ with:
+ name: test-results-e2e-${{ matrix.shard }}
+ path: ./test-results
+ if-no-files-found: ignore
+
unit-test:
name: Unit Test
runs-on: ubuntu-latest
diff --git a/README.md b/README.md
index d8b4104030..6c97c680f1 100644
--- a/README.md
+++ b/README.md
@@ -134,7 +134,6 @@ We would like to express our gratitude to all the individuals who have already c
-
## Self-Host
Get started with Docker and deploy your own feature-rich, restriction-free deployment of AFFiNE - check the [latest packages].
diff --git a/apps/electron/layers/main/src/main-window.ts b/apps/electron/layers/main/src/main-window.ts
index b725b96209..99ecbd9dd7 100644
--- a/apps/electron/layers/main/src/main-window.ts
+++ b/apps/electron/layers/main/src/main-window.ts
@@ -5,7 +5,8 @@ import { join } from 'path';
import { logger } from '../../logger';
import { isMacOS } from '../../utils';
-const IS_DEV = process.env.NODE_ENV === 'development';
+const IS_DEV: boolean =
+ process.env.NODE_ENV === 'development' && !process.env.CI;
async function createWindow() {
logger.info('create window');
diff --git a/apps/electron/package.json b/apps/electron/package.json
index 70664c4b81..0aebf29430 100644
--- a/apps/electron/package.json
+++ b/apps/electron/package.json
@@ -16,7 +16,8 @@
"make-windows-x64": "electron-forge make --platform=win32 --arch=x64",
"make-linux-x64": "electron-forge make --platform=linux --arch=x64",
"rebuild:for-test": "yarn rebuild better-sqlite3",
- "rebuild:for-electron": "yarn electron-rebuild"
+ "rebuild:for-electron": "yarn electron-rebuild",
+ "test": "playwright test"
},
"config": {
"forge": "./forge.config.js"
@@ -42,6 +43,8 @@
"electron-window-state": "^5.0.3",
"esbuild": "^0.17.18",
"fs-extra": "^11.1.1",
+ "playwright": "^1.32.3",
+ "ts-node": "^10.9.1",
"undici": "^5.22.0",
"zx": "^7.2.1"
},
@@ -62,5 +65,9 @@
"stableVersion": "0.5.3",
"installConfig": {
"hoistingLimits": "workspaces"
+ },
+ "peerDependencies": {
+ "playwright": "*",
+ "ts-node": "*"
}
}
diff --git a/apps/electron/playwright.config.ts b/apps/electron/playwright.config.ts
new file mode 100644
index 0000000000..4e91b35964
--- /dev/null
+++ b/apps/electron/playwright.config.ts
@@ -0,0 +1,27 @@
+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: './tests',
+ fullyParallel: true,
+ timeout: process.env.CI ? 50_000 : 30_000,
+ use: {
+ viewport: { width: 1440, height: 800 },
+ },
+};
+
+if (process.env.CI) {
+ config.retries = 3;
+ config.workers = '50%';
+}
+
+export default config;
diff --git a/apps/electron/scripts/build-ci.mts b/apps/electron/scripts/build-ci.mts
new file mode 100755
index 0000000000..ef6c70339f
--- /dev/null
+++ b/apps/electron/scripts/build-ci.mts
@@ -0,0 +1,17 @@
+#!/usr/bin/env ts-node-esm
+import * as esbuild from 'esbuild';
+
+import { config } from './common.mjs';
+
+const common = config();
+await esbuild.build(common.preload);
+
+await esbuild.build({
+ ...common.main,
+ define: {
+ ...common.main.define,
+ 'process.env.NODE_ENV': `"production"`,
+ },
+});
+
+console.log('Compiled successfully.');
diff --git a/apps/electron/scripts/common.mjs b/apps/electron/scripts/common.mjs
index fbae63ae5e..10240d3a5f 100644
--- a/apps/electron/scripts/common.mjs
+++ b/apps/electron/scripts/common.mjs
@@ -1,4 +1,9 @@
-const NODE_MAJOR_VERSION = 18;
+import { resolve } from 'node:path';
+
+import { fileURLToPath } from 'url';
+
+export const root = fileURLToPath(new URL('..', import.meta.url));
+export const NODE_MAJOR_VERSION = 18;
const nativeNodeModulesPlugin = {
name: 'native-node-modules',
@@ -14,7 +19,7 @@ const nativeNodeModulesPlugin = {
const ENV_MACROS = ['AFFINE_GOOGLE_CLIENT_ID', 'AFFINE_GOOGLE_CLIENT_SECRET'];
/** @return {{main: import('esbuild').BuildOptions, preload: import('esbuild').BuildOptions}} */
-export default () => {
+export const config = () => {
const define = Object.fromEntries(
ENV_MACROS.map(key => [
'process.env.' + key,
@@ -23,8 +28,8 @@ export default () => {
);
return {
main: {
- entryPoints: ['layers/main/src/index.ts'],
- outdir: 'dist/layers/main',
+ entryPoints: [resolve(root, './layers/main/src/index.ts')],
+ outdir: resolve(root, './dist/layers/main'),
bundle: true,
target: `node${NODE_MAJOR_VERSION}`,
platform: 'node',
@@ -33,8 +38,8 @@ export default () => {
define: define,
},
preload: {
- entryPoints: ['layers/preload/src/index.ts'],
- outdir: 'dist/layers/preload',
+ entryPoints: [resolve(root, './layers/preload/src/index.ts')],
+ outdir: resolve(root, './dist/layers/preload'),
bundle: true,
target: `node${NODE_MAJOR_VERSION}`,
platform: 'node',
diff --git a/apps/electron/scripts/dev.mjs b/apps/electron/scripts/dev.mjs
index ffca8e4274..cc4399b239 100644
--- a/apps/electron/scripts/dev.mjs
+++ b/apps/electron/scripts/dev.mjs
@@ -1,15 +1,11 @@
import { spawn } from 'node:child_process';
import { readFileSync } from 'node:fs';
import path from 'node:path';
-import { fileURLToPath } from 'node:url';
import electronPath from 'electron';
import * as esbuild from 'esbuild';
-import commonFn from './common.mjs';
-
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
+import { config, root } from './common.mjs';
/** @type 'production' | 'development'' */
const mode = (process.env.NODE_ENV = process.env.NODE_ENV || 'development');
@@ -22,9 +18,9 @@ const stderrFilterPatterns = [
/ExtensionLoadWarning/,
];
-// these are set before calling commonFn so we have a chance to override them
+// these are set before calling `config`, so we have a chance to override them
try {
- const devJson = readFileSync(path.resolve(__dirname, '../dev.json'), 'utf-8');
+ const devJson = readFileSync(path.resolve(root, './dev.json'), 'utf-8');
const devEnv = JSON.parse(devJson);
Object.assign(process.env, devEnv);
} catch (err) {
@@ -67,7 +63,7 @@ function spawnOrReloadElectron() {
spawnProcess.on('exit', process.exit);
}
-const common = commonFn();
+const common = config();
async function main() {
async function watchPreload(onInitialBuild) {
diff --git a/apps/electron/scripts/generate-assets.mjs b/apps/electron/scripts/generate-assets.mjs
index 38f6421381..f5f416fa5b 100644
--- a/apps/electron/scripts/generate-assets.mjs
+++ b/apps/electron/scripts/generate-assets.mjs
@@ -5,7 +5,7 @@ import path from 'node:path';
import * as esbuild from 'esbuild';
-import commonFn from './common.mjs';
+import { config } from './common.mjs';
const repoRootDir = path.join(__dirname, '..', '..', '..');
const electronRootDir = path.join(__dirname, '..');
@@ -77,7 +77,7 @@ async function cleanup() {
}
async function buildLayers() {
- const common = commonFn();
+ const common = config();
await esbuild.build(common.preload);
await esbuild.build({
diff --git a/apps/electron/tests/basic.spec.ts b/apps/electron/tests/basic.spec.ts
new file mode 100644
index 0000000000..1062d2dba0
--- /dev/null
+++ b/apps/electron/tests/basic.spec.ts
@@ -0,0 +1,22 @@
+import { resolve } from 'node:path';
+
+import { expect, test } from '@playwright/test';
+import { _electron as electron } from 'playwright';
+
+test('new page', async () => {
+ const electronApp = await electron.launch({
+ args: [resolve(__dirname, '..')],
+ executablePath: resolve(__dirname, '../node_modules/.bin/electron'),
+ });
+ const page = await electronApp.firstWindow();
+ await page.getByTestId('new-page-button').click({
+ delay: 100,
+ });
+ await page.waitForSelector('v-line');
+ const flavour = await page.evaluate(
+ // @ts-expect-error
+ () => globalThis.currentWorkspace.flavour
+ );
+ expect(flavour).toBe('local');
+ await electronApp.close();
+});
diff --git a/apps/electron/tests/tsconfig.json b/apps/electron/tests/tsconfig.json
new file mode 100644
index 0000000000..4c43f890a3
--- /dev/null
+++ b/apps/electron/tests/tsconfig.json
@@ -0,0 +1,8 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "baseUrl": ".",
+ "noEmit": true
+ },
+ "include": ["**.spec.ts", "**.test.ts"]
+}
diff --git a/apps/electron/tsconfig.json b/apps/electron/tsconfig.json
new file mode 100644
index 0000000000..59f8b6d127
--- /dev/null
+++ b/apps/electron/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "composite": true,
+ "target": "ESNext",
+ "module": "ESNext",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "moduleResolution": "bundler",
+ "isolatedModules": false,
+ "resolveJsonModule": true,
+ "types": ["node"],
+ "outDir": "dist",
+ "noEmit": false
+ },
+ "include": ["layers", "types", "package.json"],
+ "exclude": ["out", "dist", "node_modules"],
+ "references": [
+ {
+ "path": "./tsconfig.node.json"
+ }
+ ],
+ "ts-node": {
+ "esm": true,
+ "experimentalSpecifierResolution": "node"
+ }
+}
diff --git a/apps/electron/tsconfig.node.json b/apps/electron/tsconfig.node.json
new file mode 100644
index 0000000000..b9e486d423
--- /dev/null
+++ b/apps/electron/tsconfig.node.json
@@ -0,0 +1,11 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "target": "ESNext",
+ "module": "ESNext",
+ "resolveJsonModule": true,
+ "moduleResolution": "Node",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["./scripts", "package.json"]
+}
diff --git a/apps/web/src/components/pure/workspace-slider-bar/changeLog/index.tsx b/apps/web/src/components/pure/workspace-slider-bar/changeLog/index.tsx
index d74b9117d6..87c1cf1098 100644
--- a/apps/web/src/components/pure/workspace-slider-bar/changeLog/index.tsx
+++ b/apps/web/src/components/pure/workspace-slider-bar/changeLog/index.tsx
@@ -37,7 +37,7 @@ export const ChangeLog = () => {
diff --git a/apps/web/src/components/pure/workspace-slider-bar/index.tsx b/apps/web/src/components/pure/workspace-slider-bar/index.tsx
index d352c34e63..dc5f18e37d 100644
--- a/apps/web/src/components/pure/workspace-slider-bar/index.tsx
+++ b/apps/web/src/components/pure/workspace-slider-bar/index.tsx
@@ -1,4 +1,3 @@
-
import { config } from '@affine/env';
import { useTranslation } from '@affine/i18n';
import { WorkspaceFlavour } from '@affine/workspace/type';
diff --git a/tsconfig.json b/tsconfig.json
index 2d17ebe792..b77bee95d0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -37,6 +37,9 @@
{
"path": "./tests"
},
+ {
+ "path": "./apps/electron/tests"
+ },
{
"path": "./apps/web"
},
@@ -66,6 +69,9 @@
},
{
"path": "./tsconfig.node.json"
+ },
+ {
+ "path": "./apps/electron"
}
],
"files": [],
diff --git a/vitest.config.ts b/vitest.config.ts
index 0769711588..5c127c3c4a 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -27,10 +27,10 @@ export default defineConfig({
'packages/**/*.spec.tsx',
'apps/web/**/*.spec.ts',
'apps/web/**/*.spec.tsx',
- 'apps/electron/**/*.spec.ts',
'tests/unit/**/*.spec.ts',
'tests/unit/**/*.spec.tsx',
],
+ exclude: ['**/node_modules', '**/dist', '**/build', '**/out'],
testTimeout: 5000,
coverage: {
provider: 'istanbul', // or 'c8'
diff --git a/yarn.lock b/yarn.lock
index b1ea409bc6..01e0c822a8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -132,9 +132,14 @@ __metadata:
electron-window-state: ^5.0.3
esbuild: ^0.17.18
fs-extra: ^11.1.1
+ playwright: ^1.32.3
+ ts-node: ^10.9.1
undici: ^5.22.0
yjs: ^13.6.0
zx: ^7.2.1
+ peerDependencies:
+ playwright: "*"
+ ts-node: "*"
languageName: unknown
linkType: soft
@@ -18914,7 +18919,7 @@ __metadata:
languageName: node
linkType: hard
-"playwright@npm:^1.14.0":
+"playwright@npm:^1.14.0, playwright@npm:^1.32.3":
version: 1.32.3
resolution: "playwright@npm:1.32.3"
dependencies: