diff --git a/packages/plugins/@nocobase/plugin-mobile/src/server/__test__/roleMobileRoutes.test.ts b/packages/plugins/@nocobase/plugin-mobile/src/server/__test__/roleMobileRoutes.test.ts new file mode 100644 index 0000000000..08a5642d4c --- /dev/null +++ b/packages/plugins/@nocobase/plugin-mobile/src/server/__test__/roleMobileRoutes.test.ts @@ -0,0 +1,203 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { createMockServer, MockServer } from '@nocobase/test'; + +describe(`roleMobileRoutes`, async () => { + let app: MockServer, db; + let agent, rootUser, user, role; + + beforeEach(async () => { + app = await createMockServer({ + plugins: [ + 'acl', + 'mobile', + 'users', + 'ui-schema-storage', + 'system-settings', + 'field-sort', + 'data-source-main', + 'auth', + 'data-source-manager', + 'error-handler', + 'collection-tree', + ], + }); + db = app.db; + rootUser = await db.getRepository('users').findOne({ + filter: { + email: process.env.INIT_ROOT_EMAIL, + }, + }); + const rootAgent = await app.agent().login(rootUser); + const role1Response = await rootAgent.resource('roles').create({ + values: { + name: 'r1', + }, + }); + role = role1Response.body.data; + user = await db.getRepository('users').create({ + values: { + name: 'u1', + roles: [role.name], + }, + }); + }); + + afterEach(async () => { + await app.destroy(); + }); + + const generateRandomString = () => { + return Math.random().toString(36).substring(2, 15); + }; + + const createUiMenu = async (loginAgent, data?: { title?: string }) => { + const response = await loginAgent.resource(`mobileRoutes`).create({ + values: { + type: 'page', + title: data?.title || generateRandomString(), + schemaUid: generateRandomString(), + menuSchemaUid: generateRandomString(), + enableTabs: false, + children: [ + { + type: 'tabs', + schemaUid: generateRandomString(), + tabSchemaName: generateRandomString(), + hidden: true, + }, + ], + }, + }); + expect(response.statusCode).toBe(200); + expect(response.body.data).exist; + if (data?.title) { + expect(response.body.data.title).toBe(data.title); + } + const menu = response.body.data; + const uiSchemaResponse = await loginAgent + .post(`/uiSchemas:insertAdjacent/nocobase-admin-menu`) + .query({ position: 'beforeEnd' }) + .send({ + schema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: menu.title, + 'x-component': 'Menu.Item', + 'x-decorator': 'ACLMenuItemProvider', + 'x-component-props': {}, + properties: { + page: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + 'x-async': true, + properties: { + [menu.children[0].tabSchemaName]: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + 'x-uid': menu.children[0].schemaUid, + name: menu.children[0].tabSchemaName, + 'x-app-version': '1.6.0-alpha.28', + }, + }, + 'x-uid': menu.schemaUid, + name: 'page', + 'x-app-version': '1.6.0-alpha.28', + }, + }, + 'x-uid': menu.menuSchemaUid, + __route__: { + createdAt: '2025-02-27T03:34:19.689Z', + updatedAt: '2025-02-27T03:34:19.689Z', + id: menu.id, + type: menu.type, + title: menu.title, + schemaUid: menu.schemaUid, + menuSchemaUid: menu.menuSchemaUid, + enableTabs: false, + sort: menu.sort, + createdById: menu.createdById, + updatedById: menu.updatedById, + parentId: null, + icon: null, + tabSchemaName: null, + options: null, + hideInMenu: null, + enableHeader: null, + displayTitle: null, + hidden: null, + children: [ + { + createdAt: '2025-02-27T03:34:19.746Z', + updatedAt: '2025-02-27T03:34:19.746Z', + id: menu.children[0].id, + type: 'tabs', + schemaUid: menu.children[0].schemaUid, + tabSchemaName: menu.children[0].tabSchemaName, + hidden: true, + parentId: menu.children[0].parentId, + sort: menu.children[0].sort, + createdById: menu.children[0].createdById, + updatedById: menu.children[0].updatedById, + title: null, + icon: null, + menuSchemaUid: null, + options: null, + hideInMenu: null, + enableTabs: null, + enableHeader: null, + displayTitle: null, + }, + ], + }, + name: generateRandomString(), + 'x-app-version': '1.6.0-alpha.28', + }, + wrap: null, + }); + expect(uiSchemaResponse.statusCode).toBe(200); + return response.body.data; + }; + + const getAccessibleMenus = async (loginAgent) => { + const menuResponse = await loginAgent + .get(`/mobileRoutes:listAccessible`) + .query({ tree: true, sort: 'sort', paginate: false }) + .send(); + expect(menuResponse.statusCode).toBe(200); + return menuResponse.body.data; + }; + + it(`should rolesMobileRoutes tab data valid when menu only one tab and hidden=true`, async () => { + let rootAgent = await app.agent().login(rootUser); + const page = await createUiMenu(rootAgent, { title: 'page' }); + + let response = await rootAgent.post(`/roles/${role.name}/mobileRoutes:add`).send([page.id]); + expect(response.statusCode).toBe(200); + agent = await app.agent().login(user, role.name); + let accessibleMenus = await getAccessibleMenus(agent); + expect(accessibleMenus.length).toBe(1); + expect(accessibleMenus[0].children[0]).exist; + expect(accessibleMenus[0].children[0].id).toEqual(page.children[0].id); + + rootAgent = await app.agent().login(rootUser); + response = await rootAgent.post(`/roles/${role.name}/mobileRoutes:remove`).send([page.id]); + + agent = await app.agent().login(user, role.name); + accessibleMenus = await getAccessibleMenus(agent); + expect(accessibleMenus.length).toBe(0); + }); +}); diff --git a/packages/plugins/@nocobase/plugin-mobile/src/server/plugin.ts b/packages/plugins/@nocobase/plugin-mobile/src/server/plugin.ts index 514314ac90..4dfa0b53ff 100644 --- a/packages/plugins/@nocobase/plugin-mobile/src/server/plugin.ts +++ b/packages/plugins/@nocobase/plugin-mobile/src/server/plugin.ts @@ -11,6 +11,7 @@ import { Model } from '@nocobase/database'; import { Plugin } from '@nocobase/server'; import PluginLocalizationServer from '@nocobase/plugin-localization'; import { tval } from '@nocobase/utils'; +import actions, { Context } from '@nocobase/actions'; export class PluginMobileServer extends Plugin { async load() { @@ -102,6 +103,36 @@ export class PluginMobileServer extends Plugin { await next(); }); + const processRoleMobileRoutes = async (ctx: Context, next) => { + const actionName = ctx.action.actionName; + await actions[actionName](ctx, next); + + const tabs = await this.app.db.getRepository('mobileRoutes').find({ + where: { + parentId: ctx.action.params.values, + }, + }); + if (tabs.length > 1 || !tabs[0].hidden) { + return; + } + const repository = this.app.db.getRepository('rolesMobileRoutes'); + const whereOrValues = { + roleName: ctx.action.params.associatedIndex, + mobileRouteId: tabs[0].id, + }; + const rolesMobileRoute = await repository.findOne({ where: whereOrValues }); + + if (actionName === 'add') { + !rolesMobileRoute && (await repository.create({ values: whereOrValues })); + } + if (actionName === 'remove') { + rolesMobileRoute && (await repository.destroy({ filter: whereOrValues })); + } + }; + this.app.resourceManager.registerActionHandlers({ + 'roles.mobileRoutes:add': processRoleMobileRoutes, + 'roles.mobileRoutes:remove': processRoleMobileRoutes, + }); } registerLocalizationSource() {