@@ -33,11 +33,41 @@ vi.mock('sonner', () => ({
3333 toast : { success : vi . fn ( ) , error : vi . fn ( ) } ,
3434} ) ) ;
3535
36+ const mockNavigate = vi . fn ( ) ;
37+ vi . mock ( 'react-router-dom' , async ( ) => {
38+ const actual = await vi . importActual ( 'react-router-dom' ) ;
39+ return {
40+ ...actual ,
41+ useNavigate : ( ) => mockNavigate ,
42+ useParams : ( ) => ( { appName : 'test-app' } ) ,
43+ } ;
44+ } ) ;
45+
46+ const mockRefresh = vi . fn ( ) . mockResolvedValue ( undefined ) ;
47+ vi . mock ( '../context/MetadataProvider' , ( ) => ( {
48+ useMetadata : ( ) => ( {
49+ apps : [
50+ { name : 'crm' , label : 'CRM' , description : 'Customer management' , active : true , isDefault : true } ,
51+ { name : 'hr' , label : 'HR' , description : 'Human resources' , active : false , isDefault : false } ,
52+ ] ,
53+ objects : [ ] ,
54+ dashboards : [ ] ,
55+ reports : [ ] ,
56+ pages : [ ] ,
57+ loading : false ,
58+ error : null ,
59+ refresh : mockRefresh ,
60+ } ) ,
61+ } ) ) ;
62+
3663// Import after mocks
3764import { UserManagementPage } from '../pages/system/UserManagementPage' ;
3865import { OrgManagementPage } from '../pages/system/OrgManagementPage' ;
3966import { RoleManagementPage } from '../pages/system/RoleManagementPage' ;
4067import { AuditLogPage } from '../pages/system/AuditLogPage' ;
68+ import { SystemHubPage } from '../pages/system/SystemHubPage' ;
69+ import { AppManagementPage } from '../pages/system/AppManagementPage' ;
70+ import { PermissionManagementPage } from '../pages/system/PermissionManagementPage' ;
4171
4272function wrap ( ui : React . ReactElement ) {
4373 return render ( < MemoryRouter > { ui } </ MemoryRouter > ) ;
@@ -143,3 +173,123 @@ describe('AuditLogPage', () => {
143173 } ) ;
144174 } ) ;
145175} ) ;
176+
177+ describe ( 'SystemHubPage' , ( ) => {
178+ it ( 'should render System Settings heading and all hub cards' , async ( ) => {
179+ mockFind . mockResolvedValue ( { data : [ ] } ) ;
180+ wrap ( < SystemHubPage /> ) ;
181+ expect ( screen . getByText ( 'System Settings' ) ) . toBeInTheDocument ( ) ;
182+ await waitFor ( ( ) => {
183+ expect ( screen . getByTestId ( 'hub-card-applications' ) ) . toBeInTheDocument ( ) ;
184+ expect ( screen . getByTestId ( 'hub-card-users' ) ) . toBeInTheDocument ( ) ;
185+ expect ( screen . getByTestId ( 'hub-card-organizations' ) ) . toBeInTheDocument ( ) ;
186+ expect ( screen . getByTestId ( 'hub-card-roles' ) ) . toBeInTheDocument ( ) ;
187+ expect ( screen . getByTestId ( 'hub-card-permissions' ) ) . toBeInTheDocument ( ) ;
188+ expect ( screen . getByTestId ( 'hub-card-audit-log' ) ) . toBeInTheDocument ( ) ;
189+ expect ( screen . getByTestId ( 'hub-card-profile' ) ) . toBeInTheDocument ( ) ;
190+ } ) ;
191+ } ) ;
192+
193+ it ( 'should fetch counts from dataSource on mount' , async ( ) => {
194+ mockFind . mockResolvedValue ( { data : [ { id : '1' } ] } ) ;
195+ wrap ( < SystemHubPage /> ) ;
196+ await waitFor ( ( ) => {
197+ expect ( mockFind ) . toHaveBeenCalledWith ( 'sys_user' ) ;
198+ expect ( mockFind ) . toHaveBeenCalledWith ( 'sys_org' ) ;
199+ expect ( mockFind ) . toHaveBeenCalledWith ( 'sys_role' ) ;
200+ expect ( mockFind ) . toHaveBeenCalledWith ( 'sys_permission' ) ;
201+ expect ( mockFind ) . toHaveBeenCalledWith ( 'sys_audit_log' ) ;
202+ } ) ;
203+ } ) ;
204+
205+ it ( 'should navigate to section when card is clicked' , async ( ) => {
206+ mockFind . mockResolvedValue ( { data : [ ] } ) ;
207+ wrap ( < SystemHubPage /> ) ;
208+ await waitFor ( ( ) => {
209+ expect ( screen . getByTestId ( 'hub-card-users' ) ) . toBeInTheDocument ( ) ;
210+ } ) ;
211+ fireEvent . click ( screen . getByTestId ( 'hub-card-users' ) ) ;
212+ expect ( mockNavigate ) . toHaveBeenCalledWith ( '/apps/test-app/system/users' ) ;
213+ } ) ;
214+ } ) ;
215+
216+ describe ( 'AppManagementPage' , ( ) => {
217+ it ( 'should render app list from metadata' , ( ) => {
218+ wrap ( < AppManagementPage /> ) ;
219+ expect ( screen . getByText ( 'Applications' ) ) . toBeInTheDocument ( ) ;
220+ expect ( screen . getByTestId ( 'app-card-crm' ) ) . toBeInTheDocument ( ) ;
221+ expect ( screen . getByTestId ( 'app-card-hr' ) ) . toBeInTheDocument ( ) ;
222+ } ) ;
223+
224+ it ( 'should filter apps by search query' , ( ) => {
225+ wrap ( < AppManagementPage /> ) ;
226+ fireEvent . change ( screen . getByTestId ( 'app-search-input' ) , { target : { value : 'CRM' } } ) ;
227+ expect ( screen . getByTestId ( 'app-card-crm' ) ) . toBeInTheDocument ( ) ;
228+ expect ( screen . queryByTestId ( 'app-card-hr' ) ) . not . toBeInTheDocument ( ) ;
229+ } ) ;
230+
231+ it ( 'should show empty state when no matching apps' , ( ) => {
232+ wrap ( < AppManagementPage /> ) ;
233+ fireEvent . change ( screen . getByTestId ( 'app-search-input' ) , { target : { value : 'nonexistent' } } ) ;
234+ expect ( screen . getByTestId ( 'no-apps-message' ) ) . toBeInTheDocument ( ) ;
235+ } ) ;
236+
237+ it ( 'should navigate to create-app on New App button click' , ( ) => {
238+ wrap ( < AppManagementPage /> ) ;
239+ fireEvent . click ( screen . getByTestId ( 'create-app-btn' ) ) ;
240+ expect ( mockNavigate ) . toHaveBeenCalledWith ( '/apps/test-app/create-app' ) ;
241+ } ) ;
242+ } ) ;
243+
244+ describe ( 'PermissionManagementPage' , ( ) => {
245+ it ( 'should call dataSource.find("sys_permission") on mount' , async ( ) => {
246+ mockFind . mockResolvedValueOnce ( {
247+ data : [ { id : '1' , name : 'manage_users' , resource : 'user' , action : 'manage' , description : 'Full user access' } ] ,
248+ } ) ;
249+ wrap ( < PermissionManagementPage /> ) ;
250+ await waitFor ( ( ) => {
251+ expect ( mockFind ) . toHaveBeenCalledWith ( 'sys_permission' ) ;
252+ } ) ;
253+ expect ( screen . getByText ( 'manage_users' ) ) . toBeInTheDocument ( ) ;
254+ } ) ;
255+
256+ it ( 'should show empty state when no permissions' , async ( ) => {
257+ mockFind . mockResolvedValueOnce ( { data : [ ] } ) ;
258+ wrap ( < PermissionManagementPage /> ) ;
259+ await waitFor ( ( ) => {
260+ expect ( screen . getByText ( 'No permissions found.' ) ) . toBeInTheDocument ( ) ;
261+ } ) ;
262+ } ) ;
263+
264+ it ( 'should call create when Add Permission is clicked' , async ( ) => {
265+ mockFind . mockResolvedValue ( { data : [ ] } ) ;
266+ mockCreate . mockResolvedValueOnce ( { id : 'new-perm' } ) ;
267+ wrap ( < PermissionManagementPage /> ) ;
268+ await waitFor ( ( ) => {
269+ expect ( screen . getByText ( 'No permissions found.' ) ) . toBeInTheDocument ( ) ;
270+ } ) ;
271+ fireEvent . click ( screen . getByText ( 'Add Permission' ) ) ;
272+ await waitFor ( ( ) => {
273+ expect ( mockCreate ) . toHaveBeenCalledWith ( 'sys_permission' , expect . objectContaining ( { name : 'New Permission' } ) ) ;
274+ } ) ;
275+ } ) ;
276+
277+ it ( 'should filter permissions by search query' , async ( ) => {
278+ mockFind . mockResolvedValue ( {
279+ data : [
280+ { id : '1' , name : 'manage_users' , resource : 'user' , action : 'manage' , description : '' } ,
281+ { id : '2' , name : 'read_reports' , resource : 'report' , action : 'read' , description : '' } ,
282+ ] ,
283+ } ) ;
284+ wrap ( < PermissionManagementPage /> ) ;
285+ await waitFor ( ( ) => {
286+ expect ( screen . getByText ( 'manage_users' ) ) . toBeInTheDocument ( ) ;
287+ expect ( screen . getByText ( 'read_reports' ) ) . toBeInTheDocument ( ) ;
288+ } ) ;
289+ fireEvent . change ( screen . getByTestId ( 'permission-search-input' ) , { target : { value : 'report' } } ) ;
290+ await waitFor ( ( ) => {
291+ expect ( screen . queryByText ( 'manage_users' ) ) . not . toBeInTheDocument ( ) ;
292+ expect ( screen . getByText ( 'read_reports' ) ) . toBeInTheDocument ( ) ;
293+ } ) ;
294+ } ) ;
295+ } ) ;
0 commit comments