mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-22 03:33:06 +08:00
refactor(electron): fix vitest and add behavior test (#4655)
This commit is contained in:
@@ -9,22 +9,13 @@ export const rootDir = resolve(electronDir, '..', '..', '..');
|
||||
|
||||
export const NODE_MAJOR_VERSION = 18;
|
||||
|
||||
// hard-coded for now:
|
||||
// fixme(xp): report error if app is not running on DEV_SERVER_URL
|
||||
const DEV_SERVER_URL = process.env.DEV_SERVER_URL;
|
||||
|
||||
export const mode = (process.env.NODE_ENV =
|
||||
process.env.NODE_ENV || 'development');
|
||||
|
||||
export const config = (): BuildOptions => {
|
||||
const define = Object.fromEntries([
|
||||
['process.env.NODE_ENV', `"${mode}"`],
|
||||
['process.env.USE_WORKER', '"true"'],
|
||||
]);
|
||||
const define: Record<string, string> = {};
|
||||
|
||||
if (DEV_SERVER_URL) {
|
||||
define['process.env.DEV_SERVER_URL'] = `"${DEV_SERVER_URL}"`;
|
||||
}
|
||||
define['REPLACE_ME_BUILD_ENV'] = `"${process.env.BUILD_TYPE ?? 'stable'}"`;
|
||||
|
||||
return {
|
||||
entryPoints: [
|
||||
@@ -45,11 +36,11 @@ export const config = (): BuildOptions => {
|
||||
'semver',
|
||||
'tinykeys',
|
||||
],
|
||||
define: define,
|
||||
format: 'cjs',
|
||||
loader: {
|
||||
'.node': 'copy',
|
||||
},
|
||||
define,
|
||||
assetNames: '[name]',
|
||||
treeShaking: true,
|
||||
sourcemap: 'linked',
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/* eslint-disable no-async-promise-executor */
|
||||
import { spawn } from 'node:child_process';
|
||||
|
||||
import type { ChildProcessWithoutNullStreams } from 'child_process';
|
||||
import electronPath from 'electron';
|
||||
import type { BuildContext } from 'esbuild';
|
||||
import * as esbuild from 'esbuild';
|
||||
|
||||
import { config } from './common';
|
||||
import { config, electronDir } from './common';
|
||||
|
||||
// this means we don't spawn electron windows, mainly for testing
|
||||
const watchMode = process.argv.includes('--watch');
|
||||
@@ -30,7 +29,10 @@ function spawnOrReloadElectron() {
|
||||
spawnProcess = null;
|
||||
}
|
||||
|
||||
spawnProcess = spawn(String(electronPath), ['.']);
|
||||
spawnProcess = spawn('electron', ['.'], {
|
||||
cwd: electronDir,
|
||||
env: process.env,
|
||||
});
|
||||
|
||||
spawnProcess.stdout.on('data', d => {
|
||||
const str = d.toString().trim();
|
||||
@@ -38,6 +40,7 @@ function spawnOrReloadElectron() {
|
||||
console.log(str);
|
||||
}
|
||||
});
|
||||
|
||||
spawnProcess.stderr.on('data', d => {
|
||||
const data = d.toString().trim();
|
||||
if (!data) return;
|
||||
@@ -47,16 +50,20 @@ function spawnOrReloadElectron() {
|
||||
});
|
||||
|
||||
// Stops the watch script when the application has quit
|
||||
spawnProcess.on('exit', process.exit);
|
||||
spawnProcess.on('exit', code => {
|
||||
if (code && code !== 0) {
|
||||
console.log(`Electron exited with code ${code}`);
|
||||
}
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
}
|
||||
|
||||
const common = config();
|
||||
|
||||
async function watchLayers() {
|
||||
return new Promise<void>(async resolve => {
|
||||
let initialBuild = false;
|
||||
|
||||
const buildContext = await esbuild.context({
|
||||
let initialBuild = false;
|
||||
return new Promise<BuildContext>(resolve => {
|
||||
const buildContextPromise = esbuild.context({
|
||||
...common,
|
||||
plugins: [
|
||||
...(common.plugins ?? []),
|
||||
@@ -68,7 +75,7 @@ async function watchLayers() {
|
||||
console.log(`[layers] has changed, [re]launching electron...`);
|
||||
spawnOrReloadElectron();
|
||||
} else {
|
||||
resolve();
|
||||
buildContextPromise.then(resolve);
|
||||
initialBuild = true;
|
||||
}
|
||||
});
|
||||
@@ -76,19 +83,18 @@ async function watchLayers() {
|
||||
},
|
||||
],
|
||||
});
|
||||
await buildContext.watch();
|
||||
buildContextPromise.then(async buildContext => {
|
||||
await buildContext.watch();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await watchLayers();
|
||||
await watchLayers();
|
||||
|
||||
if (watchMode) {
|
||||
console.log(`Watching for changes...`);
|
||||
} else {
|
||||
spawnOrReloadElectron();
|
||||
console.log(`Electron is started, watching for changes...`);
|
||||
}
|
||||
if (watchMode) {
|
||||
console.log(`Watching for changes...`);
|
||||
} else {
|
||||
console.log('Starting electron...');
|
||||
spawnOrReloadElectron();
|
||||
console.log(`Electron is started, watching for changes...`);
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
tmp
|
||||
@@ -1 +0,0 @@
|
||||
tmp
|
||||
@@ -1 +0,0 @@
|
||||
tmp
|
||||
@@ -1,170 +0,0 @@
|
||||
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 type { MainIPCHandlerMap } from '../exposed';
|
||||
|
||||
const registeredHandlers = new Map<
|
||||
string,
|
||||
((...args: any[]) => Promise<any>)[]
|
||||
>();
|
||||
|
||||
type WithoutFirstParameter<T> = T extends (_: any, ...args: infer P) => infer R
|
||||
? (...args: P) => R
|
||||
: T;
|
||||
|
||||
// common mock dispatcher for ipcMain.handle AND app.on
|
||||
// alternatively, we can use single parameter for T & F, eg, dispatch('workspace:list'),
|
||||
// however this is too hard to be typed correctly
|
||||
async function dispatch<
|
||||
T extends keyof MainIPCHandlerMap,
|
||||
F extends keyof MainIPCHandlerMap[T],
|
||||
>(
|
||||
namespace: T,
|
||||
functionName: F,
|
||||
...args: Parameters<WithoutFirstParameter<MainIPCHandlerMap[T][F]>>
|
||||
): // @ts-expect-error
|
||||
ReturnType<MainIPCHandlerMap[T][F]> {
|
||||
// @ts-expect-error
|
||||
const handlers = registeredHandlers.get(namespace + ':' + functionName);
|
||||
assert(handlers);
|
||||
|
||||
// we only care about the first handler here
|
||||
return await handlers[0](null, ...args);
|
||||
}
|
||||
|
||||
const SESSION_DATA_PATH = path.join(__dirname, './tmp', 'affine-test');
|
||||
const DOCUMENTS_PATH = path.join(__dirname, './tmp', 'affine-test-documents');
|
||||
|
||||
const browserWindow = {
|
||||
isDestroyed: () => {
|
||||
return false;
|
||||
},
|
||||
setWindowButtonVisibility: (_v: boolean) => {
|
||||
// will be stubbed later
|
||||
},
|
||||
webContents: {
|
||||
send: (_type: string, ..._args: any[]) => {
|
||||
// will be stubbed later
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const ipcMain = {
|
||||
handle: (key: string, callback: (...args: any[]) => Promise<any>) => {
|
||||
const handlers = registeredHandlers.get(key) || [];
|
||||
handlers.push(callback);
|
||||
registeredHandlers.set(key, handlers);
|
||||
},
|
||||
setMaxListeners: (_n: number) => {
|
||||
// noop
|
||||
},
|
||||
};
|
||||
|
||||
const nativeTheme = {
|
||||
themeSource: 'light',
|
||||
};
|
||||
|
||||
const electronModule = {
|
||||
app: {
|
||||
getPath: (name: string) => {
|
||||
if (name === 'sessionData') {
|
||||
return SESSION_DATA_PATH;
|
||||
} else if (name === 'documents') {
|
||||
return DOCUMENTS_PATH;
|
||||
}
|
||||
throw new Error('not implemented');
|
||||
},
|
||||
name: 'affine-test',
|
||||
on: (name: string, callback: (...args: any[]) => any) => {
|
||||
const handlers = registeredHandlers.get(name) || [];
|
||||
handlers.push(callback);
|
||||
registeredHandlers.set(name, handlers);
|
||||
},
|
||||
addListener: (...args: any[]) => {
|
||||
// @ts-expect-error
|
||||
electronModule.app.on(...args);
|
||||
},
|
||||
removeListener: () => {},
|
||||
},
|
||||
BrowserWindow: {
|
||||
getAllWindows: () => {
|
||||
return [browserWindow];
|
||||
},
|
||||
},
|
||||
utilityProcess: {},
|
||||
nativeTheme: nativeTheme,
|
||||
ipcMain,
|
||||
shell: {} as Partial<Electron.Shell>,
|
||||
dialog: {} as Partial<Electron.Dialog>,
|
||||
};
|
||||
|
||||
// dynamically import handlers so that we can inject local variables to mocks
|
||||
vi.doMock('electron', () => {
|
||||
return electronModule;
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const { registerHandlers } = await import('../handlers');
|
||||
registerHandlers();
|
||||
|
||||
// should also register events
|
||||
const { registerEvents } = await import('../events');
|
||||
registerEvents();
|
||||
await fs.mkdirp(SESSION_DATA_PATH);
|
||||
|
||||
registeredHandlers.get('ready')?.forEach(fn => fn());
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// reset registered handlers
|
||||
registeredHandlers.get('before-quit')?.forEach(fn => fn());
|
||||
await removeWithRetry(SESSION_DATA_PATH);
|
||||
});
|
||||
|
||||
describe('UI handlers', () => {
|
||||
test('theme-change', async () => {
|
||||
await dispatch('ui', 'handleThemeChange', 'dark');
|
||||
expect(nativeTheme.themeSource).toBe('dark');
|
||||
await dispatch('ui', 'handleThemeChange', 'light');
|
||||
expect(nativeTheme.themeSource).toBe('light');
|
||||
});
|
||||
|
||||
test('sidebar-visibility-change (macOS)', async () => {
|
||||
vi.stubGlobal('process', { platform: 'darwin' });
|
||||
const setWindowButtonVisibility = vi.fn();
|
||||
browserWindow.setWindowButtonVisibility = setWindowButtonVisibility;
|
||||
await dispatch('ui', 'handleSidebarVisibilityChange', true);
|
||||
expect(setWindowButtonVisibility).toBeCalledWith(true);
|
||||
await dispatch('ui', 'handleSidebarVisibilityChange', false);
|
||||
expect(setWindowButtonVisibility).toBeCalledWith(false);
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
test('sidebar-visibility-change (non-macOS)', async () => {
|
||||
vi.stubGlobal('process', { platform: 'linux' });
|
||||
const setWindowButtonVisibility = vi.fn();
|
||||
browserWindow.setWindowButtonVisibility = setWindowButtonVisibility;
|
||||
await dispatch('ui', 'handleSidebarVisibilityChange', true);
|
||||
expect(setWindowButtonVisibility).not.toBeCalled();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
});
|
||||
|
||||
describe('applicationMenu', () => {
|
||||
// test some basic IPC events
|
||||
test('applicationMenu event', async () => {
|
||||
const { applicationMenuSubjects } = await import('../application-menu');
|
||||
const sendStub = vi.fn();
|
||||
browserWindow.webContents.send = sendStub;
|
||||
applicationMenuSubjects.newPageAction.next();
|
||||
expect(sendStub).toHaveBeenCalledWith(
|
||||
'applicationMenu:onNewPageAction',
|
||||
undefined
|
||||
);
|
||||
browserWindow.webContents.send = () => {};
|
||||
});
|
||||
});
|
||||
@@ -7,11 +7,12 @@ export const ReleaseTypeSchema = z.enum([
|
||||
'internal',
|
||||
]);
|
||||
|
||||
export const envBuildType = (
|
||||
process.env.BUILD_TYPE_OVERRIDE ||
|
||||
process.env.BUILD_TYPE ||
|
||||
'canary'
|
||||
)
|
||||
declare global {
|
||||
// THIS variable should be replaced during the build process
|
||||
const REPLACE_ME_BUILD_ENV: string;
|
||||
}
|
||||
|
||||
export const envBuildType = (process.env.BUILD_TYPE || REPLACE_ME_BUILD_ENV)
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { shell } from 'electron';
|
||||
import log from 'electron-log';
|
||||
|
||||
export const logger = log.scope('main');
|
||||
export const pluginLogger = log.scope('plugin');
|
||||
log.initialize();
|
||||
|
||||
export function getLogFilePath() {
|
||||
|
||||
@@ -9,7 +9,6 @@ import { contextBridge, ipcRenderer } from 'electron';
|
||||
contextBridge.exposeInMainWorld('appInfo', appInfo);
|
||||
contextBridge.exposeInMainWorld('apis', apis);
|
||||
contextBridge.exposeInMainWorld('events', events);
|
||||
contextBridge.exposeInMainWorld('platform', process.platform);
|
||||
|
||||
// Credit to microsoft/vscode
|
||||
const globals = {
|
||||
|
||||
+17
-7
@@ -8,7 +8,7 @@ import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
||||
const tmpDir = path.join(__dirname, 'tmp');
|
||||
const appDataPath = path.join(tmpDir, 'app-data');
|
||||
|
||||
vi.doMock('../../main-rpc', () => ({
|
||||
vi.doMock('@affine/electron/helper/main-rpc', () => ({
|
||||
mainRPC: {
|
||||
getPath: async () => appDataPath,
|
||||
},
|
||||
@@ -22,7 +22,7 @@ function existProcess() {
|
||||
process.emit('beforeExit', 0);
|
||||
}
|
||||
|
||||
vi.doMock('../secondary-db', () => {
|
||||
vi.doMock('@affine/electron/helper/db/secondary-db', () => {
|
||||
return {
|
||||
SecondaryWorkspaceSQLiteDB: class {
|
||||
constructor(...args: any[]) {
|
||||
@@ -49,7 +49,9 @@ afterEach(async () => {
|
||||
});
|
||||
|
||||
test('can get a valid WorkspaceSQLiteDB', async () => {
|
||||
const { ensureSQLiteDB } = await import('../ensure-db');
|
||||
const { ensureSQLiteDB } = await import(
|
||||
'@affine/electron/helper/db/ensure-db'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const db0 = await ensureSQLiteDB(workspaceId);
|
||||
expect(db0).toBeDefined();
|
||||
@@ -64,7 +66,9 @@ test('can get a valid WorkspaceSQLiteDB', async () => {
|
||||
});
|
||||
|
||||
test('db should be destroyed when app quits', async () => {
|
||||
const { ensureSQLiteDB } = await import('../ensure-db');
|
||||
const { ensureSQLiteDB } = await import(
|
||||
'@affine/electron/helper/db/ensure-db'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const db0 = await ensureSQLiteDB(workspaceId);
|
||||
const db1 = await ensureSQLiteDB(v4());
|
||||
@@ -82,7 +86,9 @@ test('db should be destroyed when app quits', async () => {
|
||||
});
|
||||
|
||||
test('db should be removed in db$Map after destroyed', async () => {
|
||||
const { ensureSQLiteDB, db$Map } = await import('../ensure-db');
|
||||
const { ensureSQLiteDB, db$Map } = await import(
|
||||
'@affine/electron/helper/db/ensure-db'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const db = await ensureSQLiteDB(workspaceId);
|
||||
await db.destroy();
|
||||
@@ -92,8 +98,12 @@ test('db should be removed in db$Map after destroyed', async () => {
|
||||
|
||||
// we have removed secondary db feature
|
||||
test.skip('if db has a secondary db path, we should also poll that', async () => {
|
||||
const { ensureSQLiteDB } = await import('../ensure-db');
|
||||
const { storeWorkspaceMeta } = await import('../../workspace');
|
||||
const { ensureSQLiteDB } = await import(
|
||||
'@affine/electron/helper/db/ensure-db'
|
||||
);
|
||||
const { storeWorkspaceMeta } = await import(
|
||||
'@affine/electron/helper/workspace'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
await storeWorkspaceMeta(workspaceId, {
|
||||
secondaryDBPath: path.join(tmpDir, 'secondary.db'),
|
||||
+5
-3
@@ -1,18 +1,20 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import {
|
||||
copyToTemp,
|
||||
migrateToSubdocAndReplaceDatabase,
|
||||
} from '@affine/electron/helper/db/migration';
|
||||
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 { copyToTemp, migrateToSubdocAndReplaceDatabase } from '../migration';
|
||||
|
||||
const tmpDir = path.join(__dirname, 'tmp');
|
||||
const testDBFilePath = path.resolve(__dirname, 'old-db.affine');
|
||||
|
||||
const appDataPath = path.join(tmpDir, 'app-data');
|
||||
|
||||
vi.mock('../../main-rpc', () => ({
|
||||
vi.mock('@affine/electron/helper/main-rpc', () => ({
|
||||
mainRPC: {
|
||||
getPath: async () => appDataPath,
|
||||
},
|
||||
+20
-9
@@ -1,17 +1,16 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import { dbSubjects } from '@affine/electron/helper/db/subjects';
|
||||
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 { dbSubjects } from '../subjects';
|
||||
|
||||
const tmpDir = path.join(__dirname, 'tmp');
|
||||
const appDataPath = path.join(tmpDir, 'app-data');
|
||||
|
||||
vi.doMock('../../main-rpc', () => ({
|
||||
vi.doMock('@affine/electron/helper/main-rpc', () => ({
|
||||
mainRPC: {
|
||||
getPath: async () => appDataPath,
|
||||
},
|
||||
@@ -47,7 +46,9 @@ function getTestSubDocUpdates() {
|
||||
}
|
||||
|
||||
test('can create new db file if not exists', async () => {
|
||||
const { openWorkspaceDatabase } = await import('../workspace-db-adapter');
|
||||
const { openWorkspaceDatabase } = await import(
|
||||
'@affine/electron/helper/db/workspace-db-adapter'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const db = await openWorkspaceDatabase(workspaceId);
|
||||
const dbPath = path.join(
|
||||
@@ -60,7 +61,9 @@ test('can create new db file if not exists', async () => {
|
||||
});
|
||||
|
||||
test('on applyUpdate (from self), will not trigger update', async () => {
|
||||
const { openWorkspaceDatabase } = await import('../workspace-db-adapter');
|
||||
const { openWorkspaceDatabase } = await import(
|
||||
'@affine/electron/helper/db/workspace-db-adapter'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const onUpdate = vi.fn();
|
||||
|
||||
@@ -72,7 +75,9 @@ test('on applyUpdate (from self), will not trigger update', async () => {
|
||||
});
|
||||
|
||||
test('on applyUpdate (from renderer), will trigger update', async () => {
|
||||
const { openWorkspaceDatabase } = await import('../workspace-db-adapter');
|
||||
const { openWorkspaceDatabase } = await import(
|
||||
'@affine/electron/helper/db/workspace-db-adapter'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const onUpdate = vi.fn();
|
||||
const onExternalUpdate = vi.fn();
|
||||
@@ -87,7 +92,9 @@ test('on applyUpdate (from renderer), will trigger update', async () => {
|
||||
});
|
||||
|
||||
test('on applyUpdate (from renderer, subdoc), will trigger update', async () => {
|
||||
const { openWorkspaceDatabase } = await import('../workspace-db-adapter');
|
||||
const { openWorkspaceDatabase } = await import(
|
||||
'@affine/electron/helper/db/workspace-db-adapter'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const onUpdate = vi.fn();
|
||||
const insertUpdates = vi.fn();
|
||||
@@ -112,7 +119,9 @@ test('on applyUpdate (from renderer, subdoc), will trigger update', async () =>
|
||||
});
|
||||
|
||||
test('on applyUpdate (from external), will trigger update & send external update event', async () => {
|
||||
const { openWorkspaceDatabase } = await import('../workspace-db-adapter');
|
||||
const { openWorkspaceDatabase } = await import(
|
||||
'@affine/electron/helper/db/workspace-db-adapter'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const onUpdate = vi.fn();
|
||||
const onExternalUpdate = vi.fn();
|
||||
@@ -128,7 +137,9 @@ test('on applyUpdate (from external), will trigger update & send external update
|
||||
});
|
||||
|
||||
test('on destroy, check if resources have been released', async () => {
|
||||
const { openWorkspaceDatabase } = await import('../workspace-db-adapter');
|
||||
const { openWorkspaceDatabase } = await import(
|
||||
'@affine/electron/helper/db/workspace-db-adapter'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const db = await openWorkspaceDatabase(workspaceId);
|
||||
const updateSub = {
|
||||
+23
-9
@@ -8,13 +8,13 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
|
||||
const tmpDir = path.join(__dirname, 'tmp');
|
||||
const appDataPath = path.join(tmpDir, 'app-data');
|
||||
|
||||
vi.doMock('../../db/ensure-db', () => ({
|
||||
vi.doMock('@affine/electron/helper/db/ensure-db', () => ({
|
||||
ensureSQLiteDB: async () => ({
|
||||
destroy: () => {},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.doMock('../../main-rpc', () => ({
|
||||
vi.doMock('@affine/electron/helper/main-rpc', () => ({
|
||||
mainRPC: {
|
||||
getPath: async () => appDataPath,
|
||||
},
|
||||
@@ -26,7 +26,9 @@ afterEach(async () => {
|
||||
|
||||
describe('list workspaces', () => {
|
||||
test('listWorkspaces (valid)', async () => {
|
||||
const { listWorkspaces } = await import('../handlers');
|
||||
const { listWorkspaces } = await import(
|
||||
'@affine/electron/helper/workspace/handlers'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
|
||||
const meta = {
|
||||
@@ -39,7 +41,9 @@ describe('list workspaces', () => {
|
||||
});
|
||||
|
||||
test('listWorkspaces (without meta json file)', async () => {
|
||||
const { listWorkspaces } = await import('../handlers');
|
||||
const { listWorkspaces } = await import(
|
||||
'@affine/electron/helper/workspace/handlers'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
|
||||
await fs.ensureDir(workspacePath);
|
||||
@@ -56,7 +60,9 @@ describe('list workspaces', () => {
|
||||
|
||||
describe('delete workspace', () => {
|
||||
test('deleteWorkspace', async () => {
|
||||
const { deleteWorkspace } = await import('../handlers');
|
||||
const { deleteWorkspace } = await import(
|
||||
'@affine/electron/helper/workspace/handlers'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
|
||||
await fs.ensureDir(workspacePath);
|
||||
@@ -73,7 +79,9 @@ describe('delete workspace', () => {
|
||||
|
||||
describe('getWorkspaceMeta', () => {
|
||||
test('can get meta', async () => {
|
||||
const { getWorkspaceMeta } = await import('../meta');
|
||||
const { getWorkspaceMeta } = await import(
|
||||
'@affine/electron/helper/workspace/meta'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
|
||||
const meta = {
|
||||
@@ -85,7 +93,9 @@ describe('getWorkspaceMeta', () => {
|
||||
});
|
||||
|
||||
test('can create meta if not exists', async () => {
|
||||
const { getWorkspaceMeta } = await import('../meta');
|
||||
const { getWorkspaceMeta } = await import(
|
||||
'@affine/electron/helper/workspace/meta'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
|
||||
await fs.ensureDir(workspacePath);
|
||||
@@ -99,7 +109,9 @@ describe('getWorkspaceMeta', () => {
|
||||
});
|
||||
|
||||
test('can migrate meta if db file is a link', async () => {
|
||||
const { getWorkspaceMeta } = await import('../meta');
|
||||
const { getWorkspaceMeta } = await import(
|
||||
'@affine/electron/helper/workspace/meta'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
|
||||
await fs.ensureDir(workspacePath);
|
||||
@@ -121,7 +133,9 @@ describe('getWorkspaceMeta', () => {
|
||||
});
|
||||
|
||||
test('storeWorkspaceMeta', async () => {
|
||||
const { storeWorkspaceMeta } = await import('../handlers');
|
||||
const { storeWorkspaceMeta } = await import(
|
||||
'@affine/electron/helper/workspace/handlers'
|
||||
);
|
||||
const workspaceId = v4();
|
||||
const workspacePath = path.join(appDataPath, 'workspaces', workspaceId);
|
||||
await fs.ensureDir(workspacePath);
|
||||
@@ -28,12 +28,12 @@
|
||||
{
|
||||
"path": "../../common/env"
|
||||
},
|
||||
|
||||
// Tests
|
||||
{
|
||||
"path": "./tsconfig.node.json"
|
||||
},
|
||||
{ "path": "../../../tests/kit" }
|
||||
{
|
||||
"path": "../../../tests/kit"
|
||||
}
|
||||
],
|
||||
"ts-node": {
|
||||
"esm": true,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "Node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"noEmit": false,
|
||||
"outDir": "./lib/tests",
|
||||
"types": ["node"],
|
||||
"allowJs": true
|
||||
},
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.json"
|
||||
}
|
||||
],
|
||||
"include": ["./test"]
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"composite": true
|
||||
},
|
||||
"include": ["**/__tests__/**/*", "./tests"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
/**
|
||||
* Describes all existing environment variables and their types.
|
||||
* Required for Code completion and type checking
|
||||
*
|
||||
* Note: To prevent accidentally leaking env variables to the client, only variables prefixed with `VITE_` are exposed to your Vite-processed code
|
||||
*
|
||||
* @see https://github.com/vitejs/vite/blob/cab55b32de62e0de7d7789e8c2a1f04a8eae3a3f/packages/vite/types/importMeta.d.ts#L62-L69 Base Interface
|
||||
* @see https://vitejs.dev/guide/env-and-mode.html#env-files Vite Env Variables Doc
|
||||
*/
|
||||
interface ImportMetaEnv {
|
||||
/**
|
||||
* The value of the variable is set in scripts/watch.js and depend on layers/main/vite.config.js
|
||||
*/
|
||||
readonly DEV_SERVER_URL: undefined | string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv;
|
||||
}
|
||||
@@ -4,24 +4,17 @@ import { fileURLToPath } from 'node:url';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
const rootDir = fileURLToPath(new URL('../../..', import.meta.url));
|
||||
const pluginOutputDir = resolve(
|
||||
rootDir,
|
||||
'./packages/frontend/electron/dist/plugins'
|
||||
);
|
||||
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
// prevent tests using two different sources of yjs
|
||||
yjs: resolve(rootDir, 'node_modules/yjs'),
|
||||
'@affine/electron': resolve(rootDir, 'packages/frontend/electron/src'),
|
||||
},
|
||||
},
|
||||
define: {
|
||||
'process.env.PLUGIN_DIR': JSON.stringify(pluginOutputDir),
|
||||
},
|
||||
test: {
|
||||
include: ['./src/**/*.spec.ts'],
|
||||
exclude: ['**/node_modules', '**/dist', '**/build', '**/out'],
|
||||
include: ['./test/**/*.spec.ts'],
|
||||
testTimeout: 5000,
|
||||
singleThread: true,
|
||||
threads: false,
|
||||
|
||||
Reference in New Issue
Block a user