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; }