@nocobase/client-v2 2.2.0-beta.9 → 3.0.0-alpha.1
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/APIClient.d.ts +1 -3
- package/es/BaseApplication.d.ts +1 -0
- package/es/acl/ACLProvider.d.ts +10 -0
- package/es/authRedirect.d.ts +5 -0
- package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
- package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
- package/es/components/AttachmentUpload.d.ts +51 -0
- package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
- package/es/components/category-tabs/index.d.ts +9 -0
- package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
- package/es/components/form/TypedVariableInput.d.ts +31 -5
- package/es/components/form/filter/CollectionFilter.d.ts +2 -0
- package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
- package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
- package/es/components/form/filter/index.d.ts +2 -0
- package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
- package/es/components/index.d.ts +2 -0
- package/es/flow/actions/index.d.ts +1 -1
- package/es/flow/actions/linkageRules.d.ts +3 -0
- package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
- package/es/flow/admin-shell/admin-layout/NocoBaseLogo.d.ts +10 -0
- package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
- package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
- package/es/flow/components/FlowRoute.d.ts +2 -2
- package/es/flow/components/RunJSValueEditor.d.ts +1 -0
- package/es/flow/components/filter/FilterGroup.d.ts +1 -0
- package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
- package/es/flow/index.d.ts +1 -0
- 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/base/PageModel/PageModel.d.ts +12 -0
- package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
- package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
- package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
- package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
- package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
- package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
- package/es/flow-compat/routeTypes.d.ts +1 -0
- package/es/index.d.ts +8 -1
- package/es/index.mjs +190 -141
- 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/es/utils/getRouteRuntimeVersion.d.ts +23 -0
- package/es/utils/index.d.ts +1 -0
- package/lib/index.js +195 -146
- package/lib/locale/languageCodes.js +3 -1
- package/package.json +9 -7
- package/src/APIClient.ts +1 -10
- package/src/Application.tsx +4 -0
- package/src/BaseApplication.tsx +11 -6
- package/src/__tests__/app.test.tsx +73 -0
- package/src/__tests__/authRedirect.test.ts +137 -0
- package/src/__tests__/browserChecker.test.ts +185 -6
- package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
- package/src/__tests__/mockSettingsApplication.ts +29 -0
- package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +79 -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/collection-field-interface/CollectionFieldInterface.ts +1 -0
- package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
- package/src/collection-manager/field-validation.ts +1 -1
- package/src/components/AttachmentUpload.tsx +275 -0
- package/src/components/README.md +7 -1
- package/src/components/README.zh-CN.md +6 -1
- package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
- package/src/components/category-tabs/index.ts +10 -0
- package/src/components/form/JsonTextArea.tsx +4 -0
- package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
- package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
- package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
- package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
- package/src/components/form/TypedVariableInput.tsx +452 -101
- package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
- package/src/components/form/filter/CollectionFilter.tsx +4 -0
- package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
- package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
- package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
- package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
- package/src/components/form/filter/index.ts +2 -0
- package/src/components/form/filter/useFilterActionProps.ts +37 -6
- package/src/components/index.ts +2 -0
- package/src/flow/FlowPage.tsx +9 -1
- package/src/flow/__tests__/FlowPage.test.tsx +50 -3
- package/src/flow/__tests__/FlowRoute.test.tsx +128 -10
- 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/__tests__/linkageRules.tab.test.ts +171 -0
- package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
- package/src/flow/actions/index.ts +2 -0
- package/src/flow/actions/linkageRules.tsx +111 -16
- package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
- package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +1 -57
- 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__/AdminLayoutComponent.test.tsx +253 -6
- 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/FieldAssignExactDatePicker.tsx +25 -11
- package/src/flow/components/FieldAssignValueInput.tsx +91 -20
- package/src/flow/components/FlowRoute.tsx +78 -32
- package/src/flow/components/RunJSValueEditor.tsx +9 -1
- package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
- package/src/flow/components/filter/FilterGroup.tsx +33 -5
- package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
- package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
- package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
- package/src/flow/index.ts +3 -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/PageModel.tsx +387 -53
- package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
- package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
- package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
- package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
- package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
- 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 +28 -3
- package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
- package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
- package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
- package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
- package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
- package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
- package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
- package/src/flow/models/blocks/table/TableBlockModel.tsx +4 -2
- 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/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +2 -1
- 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/VariableFieldFormModel.tsx +3 -3
- package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
- package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
- package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
- package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
- package/src/flow/models/topbar/TopbarActionModel.tsx +52 -180
- package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
- package/src/flow-compat/fieldValidationConstants.ts +1 -1
- package/src/flow-compat/routeTypes.ts +1 -0
- package/src/index.ts +14 -1
- package/src/locale/languageCodes.ts +3 -1
- package/src/nocobase-buildin-plugin/currentUserAuthStatus.ts +58 -0
- package/src/nocobase-buildin-plugin/index.tsx +110 -70
- 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
- package/src/utils/getRouteRuntimeVersion.ts +185 -0
- package/src/utils/index.tsx +1 -0
|
@@ -64,6 +64,7 @@ const languageCodes = {
|
|
|
64
64
|
"ml-IN": { label: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02" },
|
|
65
65
|
"mn-MN": { label: "\u041C\u043E\u043D\u0433\u043E\u043B \u0445\u044D\u043B" },
|
|
66
66
|
"ms-MY": { label: "\u0628\u0647\u0627\u0633 \u0645\u0644\u0627\u064A\u0648" },
|
|
67
|
+
"my-MM": { label: "\u1019\u103C\u1014\u103A\u1019\u102C\u1018\u102C\u101E\u102C" },
|
|
67
68
|
"nb-NO": { label: "Norsk bokm\xE5l" },
|
|
68
69
|
"ne-NP": { label: "\u0928\u0947\u092A\u093E\u0932\u0940" },
|
|
69
70
|
"nl-BE": { label: "Vlaams" },
|
|
@@ -83,7 +84,8 @@ const languageCodes = {
|
|
|
83
84
|
"tk-TK": { label: "Turkmen" },
|
|
84
85
|
"tr-TR": { label: "T\xFCrk\xE7e" },
|
|
85
86
|
"uk-UA": { label: "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0430" },
|
|
86
|
-
"ur-PK": { label: "
|
|
87
|
+
"ur-PK": { label: "\u0627\u0631\u062F\u0648" },
|
|
88
|
+
"uz-UZ": { label: "O\u02BBzbekcha" },
|
|
87
89
|
"vi-VN": { label: "Ti\u1EBFng Vi\u1EC7t" },
|
|
88
90
|
"zh-CN": { label: "\u7B80\u4F53\u4E2D\u6587" },
|
|
89
91
|
"zh-HK": { label: "\u7E41\u9AD4\u4E2D\u6587\uFF08\u9999\u6E2F\uFF09" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nocobase/client-v2",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-alpha.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"module": "es/index.mjs",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"@formily/antd-v5": "1.2.3",
|
|
28
28
|
"@formily/react": "^2.2.27",
|
|
29
29
|
"@formily/shared": "^2.2.27",
|
|
30
|
-
"@nocobase/evaluators": "
|
|
31
|
-
"@nocobase/flow-engine": "
|
|
32
|
-
"@nocobase/sdk": "
|
|
33
|
-
"@nocobase/shared": "
|
|
34
|
-
"@nocobase/utils": "
|
|
30
|
+
"@nocobase/evaluators": "3.0.0-alpha.1",
|
|
31
|
+
"@nocobase/flow-engine": "3.0.0-alpha.1",
|
|
32
|
+
"@nocobase/sdk": "3.0.0-alpha.1",
|
|
33
|
+
"@nocobase/shared": "3.0.0-alpha.1",
|
|
34
|
+
"@nocobase/utils": "3.0.0-alpha.1",
|
|
35
35
|
"ahooks": "^3.7.2",
|
|
36
36
|
"antd": "5.24.2",
|
|
37
37
|
"antd-style": "3.7.1",
|
|
@@ -42,9 +42,11 @@
|
|
|
42
42
|
"html5-qrcode": "^2.3.8",
|
|
43
43
|
"i18next": "^22.4.9",
|
|
44
44
|
"json5": "^2.2.3",
|
|
45
|
+
"jsqr": "^1.4.0",
|
|
45
46
|
"lodash": "4.17.21",
|
|
47
|
+
"react-device-detect": "2.2.3",
|
|
46
48
|
"react-i18next": "^11.15.1",
|
|
47
49
|
"react-router-dom": "^6.30.1"
|
|
48
50
|
},
|
|
49
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "22d8be8ece179cfa5c8a4ebb2c896c92bd418e03"
|
|
50
52
|
}
|
package/src/APIClient.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { APIClient as APIClientSDK,
|
|
10
|
+
import { APIClient as APIClientSDK, hasHeaderValue } from '@nocobase/sdk';
|
|
11
11
|
|
|
12
12
|
function offsetToTimeZone(offset: number) {
|
|
13
13
|
const hours = Math.floor(Math.abs(offset));
|
|
@@ -23,15 +23,6 @@ function getCurrentTimezone() {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
export class APIClient extends APIClientSDK {
|
|
26
|
-
appName?: string;
|
|
27
|
-
|
|
28
|
-
constructor(options?: APIClientOptions) {
|
|
29
|
-
super(options);
|
|
30
|
-
if (options && typeof options !== 'function') {
|
|
31
|
-
this.appName = options.appName;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
26
|
getHostname() {
|
|
36
27
|
if (process.env.API_BASE_URL) {
|
|
37
28
|
try {
|
package/src/Application.tsx
CHANGED
|
@@ -46,6 +46,10 @@ export class Application extends BaseApplication<
|
|
|
46
46
|
|
|
47
47
|
protected createApiClient(options: ApplicationOptions) {
|
|
48
48
|
return new APIClient({
|
|
49
|
+
// Cross-origin API deployments rely on cookies for auth (e.g. permanent file
|
|
50
|
+
// URLs); trust is enforced server-side via the CORS origin whitelist and the
|
|
51
|
+
// CSRF middleware, not by omitting credentials.
|
|
52
|
+
withCredentials: true,
|
|
49
53
|
...options.apiClient,
|
|
50
54
|
appName: options.name || getSubAppName(options.publicPath),
|
|
51
55
|
});
|
package/src/BaseApplication.tsx
CHANGED
|
@@ -35,6 +35,7 @@ import { SystemSettingsSource } from './flow/system-settings';
|
|
|
35
35
|
import { LayoutManager } from './layout-manager/LayoutManager';
|
|
36
36
|
import type { PluginClass, PluginManager, PluginType } from './PluginManager';
|
|
37
37
|
import { RouteRepository } from './RouteRepository';
|
|
38
|
+
import { stripModernClientPrefix } from './authRedirect';
|
|
38
39
|
import type {
|
|
39
40
|
ComponentTypeAndString,
|
|
40
41
|
RenderableComponentType,
|
|
@@ -81,6 +82,10 @@ const trimTrailingSlashes = (value: string) => {
|
|
|
81
82
|
return match ? value.slice(0, -match[0].length) : value;
|
|
82
83
|
};
|
|
83
84
|
|
|
85
|
+
const ensureTrailingSlash = (value: string) => {
|
|
86
|
+
return `${trimTrailingSlashes(value)}/`;
|
|
87
|
+
};
|
|
88
|
+
|
|
84
89
|
const isRenderableComponentType = (value: unknown): value is AnyComponent => isValidElementType(value);
|
|
85
90
|
|
|
86
91
|
export type DevDynamicImport = (packageName: string) => Promise<{ default: PluginClass }>;
|
|
@@ -398,7 +403,7 @@ export abstract class BaseApplication<
|
|
|
398
403
|
this.favicon = favicon || '';
|
|
399
404
|
}
|
|
400
405
|
|
|
401
|
-
const iconHref = this.favicon || '
|
|
406
|
+
const iconHref = this.favicon || this.getCdnUrl() + 'favicon/favicon.ico';
|
|
402
407
|
|
|
403
408
|
if (faviconLinkElement) {
|
|
404
409
|
faviconLinkElement.href = iconHref;
|
|
@@ -433,12 +438,12 @@ export abstract class BaseApplication<
|
|
|
433
438
|
return getSubAppName(this.getPublicPath()) || null;
|
|
434
439
|
}
|
|
435
440
|
|
|
441
|
+
getCdnUrl() {
|
|
442
|
+
return ensureTrailingSlash(window['__webpack_public_path__'] || stripModernClientPrefix(this.getPublicPath()));
|
|
443
|
+
}
|
|
444
|
+
|
|
436
445
|
getPublicPath() {
|
|
437
|
-
|
|
438
|
-
if (!publicPath.endsWith('/')) {
|
|
439
|
-
publicPath += '/';
|
|
440
|
-
}
|
|
441
|
-
return publicPath;
|
|
446
|
+
return ensureTrailingSlash(this.options.publicPath || '/');
|
|
442
447
|
}
|
|
443
448
|
|
|
444
449
|
getApiUrl(pathname = '') {
|
|
@@ -34,6 +34,8 @@ describe('app', () => {
|
|
|
34
34
|
afterEach(() => {
|
|
35
35
|
document.querySelectorAll('link[rel="shortcut icon"]').forEach((node) => node.remove());
|
|
36
36
|
document.documentElement.removeAttribute('lang');
|
|
37
|
+
delete window['__webpack_public_path__'];
|
|
38
|
+
delete window['__nocobase_modern_client_prefix__'];
|
|
37
39
|
vi.restoreAllMocks();
|
|
38
40
|
});
|
|
39
41
|
|
|
@@ -50,6 +52,24 @@ describe('app', () => {
|
|
|
50
52
|
expect(app.getHref('/test')).toBe('/test');
|
|
51
53
|
});
|
|
52
54
|
|
|
55
|
+
it('should enable credentials for api clients by default', () => {
|
|
56
|
+
const sameOriginApp = new Application({
|
|
57
|
+
router,
|
|
58
|
+
apiClient: {
|
|
59
|
+
baseURL: '/api/',
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
const crossOriginApp = new Application({
|
|
63
|
+
router,
|
|
64
|
+
apiClient: {
|
|
65
|
+
baseURL: 'https://api.example.com/api/',
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(sameOriginApp.apiClient.axios.defaults.withCredentials).toBe(true);
|
|
70
|
+
expect(crossOriginApp.apiClient.axios.defaults.withCredentials).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
53
73
|
it('should initialize shared jsonLogic operators', () => {
|
|
54
74
|
const app = new Application({ router });
|
|
55
75
|
|
|
@@ -58,6 +78,59 @@ describe('app', () => {
|
|
|
58
78
|
expect(app.jsonLogic.apply({ $testAlwaysTrue: [] })).toBe(true);
|
|
59
79
|
});
|
|
60
80
|
|
|
81
|
+
it('should normalize publicPath and webpack public path with a single trailing slash', () => {
|
|
82
|
+
const app = new Application({
|
|
83
|
+
router,
|
|
84
|
+
publicPath: '/admin//',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(app.getPublicPath()).toBe('/admin/');
|
|
88
|
+
|
|
89
|
+
window['__webpack_public_path__'] = '/cdn/assets///';
|
|
90
|
+
expect(app.getCdnUrl()).toBe('/cdn/assets/');
|
|
91
|
+
|
|
92
|
+
delete window['__webpack_public_path__'];
|
|
93
|
+
expect(app.getCdnUrl()).toBe('/admin/');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('should normalize webpack public path without a trailing slash', () => {
|
|
97
|
+
const app = new Application({
|
|
98
|
+
router,
|
|
99
|
+
publicPath: '/admin/',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
window['__webpack_public_path__'] = '/cdn/assets';
|
|
103
|
+
expect(app.getCdnUrl()).toBe('/cdn/assets/');
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should remove the modern client prefix from the CDN fallback path', () => {
|
|
107
|
+
const app = new Application({
|
|
108
|
+
router,
|
|
109
|
+
publicPath: '/v/',
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(app.getCdnUrl()).toBe('/');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should preserve APP_PUBLIC_PATH when removing the modern client prefix', () => {
|
|
116
|
+
const app = new Application({
|
|
117
|
+
router,
|
|
118
|
+
publicPath: '/nocobase/v/',
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(app.getCdnUrl()).toBe('/nocobase/');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should support a custom modern client prefix', () => {
|
|
125
|
+
window['__nocobase_modern_client_prefix__'] = 'modern';
|
|
126
|
+
const app = new Application({
|
|
127
|
+
router,
|
|
128
|
+
publicPath: '/nocobase/modern/',
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(app.getCdnUrl()).toBe('/nocobase/');
|
|
132
|
+
});
|
|
133
|
+
|
|
61
134
|
it('should apply the provided favicon immediately', () => {
|
|
62
135
|
const app = new Application({ router });
|
|
63
136
|
|
|
@@ -17,12 +17,18 @@ import {
|
|
|
17
17
|
|
|
18
18
|
describe('auth redirect helpers', () => {
|
|
19
19
|
const originalLocation = globalThis.window.location;
|
|
20
|
+
const originalModernClientPrefix = window.__nocobase_modern_client_prefix__;
|
|
20
21
|
|
|
21
22
|
afterEach(() => {
|
|
22
23
|
Object.defineProperty(globalThis.window, 'location', {
|
|
23
24
|
configurable: true,
|
|
24
25
|
value: originalLocation,
|
|
25
26
|
});
|
|
27
|
+
if (originalModernClientPrefix === undefined) {
|
|
28
|
+
delete window.__nocobase_modern_client_prefix__;
|
|
29
|
+
} else {
|
|
30
|
+
window.__nocobase_modern_client_prefix__ = originalModernClientPrefix;
|
|
31
|
+
}
|
|
26
32
|
vi.restoreAllMocks();
|
|
27
33
|
});
|
|
28
34
|
|
|
@@ -84,6 +90,107 @@ describe('auth redirect helpers', () => {
|
|
|
84
90
|
expect(buildV2SigninHref(app, '/v2/admin/7vu4c2sdk6h')).toBe('/v2/signin?redirect=%2Fv2%2Fadmin%2F7vu4c2sdk6h');
|
|
85
91
|
});
|
|
86
92
|
|
|
93
|
+
it('should keep validated standalone settings redirects outside the v2 basename', () => {
|
|
94
|
+
const app = {
|
|
95
|
+
getPublicPath: () => '/nocobase/v2/',
|
|
96
|
+
router: {
|
|
97
|
+
getBasename: () => '/nocobase/v2',
|
|
98
|
+
},
|
|
99
|
+
} as any;
|
|
100
|
+
|
|
101
|
+
expect(normalizeV2RedirectPath(app, '/nocobase/settings/workflow?tab=list#recent')).toBe(
|
|
102
|
+
'/nocobase/settings/workflow?tab=list#recent',
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should build the standalone settings signin URL for a standalone settings app', () => {
|
|
107
|
+
const app = {
|
|
108
|
+
name: 'main',
|
|
109
|
+
getPublicPath: () => '/nocobase/',
|
|
110
|
+
router: {
|
|
111
|
+
getBasename: () => '/nocobase',
|
|
112
|
+
},
|
|
113
|
+
pluginSettingsManager: {
|
|
114
|
+
getRouteName: () => 'settings.',
|
|
115
|
+
getRoutePath: () => '/settings/',
|
|
116
|
+
},
|
|
117
|
+
} satisfies Parameters<typeof buildV2SigninHref>[0];
|
|
118
|
+
expect(buildV2SigninHref(app, '/nocobase/settings/workflow?tab=list#recent')).toBe(
|
|
119
|
+
'/nocobase/settings/signin?redirect=%2Fnocobase%2Fsettings%2Fworkflow%3Ftab%3Dlist%23recent',
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should keep an apps segment inside the main public path out of the Settings application scope', () => {
|
|
124
|
+
const app = {
|
|
125
|
+
name: 'main',
|
|
126
|
+
getPublicPath: () => '/tenant/apps/root/',
|
|
127
|
+
router: {
|
|
128
|
+
getBasename: () => '/tenant/apps/root/',
|
|
129
|
+
},
|
|
130
|
+
pluginSettingsManager: {
|
|
131
|
+
getRouteName: () => 'settings.',
|
|
132
|
+
getRoutePath: () => '/settings/',
|
|
133
|
+
},
|
|
134
|
+
} satisfies Parameters<typeof buildV2SigninHref>[0];
|
|
135
|
+
|
|
136
|
+
expect(buildV2SigninHref(app, '/tenant/apps/root/settings/workflow')).toBe(
|
|
137
|
+
'/tenant/apps/root/settings/signin?redirect=%2Ftenant%2Fapps%2Froot%2Fsettings%2Fworkflow',
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it.each([
|
|
142
|
+
['/nocobase/settings/apps/test-app/', '/nocobase/settings/apps/test-app/signin'],
|
|
143
|
+
['/nocobase/settings/_app/test-app/', '/nocobase/settings/_app/test-app/signin'],
|
|
144
|
+
])('should keep the standalone settings signin in the current sub-app scope: %s', (basename, signinPath) => {
|
|
145
|
+
const app = {
|
|
146
|
+
name: 'test-app',
|
|
147
|
+
getPublicPath: () => '/nocobase/',
|
|
148
|
+
router: {
|
|
149
|
+
getBasename: () => basename,
|
|
150
|
+
},
|
|
151
|
+
pluginSettingsManager: {
|
|
152
|
+
getRouteName: () => 'settings.',
|
|
153
|
+
getRoutePath: () => '/',
|
|
154
|
+
},
|
|
155
|
+
} as any;
|
|
156
|
+
|
|
157
|
+
expect(buildV2SigninHref(app, `${basename}workflow`)).toBe(
|
|
158
|
+
`${signinPath}?redirect=${encodeURIComponent(`${basename}workflow`)}`,
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should accept the current standalone settings signin URL only', () => {
|
|
163
|
+
Object.defineProperty(globalThis.window, 'location', {
|
|
164
|
+
configurable: true,
|
|
165
|
+
value: {
|
|
166
|
+
...originalLocation,
|
|
167
|
+
origin: 'http://localhost:20000',
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
const app = {
|
|
171
|
+
name: 'test-app',
|
|
172
|
+
getPublicPath: () => '/nocobase/',
|
|
173
|
+
router: {
|
|
174
|
+
getBasename: () => '/nocobase/settings/apps/test-app/',
|
|
175
|
+
},
|
|
176
|
+
pluginSettingsManager: {
|
|
177
|
+
getRouteName: () => 'settings.',
|
|
178
|
+
getRoutePath: () => '/',
|
|
179
|
+
},
|
|
180
|
+
} as any;
|
|
181
|
+
|
|
182
|
+
expect(
|
|
183
|
+
resolveV2SigninRedirect(
|
|
184
|
+
'/nocobase/settings/apps/test-app/signin?redirect=%2Fnocobase%2Fsettings%2Fapps%2Ftest-app',
|
|
185
|
+
app,
|
|
186
|
+
),
|
|
187
|
+
).toBe(
|
|
188
|
+
'http://localhost:20000/nocobase/settings/apps/test-app/signin?redirect=%2Fnocobase%2Fsettings%2Fapps%2Ftest-app',
|
|
189
|
+
);
|
|
190
|
+
expect(resolveV2SigninRedirect('/nocobase/v/apps/test-app/signin', app)).toBeNull();
|
|
191
|
+
expect(resolveV2SigninRedirect('/nocobase/settings/apps/other-app/signin', app)).toBeNull();
|
|
192
|
+
});
|
|
193
|
+
|
|
87
194
|
it('should redirect with window.location.replace by default', () => {
|
|
88
195
|
const replace = vi.fn();
|
|
89
196
|
Object.defineProperty(globalThis.window, 'location', {
|
|
@@ -134,6 +241,36 @@ describe('auth redirect helpers', () => {
|
|
|
134
241
|
});
|
|
135
242
|
|
|
136
243
|
describe('v2 sub-app context (router basename contains /apps/<id>/)', () => {
|
|
244
|
+
it('should keep only the current sub-app standalone settings redirect', () => {
|
|
245
|
+
const app = {
|
|
246
|
+
getPublicPath: () => '/nocobase/v2/',
|
|
247
|
+
router: {
|
|
248
|
+
getBasename: () => '/nocobase/v2/apps/test-app/',
|
|
249
|
+
},
|
|
250
|
+
} as any;
|
|
251
|
+
|
|
252
|
+
expect(normalizeV2RedirectPath(app, '/nocobase/settings/apps/test-app/workflow')).toBe(
|
|
253
|
+
'/nocobase/settings/apps/test-app/workflow',
|
|
254
|
+
);
|
|
255
|
+
expect(normalizeV2RedirectPath(app, '/nocobase/settings/apps/other-app/workflow')).toBe(
|
|
256
|
+
'/nocobase/v2/apps/test-app/admin/',
|
|
257
|
+
);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it.each([
|
|
261
|
+
'/nocobase/settings/apps/test-app/../../other-app/settings',
|
|
262
|
+
'/nocobase/settings/apps/test-app/%2e%2e/%2E%2e/other-app/settings',
|
|
263
|
+
])('should reject a standalone settings redirect that resolves outside the current sub-app: %s', (target) => {
|
|
264
|
+
const app = {
|
|
265
|
+
getPublicPath: () => '/nocobase/v2/',
|
|
266
|
+
router: {
|
|
267
|
+
getBasename: () => '/nocobase/v2/apps/test-app/',
|
|
268
|
+
},
|
|
269
|
+
} as any;
|
|
270
|
+
|
|
271
|
+
expect(normalizeV2RedirectPath(app, target)).toBe('/nocobase/v2/apps/test-app/admin/');
|
|
272
|
+
});
|
|
273
|
+
|
|
137
274
|
it('should normalize signin redirect fallback under the current sub-app basename', () => {
|
|
138
275
|
const app = {
|
|
139
276
|
getPublicPath: () => '/v/',
|
|
@@ -15,6 +15,11 @@ import { fileURLToPath } from 'node:url';
|
|
|
15
15
|
type BrowserCheckerCase = {
|
|
16
16
|
pathname: string;
|
|
17
17
|
publicPath: string;
|
|
18
|
+
search?: string;
|
|
19
|
+
hash?: string;
|
|
20
|
+
modernClientPrefix?: string;
|
|
21
|
+
appClientEntryMode?: string;
|
|
22
|
+
scriptSrc?: string;
|
|
18
23
|
expectedRedirect?: string;
|
|
19
24
|
};
|
|
20
25
|
|
|
@@ -40,11 +45,13 @@ function executeBrowserChecker(scriptPath: string, input: BrowserCheckerCase) {
|
|
|
40
45
|
showLog: false,
|
|
41
46
|
window: {
|
|
42
47
|
__nocobase_public_path__: input.publicPath,
|
|
48
|
+
__nocobase_modern_client_prefix__: input.modernClientPrefix,
|
|
49
|
+
__nocobase_app_client_entry_mode__: input.appClientEntryMode,
|
|
43
50
|
location: {
|
|
44
51
|
origin: 'http://c.local.nocobase.com',
|
|
45
52
|
pathname: input.pathname,
|
|
46
|
-
search: '',
|
|
47
|
-
hash: '',
|
|
53
|
+
search: input.search || '',
|
|
54
|
+
hash: input.hash || '',
|
|
48
55
|
replace,
|
|
49
56
|
},
|
|
50
57
|
console: consoleMock,
|
|
@@ -54,6 +61,13 @@ function executeBrowserChecker(scriptPath: string, input: BrowserCheckerCase) {
|
|
|
54
61
|
onresize: undefined,
|
|
55
62
|
},
|
|
56
63
|
document: {
|
|
64
|
+
currentScript: {
|
|
65
|
+
src:
|
|
66
|
+
input.scriptSrc ||
|
|
67
|
+
(scriptPath.includes('/app/client-v2/public/')
|
|
68
|
+
? 'http://assets.local.nocobase.com/v/browser-checker.js?v=1'
|
|
69
|
+
: 'http://assets.local.nocobase.com/browser-checker.js?v=1'),
|
|
70
|
+
},
|
|
57
71
|
documentElement: {
|
|
58
72
|
className: '',
|
|
59
73
|
clientWidth: 1280,
|
|
@@ -76,11 +90,11 @@ function executeBrowserChecker(scriptPath: string, input: BrowserCheckerCase) {
|
|
|
76
90
|
describe.each(browserCheckerCases)('$label', ({ scriptPath }) => {
|
|
77
91
|
it('normalizes a relative public path before redirecting to the trailing-slash entry', () => {
|
|
78
92
|
const replace = executeBrowserChecker(scriptPath, {
|
|
79
|
-
pathname: '/nocobase/
|
|
80
|
-
publicPath: 'nocobase/
|
|
93
|
+
pathname: '/nocobase/console',
|
|
94
|
+
publicPath: 'nocobase/console/',
|
|
81
95
|
});
|
|
82
96
|
|
|
83
|
-
expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/nocobase/
|
|
97
|
+
expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/nocobase/console/');
|
|
84
98
|
});
|
|
85
99
|
|
|
86
100
|
it('prefixes outside paths with a root-relative basename instead of duplicating a relative segment', () => {
|
|
@@ -94,7 +108,7 @@ describe.each(browserCheckerCases)('$label', ({ scriptPath }) => {
|
|
|
94
108
|
|
|
95
109
|
it('does not redirect when the current path is already under the normalized basename', () => {
|
|
96
110
|
const replace = executeBrowserChecker(scriptPath, {
|
|
97
|
-
pathname: '/nocobase/v/',
|
|
111
|
+
pathname: '/nocobase/v/admin',
|
|
98
112
|
publicPath: 'nocobase/v/',
|
|
99
113
|
});
|
|
100
114
|
|
|
@@ -109,4 +123,169 @@ describe.each(browserCheckerCases)('$label', ({ scriptPath }) => {
|
|
|
109
123
|
|
|
110
124
|
expect(replace).not.toHaveBeenCalled();
|
|
111
125
|
});
|
|
126
|
+
|
|
127
|
+
if (scriptPath.includes('/app/client/public/')) {
|
|
128
|
+
it('redirects app root to modern entry for modern-default', () => {
|
|
129
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
130
|
+
pathname: '/',
|
|
131
|
+
publicPath: '/',
|
|
132
|
+
modernClientPrefix: 'v',
|
|
133
|
+
appClientEntryMode: 'modern-default',
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/v/');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('does not redirect legacy deep links for modern-default', () => {
|
|
140
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
141
|
+
pathname: '/admin',
|
|
142
|
+
publicPath: '/',
|
|
143
|
+
modernClientPrefix: 'v',
|
|
144
|
+
appClientEntryMode: 'modern-default',
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(replace).not.toHaveBeenCalled();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('rewrites legacy document paths for modern-only', () => {
|
|
151
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
152
|
+
pathname: '/admin/settings/workflow',
|
|
153
|
+
publicPath: '/',
|
|
154
|
+
modernClientPrefix: 'v',
|
|
155
|
+
appClientEntryMode: 'modern-only',
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/v/admin/settings/workflow');
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('redirects sub-path site root directly to final modern target', () => {
|
|
162
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
163
|
+
pathname: '/nocobase/',
|
|
164
|
+
publicPath: '/nocobase/',
|
|
165
|
+
modernClientPrefix: 'v',
|
|
166
|
+
appClientEntryMode: 'modern-default',
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/nocobase/v/');
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it('rewrites sub-app legacy deep links for modern-only without collapsing the sub-app segment', () => {
|
|
173
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
174
|
+
pathname: '/nocobase/apps/a_31itq60q4kg/admin/',
|
|
175
|
+
publicPath: '/nocobase/',
|
|
176
|
+
modernClientPrefix: 'v',
|
|
177
|
+
appClientEntryMode: 'modern-only',
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/nocobase/v/apps/a_31itq60q4kg/admin');
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (scriptPath.includes('/app/client-v2/public/')) {
|
|
185
|
+
it.each(['/v', '/v/'])('redirects the main modern root %s to Settings', (pathname) => {
|
|
186
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
187
|
+
pathname,
|
|
188
|
+
publicPath: '/v/',
|
|
189
|
+
modernClientPrefix: 'v',
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
expect(replace).toHaveBeenCalledOnce();
|
|
193
|
+
expect(replace).toHaveBeenCalledWith('http://c.local.nocobase.com/settings');
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it.each([
|
|
197
|
+
['/v/apps/demo', '/settings/apps/demo'],
|
|
198
|
+
['/v/apps/demo/', '/settings/apps/demo'],
|
|
199
|
+
['/v/_app/demo', '/settings/_app/demo'],
|
|
200
|
+
['/v/_app/demo/', '/settings/_app/demo'],
|
|
201
|
+
])('redirects the scoped modern root %s to %s', (pathname, expectedPathname) => {
|
|
202
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
203
|
+
pathname,
|
|
204
|
+
publicPath: '/v/',
|
|
205
|
+
modernClientPrefix: 'v',
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expect(replace).toHaveBeenCalledOnce();
|
|
209
|
+
expect(replace).toHaveBeenCalledWith(`http://c.local.nocobase.com${expectedPathname}`);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it.each([
|
|
213
|
+
['/settings/apps/demo', '/', '/settings/apps/demo/', 'v'],
|
|
214
|
+
['/settings/_app/demo', '/', '/settings/_app/demo/', 'v'],
|
|
215
|
+
['/nocobase/settings/apps/demo', '/nocobase/', '/nocobase/settings/apps/demo/', 'v'],
|
|
216
|
+
['/nocobase/settings/_app/demo', '/nocobase/', '/nocobase/settings/_app/demo/', 'v'],
|
|
217
|
+
['/tenant/apps/root/settings/apps/demo', '/tenant/apps/root/', '/tenant/apps/root/settings/apps/demo/', 'v'],
|
|
218
|
+
['/tenant/_app/root/settings/_app/demo', '/tenant/_app/root/', '/tenant/_app/root/settings/_app/demo/', 'v'],
|
|
219
|
+
['/tenant/v/settings/apps/demo', '/tenant/v/', '/tenant/v/settings/apps/demo/', 'v'],
|
|
220
|
+
['/tenant/modern/settings/_app/demo', '/tenant/modern/', '/tenant/modern/settings/_app/demo/', 'modern'],
|
|
221
|
+
])('normalizes the scoped Settings root %s to %s', (pathname, publicPath, expectedPathname, modernClientPrefix) => {
|
|
222
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
223
|
+
pathname,
|
|
224
|
+
publicPath,
|
|
225
|
+
modernClientPrefix,
|
|
226
|
+
scriptSrc: 'https://cdn.example.com/ui/settings/browser-checker.js?v=1',
|
|
227
|
+
search: '?tab=overview',
|
|
228
|
+
hash: '#panel',
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
expect(replace).toHaveBeenCalledOnce();
|
|
232
|
+
expect(replace).toHaveBeenCalledWith(`http://c.local.nocobase.com${expectedPathname}?tab=overview#panel`);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it.each([
|
|
236
|
+
['/tenant/apps/root/modern/apps/demo/', '/tenant/apps/root/modern/', '/tenant/apps/root/settings/apps/demo'],
|
|
237
|
+
['/tenant/_app/root/modern/_app/demo/', '/tenant/_app/root/modern/', '/tenant/_app/root/settings/_app/demo'],
|
|
238
|
+
])(
|
|
239
|
+
'preserves public path, query, and hash for %s without treating public-path segments as app scope',
|
|
240
|
+
(pathname, publicPath, expectedPathname) => {
|
|
241
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
242
|
+
pathname,
|
|
243
|
+
publicPath,
|
|
244
|
+
modernClientPrefix: 'modern',
|
|
245
|
+
search: '?tab=overview',
|
|
246
|
+
hash: '#panel',
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
expect(replace).toHaveBeenCalledOnce();
|
|
250
|
+
expect(replace).toHaveBeenCalledWith(`http://c.local.nocobase.com${expectedPathname}?tab=overview#panel`);
|
|
251
|
+
},
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
it.each([
|
|
255
|
+
['/v/admin', '/v/'],
|
|
256
|
+
['/v/apps/demo/admin', '/v/'],
|
|
257
|
+
['/v/_app/demo/admin', '/v/'],
|
|
258
|
+
['/v/_apps/demo', '/v/'],
|
|
259
|
+
['/v/settings/apps/demo', '/v/'],
|
|
260
|
+
['/nocobase/modern/settings/_app/demo', '/nocobase/modern/', 'modern'],
|
|
261
|
+
])('does not redirect a non-root modern path %s to Settings', (pathname, publicPath, modernClientPrefix = 'v') => {
|
|
262
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
263
|
+
pathname,
|
|
264
|
+
publicPath,
|
|
265
|
+
modernClientPrefix,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
expect(replace).not.toHaveBeenCalled();
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it.each([
|
|
272
|
+
['/settings', '/'],
|
|
273
|
+
['/settings/', '/'],
|
|
274
|
+
['/settings/apps/demo/', '/'],
|
|
275
|
+
['/settings/_app/demo/', '/'],
|
|
276
|
+
['/settings/apps/demo/multi-portal', '/'],
|
|
277
|
+
['/settings/_app/demo/multi-portal', '/'],
|
|
278
|
+
['/settings/_apps/demo', '/'],
|
|
279
|
+
['/nocobase/settings/apps/demo/multi-portal', '/nocobase/'],
|
|
280
|
+
])('does not redirect a non-target Settings path %s', (pathname, publicPath) => {
|
|
281
|
+
const replace = executeBrowserChecker(scriptPath, {
|
|
282
|
+
pathname,
|
|
283
|
+
publicPath,
|
|
284
|
+
modernClientPrefix: 'v',
|
|
285
|
+
scriptSrc: 'https://cdn.example.com/ui/settings/browser-checker.js?v=1',
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
expect(replace).not.toHaveBeenCalled();
|
|
289
|
+
});
|
|
290
|
+
}
|
|
112
291
|
});
|