Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
230 lines
5.8 KiB
TypeScript
230 lines
5.8 KiB
TypeScript
import { Container } from '@blocksuite/global/di';
|
|
import { type ExtensionType } from '@blocksuite/store';
|
|
import { describe, expect, it, vi } from 'vitest';
|
|
import { z } from 'zod';
|
|
|
|
import { ExtensionManager } from '../manager';
|
|
import {
|
|
StoreExtensionManager,
|
|
StoreExtensionManagerIdentifier,
|
|
} from '../store-manager';
|
|
import {
|
|
type StoreExtensionContext,
|
|
StoreExtensionProvider,
|
|
} from '../store-provider';
|
|
import { ViewExtensionManager } from '../view-manager';
|
|
import {
|
|
type ViewExtensionContext,
|
|
ViewExtensionProvider,
|
|
} from '../view-provider';
|
|
|
|
export const Ext1: ExtensionType = {
|
|
setup: () => {},
|
|
};
|
|
export const Ext2: ExtensionType = {
|
|
setup: () => {},
|
|
};
|
|
export const Ext3: ExtensionType = {
|
|
setup: () => {},
|
|
};
|
|
export const Ext4: ExtensionType = {
|
|
setup: () => {},
|
|
};
|
|
export const Ext5: ExtensionType = {
|
|
setup: () => {},
|
|
};
|
|
|
|
it('should be able to load extensions', () => {
|
|
class StoreExt1 extends StoreExtensionProvider {
|
|
override name = 'StoreExt1';
|
|
|
|
override setup(context: StoreExtensionContext) {
|
|
super.setup(context);
|
|
context.register(Ext1);
|
|
}
|
|
}
|
|
const manager = new ExtensionManager([StoreExt1]);
|
|
const storeExtensions = manager.get('store');
|
|
expect(storeExtensions).toEqual([Ext1]);
|
|
});
|
|
|
|
describe('multiple scopes', () => {
|
|
const setup1 = vi.fn();
|
|
const setup2 = vi.fn();
|
|
class ViewExt1 extends ViewExtensionProvider {
|
|
override name = 'ViewExt1';
|
|
|
|
constructor() {
|
|
super();
|
|
setup1();
|
|
}
|
|
|
|
override setup(context: ViewExtensionContext, option?: { foo: number }) {
|
|
super.setup(context, option);
|
|
if (context.scope === 'page') {
|
|
context.register(Ext2);
|
|
}
|
|
if (context.scope === 'edgeless') {
|
|
context.register(Ext3);
|
|
}
|
|
}
|
|
}
|
|
class ViewExt2 extends ViewExtensionProvider {
|
|
override name = 'ViewExt2';
|
|
|
|
constructor() {
|
|
super();
|
|
setup2();
|
|
}
|
|
|
|
override setup(context: ViewExtensionContext) {
|
|
super.setup(context);
|
|
if (context.scope === 'page') {
|
|
context.register(Ext4);
|
|
}
|
|
if (context.scope === 'edgeless') {
|
|
context.register(Ext5);
|
|
}
|
|
}
|
|
}
|
|
const manager = new ExtensionManager([ViewExt1, ViewExt2]);
|
|
const pageExtensions = manager.get('page');
|
|
const edgelessExtensions = manager.get('edgeless');
|
|
it('should be able to load extensions from different scopes', () => {
|
|
expect(pageExtensions).toEqual([Ext2, Ext4]);
|
|
expect(edgelessExtensions).toEqual([Ext3, Ext5]);
|
|
});
|
|
|
|
it('should cache provider instances', () => {
|
|
manager.get('page');
|
|
manager.get('edgeless');
|
|
expect(setup1).toHaveBeenCalledTimes(1);
|
|
expect(setup2).toHaveBeenCalledTimes(1);
|
|
manager.get('page');
|
|
manager.get('edgeless');
|
|
expect(setup1).toHaveBeenCalledTimes(1);
|
|
expect(setup2).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
it('should be able to validate schema', () => {
|
|
type Option = { foo: number; bar: string };
|
|
const setupOption = vi.fn();
|
|
class ViewExt1 extends ViewExtensionProvider<Option> {
|
|
override name = 'ViewExt1';
|
|
|
|
override schema = z.object({
|
|
foo: z.number(),
|
|
bar: z.string(),
|
|
});
|
|
|
|
override setup(context: ViewExtensionContext, option?: Option) {
|
|
super.setup(context, option);
|
|
if (context.scope === 'page') {
|
|
setupOption(option);
|
|
context.register(Ext1);
|
|
}
|
|
if (context.scope === 'edgeless') {
|
|
setupOption(option);
|
|
context.register(Ext2);
|
|
}
|
|
}
|
|
}
|
|
const manager = new ExtensionManager([ViewExt1]);
|
|
|
|
manager.configure(ViewExt1, { foo: 1, bar: '2' });
|
|
manager.configure(ViewExt1, prev => {
|
|
if (!prev) {
|
|
return prev;
|
|
}
|
|
return {
|
|
...prev,
|
|
foo: prev.foo + 1,
|
|
};
|
|
});
|
|
let viewExtensions = manager.get('page');
|
|
expect(viewExtensions).toEqual([Ext1]);
|
|
expect(setupOption).toHaveBeenCalledWith({ foo: 2, bar: '2' });
|
|
|
|
setupOption.mockClear();
|
|
manager.configure(ViewExt1, undefined);
|
|
viewExtensions = manager.get('edgeless');
|
|
expect(viewExtensions).toEqual([Ext2]);
|
|
expect(setupOption).toHaveBeenCalledWith(undefined);
|
|
|
|
viewExtensions = manager.get('page');
|
|
expect(setupOption).toHaveBeenCalledWith(undefined);
|
|
});
|
|
|
|
it('should extension manager be able to be injected', () => {
|
|
class StoreExt1 extends StoreExtensionProvider {
|
|
override name = 'StoreExt1';
|
|
|
|
override setup(context: StoreExtensionContext) {
|
|
super.setup(context);
|
|
context.register(Ext1);
|
|
}
|
|
}
|
|
const manager = new StoreExtensionManager([StoreExt1]);
|
|
const extensions = manager.get('store');
|
|
const container = new Container();
|
|
extensions.forEach(ext => {
|
|
ext.setup(container);
|
|
});
|
|
const provider = container.provider();
|
|
expect(provider.get(StoreExtensionManagerIdentifier)).toBe(manager);
|
|
});
|
|
|
|
it('should effect only run once', () => {
|
|
const effect1 = vi.fn();
|
|
const effect2 = vi.fn();
|
|
class ViewExt1 extends ViewExtensionProvider {
|
|
override name = 'ViewExt1';
|
|
|
|
override effect() {
|
|
super.effect();
|
|
effect1();
|
|
}
|
|
|
|
override setup(context: ViewExtensionContext) {
|
|
super.setup(context);
|
|
context.register(Ext1);
|
|
}
|
|
}
|
|
|
|
class ViewExt2 extends ViewExtensionProvider {
|
|
override name = 'ViewExt2';
|
|
|
|
override effect() {
|
|
super.effect();
|
|
effect2();
|
|
}
|
|
|
|
override setup(context: ViewExtensionContext) {
|
|
super.setup(context);
|
|
context.register(Ext2);
|
|
}
|
|
}
|
|
|
|
const manager = new ViewExtensionManager([ViewExt1]);
|
|
|
|
expect(ViewExt1.effectRan).toBe(false);
|
|
expect(ViewExt2.effectRan).toBe(false);
|
|
|
|
manager.get('page');
|
|
|
|
expect(ViewExt1.effectRan).toBe(true);
|
|
expect(ViewExt2.effectRan).toBe(false);
|
|
|
|
expect(effect1).toHaveBeenCalledTimes(1);
|
|
expect(effect2).toHaveBeenCalledTimes(0);
|
|
|
|
manager.get('edgeless');
|
|
|
|
expect(ViewExt1.effectRan).toBe(true);
|
|
expect(ViewExt2.effectRan).toBe(false);
|
|
|
|
expect(effect1).toHaveBeenCalledTimes(1);
|
|
expect(effect2).toHaveBeenCalledTimes(0);
|
|
});
|