diff --git a/packages/core/client-v2/src/acl/ACLProvider.tsx b/packages/core/client-v2/src/acl/ACLProvider.tsx index 2c6f9d10202..c5ebf5e2d49 100644 --- a/packages/core/client-v2/src/acl/ACLProvider.tsx +++ b/packages/core/client-v2/src/acl/ACLProvider.tsx @@ -11,6 +11,7 @@ import { observable } from '@formily/reactive'; import React, { useRef } from 'react'; import { createContext, useCallback, useContext, useEffect, useMemo, useState, type FC } from 'react'; import { useLocation } from 'react-router-dom'; +import { writeFlowSettingsPreference } from '../flow/admin-shell/admin-layout/flowSettingsPreference'; import { useApp } from '../hooks/useApp'; import { getACLCheckReady, setACLCheckReady } from './aclCheckReadiness'; import { createAclSnippetAllow } from './createAclSnippetAllow'; @@ -133,6 +134,7 @@ export const ACLRolesCheckProvider: FC = ({ children }) => { setACLCheckReady(app, true); if (!createAclSnippetAllow(nextData?.snippets || [], !!nextData?.allowAll)('ui.*')) { + writeFlowSettingsPreference(false); await app.flowEngine.flowSettings.disable(); } } catch (error) { diff --git a/packages/core/client-v2/src/acl/__tests__/ACLProvider.test.tsx b/packages/core/client-v2/src/acl/__tests__/ACLProvider.test.tsx new file mode 100644 index 00000000000..1f42c8a9ea6 --- /dev/null +++ b/packages/core/client-v2/src/acl/__tests__/ACLProvider.test.tsx @@ -0,0 +1,92 @@ +/** + * 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 { render, screen, waitFor } from '@testing-library/react'; +import React from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { + FLOW_SETTINGS_PREFERENCE_STORAGE_KEY, + writeFlowSettingsPreference, +} from '../../flow/admin-shell/admin-layout/flowSettingsPreference'; +import { ACLRolesCheckProvider } from '../ACLProvider'; + +const mocks = vi.hoisted(() => ({ + app: undefined as unknown, +})); + +vi.mock('../../hooks/useApp', () => ({ + useApp: () => mocks.app, +})); + +describe('ACLRolesCheckProvider', () => { + beforeEach(() => { + window.localStorage.removeItem(FLOW_SETTINGS_PREFERENCE_STORAGE_KEY); + }); + + it('exits UI editing mode when the current role cannot configure pages', async () => { + const disable = vi.fn(async () => undefined); + const setRole = vi.fn(); + const aclContext: Record = {}; + const appContext = { + acl: undefined, + defineProperty(name: string, descriptor: PropertyDescriptor) { + Object.defineProperty(this, name, descriptor); + }, + ...aclContext, + }; + + mocks.app = { + apiClient: { + auth: { + role: 'member', + setRole, + }, + request: vi.fn(async () => ({ + data: { + data: { + role: 'viewer', + snippets: [], + }, + meta: {}, + }, + })), + }, + context: appContext, + flowEngine: { + flowSettings: { + disable, + }, + }, + pluginSettingsManager: { + setAclSnippets: vi.fn(), + }, + renderComponent: () =>
Loading
, + router: { + isSkippedAuthCheckRoute: () => false, + }, + }; + writeFlowSettingsPreference(true); + + render( + + +
Page content
+
+
, + ); + + expect(await screen.findByText('Page content')).toBeInTheDocument(); + await waitFor(() => { + expect(disable).toHaveBeenCalledTimes(1); + }); + expect(window.localStorage.getItem(FLOW_SETTINGS_PREFERENCE_STORAGE_KEY)).toBe('0'); + expect(setRole).toHaveBeenCalledWith('viewer'); + }); +});