diff --git a/packages/core/client/src/application/__tests__/Application.test.tsx b/packages/core/client/src/application/__tests__/Application.test.tsx
index 83a95768ae..2d39cb0047 100644
--- a/packages/core/client/src/application/__tests__/Application.test.tsx
+++ b/packages/core/client/src/application/__tests__/Application.test.tsx
@@ -242,8 +242,7 @@ describe('Application', () => {
expect(screen.getByText('AboutComponent')).toBeInTheDocument();
});
- // TODO: 会一直 loading,暂时不知道怎么解决,先跳过
- it.skip('mount', async () => {
+ it('mount', async () => {
const Hello = () =>
Hello
;
const app = new Application({
router,
diff --git a/packages/core/client/src/application/__tests__/Plugin.test.ts b/packages/core/client/src/application/__tests__/Plugin.test.ts
index 693bf76fb4..836a4ff78c 100644
--- a/packages/core/client/src/application/__tests__/Plugin.test.ts
+++ b/packages/core/client/src/application/__tests__/Plugin.test.ts
@@ -4,13 +4,6 @@ import { Application } from '../Application';
import { Plugin } from '../Plugin';
describe('Plugin', () => {
- beforeAll(() => {
- const mock = new MockAdapter(axios);
- mock.onGet('pm:listEnabled').reply(200, {
- data: [],
- });
- });
-
it('lifecycle', async () => {
const afterAdd = vitest.fn();
const beforeLoad = vitest.fn();
@@ -66,6 +59,63 @@ describe('PluginManager', () => {
expect(fn2).toBeCalledWith(config);
});
+ it('remote plugins', async () => {
+ const mock = new MockAdapter(axios);
+ mock.onGet('pm:listEnabled').reply(200, {
+ data: [
+ {
+ name: '@nocobase/demo',
+ packageName: '@nocobase/demo',
+ url: 'https://demo1.com',
+ },
+ {
+ name: '@nocobase/demo2',
+ packageName: '@nocobase/demo2',
+ url: 'https://demo2.com',
+ },
+ ],
+ });
+
+ // mock requirejs
+ const remoteFn = vi.fn();
+
+ const demo1Mock = vi.fn();
+ const demo2Mock = vi.fn();
+ class Demo1Plugin extends Plugin {
+ async load() {
+ demo1Mock();
+ }
+ }
+
+ class Demo2Plugin extends Plugin {
+ async load() {
+ demo2Mock();
+ }
+ }
+
+ const mockPluginsModules = (pluginData, resolve) => {
+ remoteFn();
+ resolve({ default: Demo1Plugin }, { default: Demo2Plugin });
+ };
+
+ const requirejs: any = {
+ requirejs: mockPluginsModules,
+ };
+
+ requirejs.requirejs.config = vi.fn();
+ requirejs.requirejs.requirejs = vi.fn();
+
+ const app = new Application({
+ loadRemotePlugins: true,
+ });
+ app.requirejs = requirejs;
+
+ await app.load();
+
+ expect(remoteFn).toBeCalledTimes(1);
+ expect(demo1Mock).toBeCalledTimes(1);
+ });
+
it('Load other plugins through plugins', async () => {
const fn2 = vitest.fn();
const config = { a: 1 };
diff --git a/packages/core/client/src/application/__tests__/SchemaToolbar.test.tsx b/packages/core/client/src/application/__tests__/SchemaToolbar.test.tsx
new file mode 100644
index 0000000000..ce51b2eab1
--- /dev/null
+++ b/packages/core/client/src/application/__tests__/SchemaToolbar.test.tsx
@@ -0,0 +1,147 @@
+import React from 'react';
+import { fireEvent, render, screen, waitFor } from '@nocobase/test/client';
+import { SchemaToolbarProvider, useSchemaToolbar, useSchemaToolbarRender } from '../schema-toolbar';
+import { SchemaComponent, SchemaComponentProvider, SortableContext, SortableProvider } from '../../schema-component';
+import { useFieldSchema } from '@formily/react';
+import { Application, ApplicationContext } from '@nocobase/client';
+
+describe('SchemaToolbar', () => {
+ test('SchemaToolbarProvider & useSchemaToolbar', () => {
+ const Demo = () => {
+ const context = useSchemaToolbar();
+ return {context.test}
;
+ };
+
+ const Root = () => {
+ return (
+
+
+
+ );
+ };
+
+ render();
+
+ expect(screen.getByTestId('content')).toHaveTextContent('123');
+ });
+
+ describe('useSchemaToolbarRender()', () => {
+ const renderApp = (demoSchema: any, designable = true) => {
+ const Demo = () => Demo
;
+
+ const CustomToolbar = (props) => {
+ return (
+ <>
+ CustomToolbar
+ {JSON.stringify(props)}
+ >
+ );
+ };
+
+ const schema = {
+ name: 'root',
+ type: 'object',
+ 'x-decorator': 'Wrapper',
+ 'x-component': 'Demo',
+ ...demoSchema,
+ };
+
+ const Wrapper = ({ children }) => {
+ const schema = useFieldSchema();
+ const context = useSchemaToolbarRender(schema);
+ return (
+ <>
+ {context.render({ customProps: '123' })}
+ {JSON.stringify(context.exists)}
+ {children}
+ >
+ );
+ };
+
+ const app = new Application({});
+
+ render(
+
+
+
+
+
+
+ ,
+ );
+ };
+
+ test('Render x-designer if x-designer has a value', () => {
+ renderApp({
+ 'x-designer': 'CustomToolbar',
+ });
+
+ expect(screen.getByTestId('custom-toolbar')).toHaveTextContent('CustomToolbar');
+ expect(screen.getByTestId('custom-toolbar-props')).toHaveTextContent('{"customProps":"123"}');
+ expect(screen.getByTestId('toolbar-exists')).toHaveTextContent('true');
+ });
+
+ test('Render x-toolbar if it has a value', () => {
+ renderApp({
+ 'x-toolbar': 'CustomToolbar',
+ });
+
+ expect(screen.getByTestId('custom-toolbar')).toHaveTextContent('CustomToolbar');
+ });
+
+ test('Render x-toolbar if both x-toolbar and x-designer have values', () => {
+ renderApp({
+ 'x-toolbar': 'CustomToolbar',
+ 'x-designer': 'CustomToolbar',
+ });
+
+ expect(screen.getByTestId('custom-toolbar')).toHaveTextContent('CustomToolbar');
+ });
+
+ test('Render the default SchemaToolbar component if x-toolbar and x-designer have no values and x-settings has a value', () => {
+ renderApp({
+ 'x-settings': 'DemoSettings',
+ });
+
+ expect(screen.getByTestId('toolbar').innerHTML.length > 0).toBe(true);
+ expect(screen.getByTestId('toolbar-exists')).toHaveTextContent('true');
+ });
+
+ test('Do not render if x-toolbar and x-designer have no values and x-settings also has no value', () => {
+ renderApp({});
+
+ expect(screen.getByTestId('toolbar')).toHaveTextContent('');
+ expect(screen.getByTestId('toolbar-exists')).toHaveTextContent('false');
+ });
+
+ test('Do not render if the component corresponding to x-toolbar cannot be found', () => {
+ renderApp({
+ 'x-toolbar': 'NotFound',
+ });
+
+ expect(screen.getByTestId('toolbar')).toHaveTextContent('');
+ });
+
+ test('Do not render if designable is false', () => {
+ renderApp(
+ {
+ 'x-designer': 'CustomToolbar',
+ },
+ false,
+ );
+
+ expect(screen.getByTestId('toolbar')).toHaveTextContent('');
+ });
+
+ test('x-toolbar-props and custom Props', () => {
+ renderApp({
+ 'x-toolbar': 'CustomToolbar',
+ 'x-toolbar-props': {
+ test: '123',
+ },
+ });
+
+ expect(screen.getByTestId('custom-toolbar-props')).toHaveTextContent('{"test":"123","customProps":"123"}');
+ });
+ });
+});
diff --git a/packages/core/client/src/application/schema-toolbar/hooks/index.tsx b/packages/core/client/src/application/schema-toolbar/hooks/index.tsx
index 5ee00713c2..cbe159329d 100644
--- a/packages/core/client/src/application/schema-toolbar/hooks/index.tsx
+++ b/packages/core/client/src/application/schema-toolbar/hooks/index.tsx
@@ -6,8 +6,8 @@ import { SchemaToolbar, SchemaToolbarProps } from '../../../schema-settings';
export const useSchemaToolbarRender = (fieldSchema: ISchema) => {
const { designable } = useDesignable();
const toolbar = useMemo(() => {
- if (fieldSchema['x-designer'] || fieldSchema['x-toolbar']) {
- return fieldSchema['x-toolbar'];
+ if (fieldSchema['x-toolbar'] || fieldSchema['x-designer']) {
+ return fieldSchema['x-toolbar'] || fieldSchema['x-designer'];
}
if (fieldSchema['x-settings']) {
@@ -17,7 +17,7 @@ export const useSchemaToolbarRender = (fieldSchema: ISchema) => {
const C = useComponent(toolbar);
return {
- render(props?: SchemaToolbarProps) {
+ render(props?: SchemaToolbarProps & { [index: string]: any }) {
if (!designable || !C) {
return null;
}