@nocobase/client-v2 2.3.0-alpha.1 → 3.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/acl/ACLProvider.d.ts +10 -0
- package/es/authRedirect.d.ts +5 -0
- package/es/components/AttachmentUpload.d.ts +51 -0
- package/es/components/form/ScanInput/useCodeScanner.d.ts +2 -2
- package/es/components/index.d.ts +1 -0
- package/es/flow/actions/linkageRules.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/NocoBaseLogo.d.ts +10 -0
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
- package/es/flow/models/base/ActionModelCore.d.ts +4 -2
- package/es/flow/models/base/BlockGridModel.d.ts +3 -0
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
- package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
- package/es/index.d.ts +4 -1
- package/es/index.mjs +139 -125
- package/es/nocobase-buildin-plugin/currentUserAuthStatus.d.ts +11 -0
- package/es/nocobase-buildin-plugin/index.d.ts +9 -0
- package/es/settings-app/SettingsApplication.d.ts +18 -0
- package/es/settings-app/SettingsBrand.d.ts +16 -0
- package/es/settings-app/SettingsBuildInPlugin.d.ts +9 -0
- package/es/settings-app/SettingsDocumentRedirect.d.ts +10 -0
- package/es/settings-app/SettingsGroupNav.d.ts +17 -0
- package/es/settings-app/SettingsPluginSettingsManager.d.ts +14 -0
- package/es/settings-app/SettingsRouterManager.d.ts +15 -0
- package/es/settings-app/SettingsSearch.d.ts +17 -0
- package/es/settings-app/SettingsShell.d.ts +10 -0
- package/es/settings-app/isSettingsApp.d.ts +18 -0
- package/es/settings-app/runtimePaths.d.ts +18 -0
- package/es/settings-app/settingsDocumentPath.d.ts +14 -0
- package/es/settings-app/settingsTheme.d.ts +87 -0
- package/es/settings-app/useSettingsThemeConfig.d.ts +22 -0
- package/es/settings-center/AdminSettingsLayout.d.ts +2 -2
- package/es/settings-center/groups.d.ts +66 -0
- package/es/settings-center/useSettingsGroups.d.ts +36 -0
- package/es/settings-center/useSettingsSearch.d.ts +35 -0
- package/es/settings-center/utils.d.ts +24 -0
- package/es/theme/index.d.ts +1 -0
- package/es/theme/itemActive.d.ts +24 -0
- package/es/theme/type.d.ts +9 -0
- package/lib/index.js +141 -127
- package/lib/locale/languageCodes.js +1 -0
- package/package.json +8 -7
- package/src/__tests__/authRedirect.test.ts +137 -0
- package/src/__tests__/browserChecker.test.ts +124 -6
- package/src/__tests__/mockSettingsApplication.ts +29 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +51 -10
- package/src/__tests__/plugin-manager.test.tsx +7 -6
- package/src/__tests__/settings-application.test.ts +164 -0
- package/src/__tests__/settings-auth-routes.test.ts +71 -0
- package/src/__tests__/settings-center.test.tsx +38 -37
- package/src/__tests__/settings-layout-root.test.tsx +260 -0
- package/src/__tests__/settings-runtime-paths.test.ts +99 -0
- package/src/__tests__/settings-shell.test.tsx +200 -0
- package/src/acl/ACLProvider.tsx +21 -0
- package/src/authRedirect.ts +72 -3
- package/src/components/AttachmentUpload.tsx +275 -0
- package/src/components/form/ScanInput/CodeScanner.tsx +61 -34
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +114 -18
- package/src/components/form/ScanInput/useCodeScanner.ts +166 -23
- package/src/components/index.ts +1 -0
- package/src/flow/__tests__/FlowRoute.test.tsx +126 -8
- package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
- package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
- package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
- package/src/flow/actions/linkageRules.tsx +26 -6
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +1 -56
- package/src/flow/admin-shell/admin-layout/NocoBaseLogo.tsx +77 -0
- package/src/flow/admin-shell/admin-layout/TopbarActionsBar.tsx +37 -14
- package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +77 -13
- package/src/flow/common/Markdown/style.ts +4 -0
- package/src/flow/components/BlockItemCard.tsx +2 -2
- package/src/flow/components/FlowRoute.tsx +71 -29
- package/src/flow/index.ts +2 -0
- package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
- package/src/flow/models/base/ActionModelCore.tsx +11 -2
- package/src/flow/models/base/BlockGridModel.tsx +26 -0
- package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
- package/src/flow/models/base/PageModel/PageTabModel.tsx +67 -17
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +509 -12
- package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
- package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
- package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
- package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +27 -3
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +32 -4
- package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +3 -1
- package/src/flow/models/blocks/table/TableColumnModel.tsx +30 -1
- package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
- package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +33 -0
- package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
- package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
- package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
- package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
- package/src/flow/models/topbar/TopbarActionModel.tsx +52 -180
- package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
- package/src/index.ts +10 -1
- package/src/locale/languageCodes.ts +1 -0
- package/src/nocobase-buildin-plugin/currentUserAuthStatus.ts +58 -0
- package/src/nocobase-buildin-plugin/index.tsx +99 -71
- package/src/settings-app/SettingsApplication.ts +31 -0
- package/src/settings-app/SettingsBrand.tsx +108 -0
- package/src/settings-app/SettingsBuildInPlugin.ts +10 -0
- package/src/settings-app/SettingsDocumentRedirect.tsx +26 -0
- package/src/settings-app/SettingsGroupNav.tsx +124 -0
- package/src/settings-app/SettingsPluginSettingsManager.ts +38 -0
- package/src/settings-app/SettingsRouterManager.ts +92 -0
- package/src/settings-app/SettingsSearch.tsx +238 -0
- package/src/settings-app/SettingsShell.tsx +167 -0
- package/src/settings-app/isSettingsApp.ts +21 -0
- package/src/settings-app/runtimePaths.ts +104 -0
- package/src/settings-app/settingsDocumentPath.ts +66 -0
- package/src/settings-app/settingsTheme.ts +301 -0
- package/src/settings-app/useSettingsThemeConfig.ts +100 -0
- package/src/settings-center/AdminSettingsLayout.tsx +122 -141
- package/src/settings-center/SystemSettingsPage.tsx +4 -170
- package/src/settings-center/groups.ts +108 -0
- package/src/settings-center/useSettingsGroups.ts +105 -0
- package/src/settings-center/useSettingsSearch.ts +173 -0
- package/src/settings-center/utils.tsx +50 -1
- package/src/theme/index.tsx +1 -0
- package/src/theme/itemActive.ts +31 -0
- package/src/theme/type.ts +10 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import MockAdapter from 'axios-mock-adapter';
|
|
11
|
+
import { describe, expect, it } from 'vitest';
|
|
12
|
+
import { Application } from '../index';
|
|
13
|
+
import { SettingsApplication } from '../settings-app/SettingsApplication';
|
|
14
|
+
import { SettingsBuildInPlugin } from '../settings-app/SettingsBuildInPlugin';
|
|
15
|
+
|
|
16
|
+
describe('SettingsApplication', () => {
|
|
17
|
+
it('uses the standalone settings route namespace', () => {
|
|
18
|
+
const adminApp = new Application({ router: { type: 'memory' }, ws: false });
|
|
19
|
+
const settingsApp = new SettingsApplication({ router: { type: 'memory' }, ws: false });
|
|
20
|
+
|
|
21
|
+
expect(adminApp.pluginSettingsManager.getRouteName('demo.index')).toBe('admin.settings.demo.index');
|
|
22
|
+
expect(adminApp.pluginSettingsManager.getRoutePath('demo.index')).toBe('/admin/settings/demo');
|
|
23
|
+
|
|
24
|
+
expect(settingsApp.pluginSettingsManager.getRouteName('demo.index')).toBe('settings.demo.index');
|
|
25
|
+
expect(settingsApp.pluginSettingsManager.getRoutePath('demo.index')).toBe('/settings/demo');
|
|
26
|
+
expect(settingsApp.pluginSettingsManager.getRoutePath('')).toBe('/settings/');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it.each(['apps', '_app'])('uses basename-relative paths for the %s sub-application Settings runtime', (scope) => {
|
|
30
|
+
const app = new SettingsApplication({
|
|
31
|
+
publicPath: '/nocobase/',
|
|
32
|
+
router: { type: 'memory', basename: `/nocobase/settings/${scope}/demo/` },
|
|
33
|
+
ws: false,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(app.pluginSettingsManager.getRouteName('demo.index')).toBe('settings.demo.index');
|
|
37
|
+
expect(app.pluginSettingsManager.getRoutePath('')).toBe('/');
|
|
38
|
+
expect(app.pluginSettingsManager.getRoutePath('demo.index')).toBe('/demo');
|
|
39
|
+
|
|
40
|
+
app.router.add('settings', { path: '/settings' });
|
|
41
|
+
app.router.add('settingsDetails.workflow.canvas', { path: '/settings/workflow/workflows/:id' });
|
|
42
|
+
app.router.add('settingsDetails.preview', { path: '/settings-preview/:id' });
|
|
43
|
+
app.router.add('auth.signin', { path: '/signin' });
|
|
44
|
+
|
|
45
|
+
expect(app.router.get('settings')).toMatchObject({ path: '/' });
|
|
46
|
+
expect(app.router.get('settingsDetails.workflow.canvas')).toMatchObject({
|
|
47
|
+
path: '/workflow/workflows/:id',
|
|
48
|
+
authCheck: true,
|
|
49
|
+
});
|
|
50
|
+
expect(
|
|
51
|
+
app.router
|
|
52
|
+
.matchRoutes(`/nocobase/settings/${scope}/demo/workflow/workflows/1`)
|
|
53
|
+
?.some((match) => match.route.id === 'settingsDetails.workflow.canvas'),
|
|
54
|
+
).toBe(true);
|
|
55
|
+
expect(
|
|
56
|
+
app.router
|
|
57
|
+
.matchRoutes(`/nocobase/settings/${scope}/demo/settings/workflow/workflows/1`)
|
|
58
|
+
?.some((match) => match.route.id === 'settingsDetails.workflow.canvas'),
|
|
59
|
+
).toBe(false);
|
|
60
|
+
expect(app.router.get('settingsDetails.preview')).toMatchObject({ path: '/settings-preview/:id' });
|
|
61
|
+
expect(app.router.get('auth.signin')).toMatchObject({ path: '/signin' });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('does not treat an apps segment inside the main application public path as a sub-application scope', () => {
|
|
65
|
+
const app = new SettingsApplication({
|
|
66
|
+
router: { type: 'memory', basename: '/tenant/apps/root/' },
|
|
67
|
+
ws: false,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(app.pluginSettingsManager.getRoutePath('')).toBe('/settings/');
|
|
71
|
+
expect(app.pluginSettingsManager.getRoutePath('demo.index')).toBe('/settings/demo');
|
|
72
|
+
|
|
73
|
+
app.router.add('settingsDetails.workflow.canvas', { path: '/settings/workflow/workflows/:id' });
|
|
74
|
+
expect(app.router.get('settingsDetails.workflow.canvas')).toMatchObject({
|
|
75
|
+
path: '/settings/workflow/workflows/:id',
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it.each(['apps', '_app'])(
|
|
80
|
+
'does not treat a scoped-looking %s suffix inside the main public path as a sub-application scope',
|
|
81
|
+
(scope) => {
|
|
82
|
+
const publicPath = `/tenant/settings/${scope}/root/`;
|
|
83
|
+
const app = new SettingsApplication({
|
|
84
|
+
publicPath,
|
|
85
|
+
router: { type: 'memory', basename: publicPath },
|
|
86
|
+
ws: false,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(app.pluginSettingsManager.getRoutePath('')).toBe('/settings/');
|
|
90
|
+
expect(app.pluginSettingsManager.getRoutePath('demo.index')).toBe('/settings/demo');
|
|
91
|
+
|
|
92
|
+
app.router.add('settingsDetails.workflow.canvas', { path: '/settings/workflow/workflows/:id' });
|
|
93
|
+
app.router.add('auth.signin', { path: '/signin' });
|
|
94
|
+
|
|
95
|
+
expect(app.router.get('settingsDetails.workflow.canvas')).toMatchObject({
|
|
96
|
+
path: '/settings/workflow/workflows/:id',
|
|
97
|
+
});
|
|
98
|
+
expect(app.router.get('auth.signin')).toMatchObject({ path: '/settings/signin' });
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
it('keeps only routes owned by the settings runtime', () => {
|
|
103
|
+
const app = new SettingsApplication({ router: { type: 'memory' }, ws: false });
|
|
104
|
+
|
|
105
|
+
app.router.add('settings', { path: '/settings' });
|
|
106
|
+
app.router.add('settings.demo', { path: 'demo' });
|
|
107
|
+
app.router.add('settingsDetails.workflow.canvas', { path: '/settings/workflow/workflows/:id' });
|
|
108
|
+
app.router.add('admin.demo', { path: '/admin/demo' });
|
|
109
|
+
app.router.add('public-forms', { path: '/public-forms/:key' });
|
|
110
|
+
app.router.add('mobile', { path: '/mobile' });
|
|
111
|
+
|
|
112
|
+
expect(app.router.has('settings')).toBe(true);
|
|
113
|
+
expect(app.router.has('settings.demo')).toBe(true);
|
|
114
|
+
expect(app.router.get('settingsDetails.workflow.canvas')).toMatchObject({
|
|
115
|
+
path: '/settings/workflow/workflows/:id',
|
|
116
|
+
authCheck: true,
|
|
117
|
+
});
|
|
118
|
+
expect(app.router.has('admin.demo')).toBe(false);
|
|
119
|
+
expect(app.router.has('public-forms')).toBe(false);
|
|
120
|
+
expect(app.router.has('mobile')).toBe(false);
|
|
121
|
+
expect(app.router.has('not-found')).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('keeps hidden plugin detail pages in the normal Settings route tree', () => {
|
|
125
|
+
const app = new SettingsApplication({ router: { type: 'memory' }, ws: false });
|
|
126
|
+
|
|
127
|
+
app.pluginSettingsManager.addMenuItem({ key: 'public-forms', title: 'Public forms' });
|
|
128
|
+
app.pluginSettingsManager.addPageTabItem({
|
|
129
|
+
menuKey: 'public-forms',
|
|
130
|
+
key: ':name',
|
|
131
|
+
title: false,
|
|
132
|
+
hidden: true,
|
|
133
|
+
Component: () => null,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(app.pluginSettingsManager.get('public-forms.:name')).toMatchObject({
|
|
137
|
+
hidden: true,
|
|
138
|
+
path: '/settings/public-forms/:name',
|
|
139
|
+
});
|
|
140
|
+
expect(app.router.get('settings.public-forms.:name')).toMatchObject({ path: ':name' });
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('registers the authenticated settings shell without the admin layout', async () => {
|
|
144
|
+
const app = new SettingsApplication({ router: { type: 'memory' }, ws: false });
|
|
145
|
+
const apiMock = new MockAdapter(app.apiClient.axios);
|
|
146
|
+
apiMock.onGet('app:getLang').reply(200, { data: { lang: 'en-US', resources: { client: {} }, cron: {} } });
|
|
147
|
+
const plugin = new SettingsBuildInPlugin({ name: 'settings-buildin' }, app);
|
|
148
|
+
|
|
149
|
+
await plugin.afterAdd();
|
|
150
|
+
await plugin.load();
|
|
151
|
+
app.router.add('settingsDetails.workflow.canvas', { path: '/settings/workflow/workflows/:id' });
|
|
152
|
+
|
|
153
|
+
expect(app.router.get('settings')).toMatchObject({ path: '/settings', authCheck: true });
|
|
154
|
+
expect(app.router.get('settingsDetails')).toMatchObject({ path: '/settings', authCheck: true });
|
|
155
|
+
expect(
|
|
156
|
+
app.router.matchRoutes('/settings/workflow/workflows/1')?.some((match) => match.route.authCheck === true),
|
|
157
|
+
).toBe(true);
|
|
158
|
+
expect(app.router.has('admin')).toBe(false);
|
|
159
|
+
expect(app.layoutManager.hasLayout('admin')).toBe(false);
|
|
160
|
+
expect(app.pluginSettingsManager.has('plugin-manager')).toBe(true);
|
|
161
|
+
expect(app.pluginSettingsManager.has('system-settings')).toBe(true);
|
|
162
|
+
expect(app.pluginSettingsManager.has('security')).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Application } from '../index';
|
|
11
|
+
import { SettingsApplication } from '../settings-app/SettingsApplication';
|
|
12
|
+
|
|
13
|
+
describe('standalone settings authentication routes', () => {
|
|
14
|
+
it('keeps and rebases the auth and 2fa route families', () => {
|
|
15
|
+
const app = new SettingsApplication({ router: { type: 'memory' }, ws: false });
|
|
16
|
+
|
|
17
|
+
app.router.add('auth', { Component: () => null });
|
|
18
|
+
app.router.add('auth.signin', { path: '/signin', skipAuthCheck: true, Component: () => null });
|
|
19
|
+
app.router.add('auth.signup', { path: '/signup', skipAuthCheck: true, Component: () => null });
|
|
20
|
+
app.router.add('auth.forgotPassword', {
|
|
21
|
+
path: '/forgot-password',
|
|
22
|
+
skipAuthCheck: true,
|
|
23
|
+
Component: () => null,
|
|
24
|
+
});
|
|
25
|
+
app.router.add('auth.resetPassword', {
|
|
26
|
+
path: '/reset-password',
|
|
27
|
+
skipAuthCheck: true,
|
|
28
|
+
Component: () => null,
|
|
29
|
+
});
|
|
30
|
+
app.router.add('2fa', { Component: () => null });
|
|
31
|
+
app.router.add('2fa.verify', { path: '/2fa', skipAuthCheck: true, Component: () => null });
|
|
32
|
+
|
|
33
|
+
expect(app.router.get('auth.signin')).toMatchObject({ path: '/settings/signin', skipAuthCheck: true });
|
|
34
|
+
expect(app.router.get('auth.signup')).toMatchObject({ path: '/settings/signup', skipAuthCheck: true });
|
|
35
|
+
expect(app.router.get('auth.forgotPassword')).toMatchObject({
|
|
36
|
+
path: '/settings/forgot-password',
|
|
37
|
+
skipAuthCheck: true,
|
|
38
|
+
});
|
|
39
|
+
expect(app.router.get('auth.resetPassword')).toMatchObject({
|
|
40
|
+
path: '/settings/reset-password',
|
|
41
|
+
skipAuthCheck: true,
|
|
42
|
+
});
|
|
43
|
+
expect(app.router.get('2fa.verify')).toMatchObject({ path: '/settings/2fa', skipAuthCheck: true });
|
|
44
|
+
expect(app.router.matchRoutes('/settings/signin')?.map((match) => match.route.id)).toContain('auth.signin');
|
|
45
|
+
expect(app.router.isSkippedAuthCheckRoute('/settings/reset-password')).toBe(true);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('does not change the default Client V2 authentication routes', () => {
|
|
49
|
+
const app = new Application({ router: { type: 'memory' }, ws: false });
|
|
50
|
+
|
|
51
|
+
app.router.add('auth', { Component: () => null });
|
|
52
|
+
app.router.add('auth.signin', { path: '/signin', skipAuthCheck: true, Component: () => null });
|
|
53
|
+
app.router.add('2fa', { Component: () => null });
|
|
54
|
+
app.router.add('2fa.verify', { path: '/2fa', skipAuthCheck: true, Component: () => null });
|
|
55
|
+
|
|
56
|
+
expect(app.router.get('auth.signin')).toMatchObject({ path: '/signin', skipAuthCheck: true });
|
|
57
|
+
expect(app.router.get('2fa.verify')).toMatchObject({ path: '/2fa', skipAuthCheck: true });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('continues to reject unrelated routes from the shared plugin lane', () => {
|
|
61
|
+
const app = new SettingsApplication({ router: { type: 'memory' }, ws: false });
|
|
62
|
+
|
|
63
|
+
app.router.add('admin.demo', { path: '/admin/demo' });
|
|
64
|
+
app.router.add('public.demo', { path: '/public/demo' });
|
|
65
|
+
app.router.add('mobile.demo', { path: '/mobile/demo' });
|
|
66
|
+
|
|
67
|
+
expect(app.router.has('admin.demo')).toBe(false);
|
|
68
|
+
expect(app.router.has('public.demo')).toBe(false);
|
|
69
|
+
expect(app.router.has('mobile.demo')).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
@@ -7,14 +7,15 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { ACLRolesCheckProvider,
|
|
10
|
+
import { ACLRolesCheckProvider, Plugin } from '@nocobase/client-v2';
|
|
11
11
|
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
|
|
12
12
|
import React from 'react';
|
|
13
13
|
import { message } from 'antd';
|
|
14
14
|
import { AdminSettingsLayoutModel as ClientV2AdminSettingsLayoutModel } from '../settings-center';
|
|
15
15
|
import { AdminSettingsLayoutModel as ClientV1AdminSettingsLayoutModel } from '../../../client/src/pm/AdminSettingsLayoutModel';
|
|
16
|
-
import {
|
|
16
|
+
import { SettingsBuildInPlugin } from '../settings-app/SettingsBuildInPlugin';
|
|
17
17
|
import { matchSettingsRoute, sortTopLevelSettings } from '../settings-center/utils';
|
|
18
|
+
import { createMockSettingsClient } from './mockSettingsApplication';
|
|
18
19
|
|
|
19
20
|
class TestAclPlugin extends Plugin {
|
|
20
21
|
async load() {
|
|
@@ -22,7 +23,7 @@ class TestAclPlugin extends Plugin {
|
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
type MockClientApplication = ReturnType<typeof
|
|
26
|
+
type MockClientApplication = ReturnType<typeof createMockSettingsClient>;
|
|
26
27
|
|
|
27
28
|
const renderApp = async (app: MockClientApplication) => {
|
|
28
29
|
const Root = app.getRootComponent();
|
|
@@ -174,10 +175,10 @@ describe('settings center', () => {
|
|
|
174
175
|
expect(sortTopLevelSettings(settings).map((item) => item.name)).toEqual(['api-keys', 'backups', 'system-settings']);
|
|
175
176
|
});
|
|
176
177
|
|
|
177
|
-
it('should redirect /
|
|
178
|
-
const app =
|
|
179
|
-
plugins: [
|
|
180
|
-
router: { type: 'memory', initialEntries: ['/
|
|
178
|
+
it('should redirect /settings to system-settings by default', async () => {
|
|
179
|
+
const app = createMockSettingsClient({
|
|
180
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin],
|
|
181
|
+
router: { type: 'memory', initialEntries: ['/settings'] },
|
|
181
182
|
});
|
|
182
183
|
mockAdminRuntime(app);
|
|
183
184
|
|
|
@@ -188,9 +189,9 @@ describe('settings center', () => {
|
|
|
188
189
|
});
|
|
189
190
|
|
|
190
191
|
it('should expose current language variable as enabled-language selector', async () => {
|
|
191
|
-
const app =
|
|
192
|
-
plugins: [
|
|
193
|
-
router: { type: 'memory', initialEntries: ['/
|
|
192
|
+
const app = createMockSettingsClient({
|
|
193
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin],
|
|
194
|
+
router: { type: 'memory', initialEntries: ['/settings/system-settings'] },
|
|
194
195
|
});
|
|
195
196
|
mockAdminRuntime(app, {
|
|
196
197
|
systemSettings: {
|
|
@@ -218,9 +219,9 @@ describe('settings center', () => {
|
|
|
218
219
|
});
|
|
219
220
|
|
|
220
221
|
it('should fallback to plugin-manager when system-settings is not allowed', async () => {
|
|
221
|
-
const app =
|
|
222
|
-
plugins: [
|
|
223
|
-
router: { type: 'memory', initialEntries: ['/
|
|
222
|
+
const app = createMockSettingsClient({
|
|
223
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin],
|
|
224
|
+
router: { type: 'memory', initialEntries: ['/settings'] },
|
|
224
225
|
});
|
|
225
226
|
mockAdminRuntime(app, {
|
|
226
227
|
snippets: ['pm', '!pm.system-settings.system-settings'],
|
|
@@ -244,9 +245,9 @@ describe('settings center', () => {
|
|
|
244
245
|
});
|
|
245
246
|
|
|
246
247
|
it('should hide plugin-manager menu item when pm snippet is missing', async () => {
|
|
247
|
-
const app =
|
|
248
|
-
plugins: [
|
|
249
|
-
router: { type: 'memory', initialEntries: ['/
|
|
248
|
+
const app = createMockSettingsClient({
|
|
249
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin],
|
|
250
|
+
router: { type: 'memory', initialEntries: ['/settings/system-settings'] },
|
|
250
251
|
});
|
|
251
252
|
mockAdminRuntime(app, {
|
|
252
253
|
snippets: ['pm.system-settings.system-settings'],
|
|
@@ -260,9 +261,9 @@ describe('settings center', () => {
|
|
|
260
261
|
});
|
|
261
262
|
|
|
262
263
|
it('should show route empty state for unknown settings routes', async () => {
|
|
263
|
-
const app =
|
|
264
|
-
plugins: [
|
|
265
|
-
router: { type: 'memory', initialEntries: ['/
|
|
264
|
+
const app = createMockSettingsClient({
|
|
265
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin],
|
|
266
|
+
router: { type: 'memory', initialEntries: ['/settings/unknown'] },
|
|
266
267
|
});
|
|
267
268
|
mockAdminRuntime(app);
|
|
268
269
|
|
|
@@ -286,9 +287,9 @@ describe('settings center', () => {
|
|
|
286
287
|
}
|
|
287
288
|
}
|
|
288
289
|
|
|
289
|
-
const app =
|
|
290
|
-
plugins: [
|
|
291
|
-
router: { type: 'memory', initialEntries: ['/
|
|
290
|
+
const app = createMockSettingsClient({
|
|
291
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin, HiddenSettingsPlugin],
|
|
292
|
+
router: { type: 'memory', initialEntries: ['/settings/hidden-demo'] },
|
|
292
293
|
});
|
|
293
294
|
mockAdminRuntime(app);
|
|
294
295
|
|
|
@@ -313,9 +314,9 @@ describe('settings center', () => {
|
|
|
313
314
|
}
|
|
314
315
|
}
|
|
315
316
|
|
|
316
|
-
const app =
|
|
317
|
-
plugins: [
|
|
318
|
-
router: { type: 'memory', initialEntries: ['/
|
|
317
|
+
const app = createMockSettingsClient({
|
|
318
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin, ProtectedSettingsPlugin],
|
|
319
|
+
router: { type: 'memory', initialEntries: ['/settings/secure-demo'] },
|
|
319
320
|
});
|
|
320
321
|
mockAdminRuntime(app, {
|
|
321
322
|
snippets: ['pm', 'pm.system-settings.system-settings', '!pm.secure-demo.index'],
|
|
@@ -345,9 +346,9 @@ describe('settings center', () => {
|
|
|
345
346
|
}
|
|
346
347
|
}
|
|
347
348
|
|
|
348
|
-
const app =
|
|
349
|
-
plugins: [
|
|
350
|
-
router: { type: 'memory', initialEntries: ['/
|
|
349
|
+
const app = createMockSettingsClient({
|
|
350
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin, MenuAclPlugin],
|
|
351
|
+
router: { type: 'memory', initialEntries: ['/settings/menu-acl-demo'] },
|
|
351
352
|
});
|
|
352
353
|
mockAdminRuntime(app, {
|
|
353
354
|
snippets: ['pm', 'pm.system-settings.system-settings', '!pm.menu-acl-demo.menu'],
|
|
@@ -378,9 +379,9 @@ describe('settings center', () => {
|
|
|
378
379
|
}
|
|
379
380
|
}
|
|
380
381
|
|
|
381
|
-
const app =
|
|
382
|
-
plugins: [
|
|
383
|
-
router: { type: 'memory', initialEntries: ['/
|
|
382
|
+
const app = createMockSettingsClient({
|
|
383
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin, ManySettingsPlugin],
|
|
384
|
+
router: { type: 'memory', initialEntries: ['/settings/scroll-demo-29'] },
|
|
384
385
|
});
|
|
385
386
|
mockAdminRuntime(app);
|
|
386
387
|
|
|
@@ -394,9 +395,9 @@ describe('settings center', () => {
|
|
|
394
395
|
});
|
|
395
396
|
|
|
396
397
|
it('should save system settings through systemSettings:put', async () => {
|
|
397
|
-
const app =
|
|
398
|
-
plugins: [
|
|
399
|
-
router: { type: 'memory', initialEntries: ['/
|
|
398
|
+
const app = createMockSettingsClient({
|
|
399
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin],
|
|
400
|
+
router: { type: 'memory', initialEntries: ['/settings/system-settings'] },
|
|
400
401
|
});
|
|
401
402
|
mockAdminRuntime(app);
|
|
402
403
|
|
|
@@ -413,9 +414,9 @@ describe('settings center', () => {
|
|
|
413
414
|
});
|
|
414
415
|
|
|
415
416
|
it('should block invalid logo uploads by storage rules', async () => {
|
|
416
|
-
const app =
|
|
417
|
-
plugins: [
|
|
418
|
-
router: { type: 'memory', initialEntries: ['/
|
|
417
|
+
const app = createMockSettingsClient({
|
|
418
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin],
|
|
419
|
+
router: { type: 'memory', initialEntries: ['/settings/system-settings'] },
|
|
419
420
|
});
|
|
420
421
|
const messageErrorSpy = vi.spyOn(message, 'error').mockImplementation(() => {
|
|
421
422
|
return undefined as any;
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is part of the NocoBase (R) project.
|
|
3
|
+
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
|
4
|
+
* Authors: NocoBase Team.
|
|
5
|
+
*
|
|
6
|
+
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
|
7
|
+
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { render, screen, waitFor } from '@testing-library/react';
|
|
11
|
+
import MockAdapter from 'axios-mock-adapter';
|
|
12
|
+
import React from 'react';
|
|
13
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
14
|
+
import { ACLRolesCheckProvider } from '../acl';
|
|
15
|
+
import { Plugin } from '../Plugin';
|
|
16
|
+
import { SettingsApplication } from '../settings-app/SettingsApplication';
|
|
17
|
+
import { SettingsBuildInPlugin } from '../settings-app/SettingsBuildInPlugin';
|
|
18
|
+
|
|
19
|
+
class TestAclPlugin extends Plugin {
|
|
20
|
+
async load() {
|
|
21
|
+
this.app.use(ACLRolesCheckProvider);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
class StandaloneSettingsPlugin extends Plugin {
|
|
26
|
+
async load() {
|
|
27
|
+
this.pluginSettingsManager.addMenuItem({ key: 'standalone', title: 'Standalone' });
|
|
28
|
+
this.pluginSettingsManager.addPageTabItem({
|
|
29
|
+
menuKey: 'standalone',
|
|
30
|
+
key: 'index',
|
|
31
|
+
title: 'Standalone',
|
|
32
|
+
Component: () => <div>Standalone settings page</div>,
|
|
33
|
+
sort: -1000,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
class MultiPortalSettingsPlugin extends Plugin {
|
|
39
|
+
async load() {
|
|
40
|
+
this.pluginSettingsManager.addMenuItem({
|
|
41
|
+
key: 'multi-portal',
|
|
42
|
+
title: 'Portal manager',
|
|
43
|
+
aclSnippet: 'pm.multi-portal',
|
|
44
|
+
sort: -300,
|
|
45
|
+
});
|
|
46
|
+
this.pluginSettingsManager.addPageTabItem({
|
|
47
|
+
menuKey: 'multi-portal',
|
|
48
|
+
key: 'index',
|
|
49
|
+
title: 'Portal manager',
|
|
50
|
+
aclSnippet: 'pm.multi-portal',
|
|
51
|
+
Component: () => <div>Portal manager page</div>,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
class PrimarySettingsPlugin extends Plugin {
|
|
57
|
+
async load() {
|
|
58
|
+
this.pluginSettingsManager.addMenuItem({
|
|
59
|
+
key: 'portal-manager',
|
|
60
|
+
title: 'Portal manager',
|
|
61
|
+
sort: -300,
|
|
62
|
+
});
|
|
63
|
+
this.pluginSettingsManager.addPageTabItem({
|
|
64
|
+
menuKey: 'portal-manager',
|
|
65
|
+
key: 'index',
|
|
66
|
+
title: 'Portal manager',
|
|
67
|
+
Component: () => <div>Portal manager page</div>,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
describe('standalone settings layout root', () => {
|
|
73
|
+
const originalLocation = window.location;
|
|
74
|
+
const originalModernClientPrefix = window.__nocobase_modern_client_prefix__;
|
|
75
|
+
|
|
76
|
+
afterEach(() => {
|
|
77
|
+
Object.defineProperty(window, 'location', { configurable: true, value: originalLocation });
|
|
78
|
+
if (originalModernClientPrefix === undefined) {
|
|
79
|
+
delete window.__nocobase_modern_client_prefix__;
|
|
80
|
+
} else {
|
|
81
|
+
window.__nocobase_modern_client_prefix__ = originalModernClientPrefix;
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it.each(['/settings', '/settings/', '/settings/index'])(
|
|
86
|
+
'redirects %s to multi-portal when it is accessible',
|
|
87
|
+
async (initialEntry) => {
|
|
88
|
+
const app = new SettingsApplication({
|
|
89
|
+
plugins: [SettingsBuildInPlugin, MultiPortalSettingsPlugin],
|
|
90
|
+
router: { type: 'memory', initialEntries: [initialEntry] },
|
|
91
|
+
ws: false,
|
|
92
|
+
});
|
|
93
|
+
const apiMock = new MockAdapter(app.apiClient.axios);
|
|
94
|
+
app.dataSourceManager.ensureLoaded = async () => {};
|
|
95
|
+
apiMock.onGet('app:getLang').reply(200, {
|
|
96
|
+
data: { lang: 'en-US', resources: { client: {} }, cron: {} },
|
|
97
|
+
});
|
|
98
|
+
apiMock.onGet('/auth:check').reply(200, { data: { id: 1, nickname: 'Admin' } });
|
|
99
|
+
apiMock.onGet('app:getInfo').reply(200, { data: { id: 'mock-app', version: 'test' } });
|
|
100
|
+
apiMock.onGet('systemSettings:get').reply(200, {
|
|
101
|
+
data: { id: 1, title: 'NocoBase', raw_title: 'NocoBase', logo: null },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const Root = app.getRootComponent();
|
|
105
|
+
render(<Root />);
|
|
106
|
+
|
|
107
|
+
await waitFor(() => {
|
|
108
|
+
expect(app.router.state.location.pathname).toBe('/settings/multi-portal');
|
|
109
|
+
});
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
it('falls back to system-settings when multi-portal is not registered', async () => {
|
|
114
|
+
const app = new SettingsApplication({
|
|
115
|
+
plugins: [SettingsBuildInPlugin, StandaloneSettingsPlugin],
|
|
116
|
+
router: { type: 'memory', initialEntries: ['/settings'] },
|
|
117
|
+
ws: false,
|
|
118
|
+
});
|
|
119
|
+
const apiMock = new MockAdapter(app.apiClient.axios);
|
|
120
|
+
app.dataSourceManager.ensureLoaded = async () => {};
|
|
121
|
+
apiMock.onGet('app:getLang').reply(200, {
|
|
122
|
+
data: { lang: 'en-US', resources: { client: {} }, cron: {} },
|
|
123
|
+
});
|
|
124
|
+
apiMock.onGet('/auth:check').reply(200, { data: { id: 1, nickname: 'Admin' } });
|
|
125
|
+
apiMock.onGet('app:getInfo').reply(200, { data: { id: 'mock-app', version: 'test' } });
|
|
126
|
+
apiMock.onGet('systemSettings:get').reply(200, {
|
|
127
|
+
data: { id: 1, title: 'NocoBase', raw_title: 'NocoBase', logo: null },
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
const Root = app.getRootComponent();
|
|
131
|
+
render(<Root />);
|
|
132
|
+
|
|
133
|
+
await waitFor(() => {
|
|
134
|
+
expect(app.router.state.location.pathname).toBe('/settings/system-settings');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('falls back to system-settings when multi-portal is not accessible', async () => {
|
|
139
|
+
const app = new SettingsApplication({
|
|
140
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin, MultiPortalSettingsPlugin],
|
|
141
|
+
router: { type: 'memory', initialEntries: ['/settings'] },
|
|
142
|
+
ws: false,
|
|
143
|
+
});
|
|
144
|
+
const apiMock = new MockAdapter(app.apiClient.axios);
|
|
145
|
+
app.dataSourceManager.ensureLoaded = async () => {};
|
|
146
|
+
apiMock.onGet('app:getLang').reply(200, {
|
|
147
|
+
data: { lang: 'en-US', resources: { client: {} }, cron: {} },
|
|
148
|
+
});
|
|
149
|
+
apiMock.onGet('/auth:check').reply(200, { data: { id: 1, nickname: 'Admin' } });
|
|
150
|
+
apiMock.onGet('app:getInfo').reply(200, { data: { id: 'mock-app', version: 'test' } });
|
|
151
|
+
apiMock.onGet('roles:check').reply(200, {
|
|
152
|
+
data: { role: 'member', snippets: ['!pm.multi-portal'] },
|
|
153
|
+
});
|
|
154
|
+
apiMock.onGet('systemSettings:get').reply(200, {
|
|
155
|
+
data: { id: 1, title: 'NocoBase', raw_title: 'NocoBase', logo: null },
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const Root = app.getRootComponent();
|
|
159
|
+
render(<Root />);
|
|
160
|
+
|
|
161
|
+
await waitFor(() => {
|
|
162
|
+
expect(app.router.state.location.pathname).toBe('/settings/system-settings');
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('keeps ordinary negative-sort settings in the active Settings group sidebar', async () => {
|
|
167
|
+
const app = new SettingsApplication({
|
|
168
|
+
plugins: [SettingsBuildInPlugin, TestAclPlugin, PrimarySettingsPlugin],
|
|
169
|
+
router: { type: 'memory', initialEntries: ['/settings/portal-manager'] },
|
|
170
|
+
ws: false,
|
|
171
|
+
});
|
|
172
|
+
const apiMock = new MockAdapter(app.apiClient.axios);
|
|
173
|
+
app.dataSourceManager.ensureLoaded = async () => {};
|
|
174
|
+
apiMock.onGet('app:getLang').reply(200, {
|
|
175
|
+
data: { lang: 'en-US', resources: { client: {} }, cron: {} },
|
|
176
|
+
});
|
|
177
|
+
apiMock.onGet('/auth:check').reply(200, { data: { id: 1, nickname: 'Admin' } });
|
|
178
|
+
apiMock.onGet('app:getInfo').reply(200, { data: { id: 'mock-app', version: 'test' } });
|
|
179
|
+
apiMock.onGet('roles:check').reply(200, {
|
|
180
|
+
data: { role: 'root', snippets: ['pm'] },
|
|
181
|
+
});
|
|
182
|
+
apiMock.onGet('systemSettings:get').reply(200, {
|
|
183
|
+
data: { id: 1, title: 'NocoBase', raw_title: 'NocoBase', logo: null },
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const Root = app.getRootComponent();
|
|
187
|
+
render(<Root />);
|
|
188
|
+
|
|
189
|
+
expect(await screen.findByText('Portal manager page')).toBeInTheDocument();
|
|
190
|
+
|
|
191
|
+
const portalManagerItem = screen.getByRole('menuitem', { name: 'Portal manager' });
|
|
192
|
+
expect(portalManagerItem.closest('aside')).toBeInTheDocument();
|
|
193
|
+
expect(screen.queryByRole('menuitem', { name: /Plugin manager$/ })).not.toBeInTheDocument();
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('uses document navigation to the standalone Settings signin page when unauthenticated', async () => {
|
|
197
|
+
const replace = vi.fn();
|
|
198
|
+
Object.defineProperty(window, 'location', {
|
|
199
|
+
configurable: true,
|
|
200
|
+
value: { ...originalLocation, replace },
|
|
201
|
+
});
|
|
202
|
+
window.__nocobase_modern_client_prefix__ = 'v';
|
|
203
|
+
const app = new SettingsApplication({
|
|
204
|
+
plugins: [SettingsBuildInPlugin],
|
|
205
|
+
router: { type: 'memory', initialEntries: ['/settings/workflow?tab=list#recent'] },
|
|
206
|
+
ws: false,
|
|
207
|
+
});
|
|
208
|
+
const apiMock = new MockAdapter(app.apiClient.axios);
|
|
209
|
+
apiMock.onGet('app:getLang').reply(200, {
|
|
210
|
+
data: { lang: 'en-US', resources: { client: {} }, cron: {} },
|
|
211
|
+
});
|
|
212
|
+
apiMock.onGet('app:getInfo').reply(200, { data: { id: 'mock-app', version: 'test' } });
|
|
213
|
+
apiMock.onGet('systemSettings:get').reply(200, {
|
|
214
|
+
data: { id: 1, title: 'NocoBase', raw_title: 'NocoBase', logo: null },
|
|
215
|
+
});
|
|
216
|
+
apiMock.onGet('/auth:check').reply(200, { data: {} });
|
|
217
|
+
|
|
218
|
+
const Root = app.getRootComponent();
|
|
219
|
+
render(<Root />);
|
|
220
|
+
|
|
221
|
+
await waitFor(() => {
|
|
222
|
+
expect(replace).toHaveBeenCalledWith('/settings/signin?redirect=%2Fsettings%2Fworkflow%3Ftab%3Dlist%23recent');
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
it('does not render the Settings header before the initial auth check completes', async () => {
|
|
227
|
+
let resolveAuthCheck: (response: [number, { data: { id: number } }]) => void = () => {
|
|
228
|
+
throw new Error('Auth check resolver is not initialized');
|
|
229
|
+
};
|
|
230
|
+
const authCheckResponse = new Promise<[number, { data: { id: number } }]>((resolve) => {
|
|
231
|
+
resolveAuthCheck = resolve;
|
|
232
|
+
});
|
|
233
|
+
const app = new SettingsApplication({
|
|
234
|
+
plugins: [SettingsBuildInPlugin],
|
|
235
|
+
router: { type: 'memory', initialEntries: ['/settings'] },
|
|
236
|
+
ws: false,
|
|
237
|
+
});
|
|
238
|
+
const apiMock = new MockAdapter(app.apiClient.axios);
|
|
239
|
+
app.dataSourceManager.ensureLoaded = async () => {};
|
|
240
|
+
apiMock.onGet('app:getLang').reply(200, {
|
|
241
|
+
data: { lang: 'en-US', resources: { client: {} }, cron: {} },
|
|
242
|
+
});
|
|
243
|
+
apiMock.onGet('/auth:check').reply(() => authCheckResponse);
|
|
244
|
+
apiMock.onGet('app:getInfo').reply(200, { data: { id: 'mock-app', version: 'test' } });
|
|
245
|
+
apiMock.onGet('systemSettings:get').reply(200, {
|
|
246
|
+
data: { id: 1, title: 'NocoBase', raw_title: 'NocoBase', logo: null },
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
const Root = app.getRootComponent();
|
|
250
|
+
render(<Root />);
|
|
251
|
+
|
|
252
|
+
await waitFor(() => {
|
|
253
|
+
expect(apiMock.history.get.some((request) => request.url === '/auth:check')).toBe(true);
|
|
254
|
+
});
|
|
255
|
+
expect(screen.queryByRole('banner')).not.toBeInTheDocument();
|
|
256
|
+
|
|
257
|
+
resolveAuthCheck([200, { data: { id: 1 } }]);
|
|
258
|
+
expect(await screen.findByRole('banner')).toBeInTheDocument();
|
|
259
|
+
});
|
|
260
|
+
});
|