@qlover/create-app 0.9.0 → 0.10.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/CHANGELOG.md +19 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/templates/next-app/.env.template +9 -10
- package/dist/templates/next-app/eslint.config.mjs +5 -0
- package/dist/templates/next-app/migrations/schema/UserSchema.ts +17 -3
- package/dist/templates/next-app/next.config.ts +1 -1
- package/dist/templates/next-app/package.json +2 -3
- package/dist/templates/next-app/src/app/[locale]/login/page.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/page.tsx +1 -1
- package/dist/templates/next-app/src/app/[locale]/register/page.tsx +1 -1
- package/dist/templates/next-app/src/app/api/locales/json/route.ts +2 -1
- package/dist/templates/next-app/src/base/cases/DialogHandler.ts +1 -2
- package/dist/templates/next-app/src/base/cases/RequestEncryptPlugin.ts +4 -5
- package/dist/templates/next-app/src/base/cases/UserServiceApi.ts +44 -29
- package/dist/templates/next-app/src/base/cases/ZodColumnBuilder.ts +1 -2
- package/dist/templates/next-app/src/base/port/AppUserApiInterface.ts +22 -10
- package/dist/templates/next-app/src/base/port/UserServiceInterface.ts +17 -9
- package/dist/templates/next-app/src/base/services/ResourceService.ts +3 -4
- package/dist/templates/next-app/src/base/services/UserService.ts +37 -13
- package/dist/templates/next-app/src/base/services/appApi/AppApiRequester.ts +8 -7
- package/dist/templates/next-app/src/base/services/appApi/AppUserApi.ts +15 -26
- package/dist/templates/next-app/src/core/clientIoc/ClientIOC.ts +4 -3
- package/dist/templates/next-app/src/core/clientIoc/ClientIOCRegister.ts +4 -3
- package/dist/templates/next-app/src/core/globals.ts +2 -1
- package/dist/templates/next-app/src/core/serverIoc/ServerIOC.ts +4 -3
- package/dist/templates/next-app/src/core/serverIoc/ServerIOCRegister.ts +5 -6
- package/dist/templates/next-app/src/i18n/request.ts +2 -2
- package/dist/templates/next-app/src/server/UserCredentialToken.ts +1 -3
- package/dist/templates/next-app/src/uikit/components/LocaleLink.tsx +1 -2
- package/dist/templates/react-app/__tests__/__mocks__/{MockAppConfit.ts → MockAppConfig.ts} +1 -1
- package/dist/templates/react-app/__tests__/__mocks__/components/TestApp.tsx +10 -17
- package/dist/templates/react-app/__tests__/__mocks__/components/TestBootstrapsProvider.tsx +27 -8
- package/dist/templates/react-app/__tests__/__mocks__/createMockGlobals.ts +1 -1
- package/dist/templates/react-app/__tests__/__mocks__/i18nextHttpBackend.ts +110 -0
- package/dist/templates/react-app/__tests__/__mocks__/testIOC/TestIOCRegister.ts +3 -2
- package/dist/templates/react-app/__tests__/setup/setupGlobal.ts +13 -0
- package/dist/templates/react-app/__tests__/src/base/services/I18nService.test.ts +3 -1
- package/dist/templates/react-app/config/IOCIdentifier.ts +9 -6
- package/dist/templates/react-app/config/common.ts +38 -0
- package/dist/templates/react-app/config/feapi.mock.json +5 -12
- package/dist/templates/react-app/eslint.config.mjs +6 -5
- package/dist/templates/react-app/package.json +1 -1
- package/dist/templates/react-app/src/base/apis/userApi/UserApi.ts +22 -13
- package/dist/templates/react-app/src/base/apis/userApi/UserApiBootstarp.ts +3 -3
- package/dist/templates/react-app/src/base/apis/userApi/UserApiType.ts +17 -12
- package/dist/templates/react-app/src/base/cases/I18nKeyErrorPlugin.ts +19 -2
- package/dist/templates/react-app/src/base/port/RouteServiceInterface.ts +2 -4
- package/dist/templates/react-app/src/base/port/UserServiceInterface.ts +15 -9
- package/dist/templates/react-app/src/base/services/BaseLayoutService.ts +55 -0
- package/dist/templates/react-app/src/base/services/I18nService.ts +1 -0
- package/dist/templates/react-app/src/base/services/UserBootstrap.ts +43 -0
- package/dist/templates/react-app/src/base/services/UserGatewayPlugin.ts +16 -0
- package/dist/templates/react-app/src/base/services/UserService.ts +51 -80
- package/dist/templates/react-app/src/core/bootstraps/BootstrapClient.ts +8 -3
- package/dist/templates/react-app/src/core/bootstraps/BootstrapsRegistry.ts +6 -6
- package/dist/templates/react-app/src/core/bootstraps/SaveAppInfo.ts +28 -0
- package/dist/templates/react-app/src/core/clientIoc/ClientIOCRegister.ts +24 -18
- package/dist/templates/react-app/src/core/globals.ts +10 -11
- package/dist/templates/react-app/src/main.tsx +1 -1
- package/dist/templates/react-app/src/pages/auth/Layout.tsx +4 -4
- package/dist/templates/react-app/src/pages/auth/RegisterPage.tsx +1 -1
- package/dist/templates/react-app/src/pages/base/Layout.tsx +3 -3
- package/dist/templates/react-app/src/uikit/components/BaseLayoutProvider.tsx +44 -0
- package/dist/templates/react-app/src/uikit/components/LogoutButton.tsx +1 -3
- package/dist/templates/react-app/src/uikit/hooks/useNavigateBridge.ts +9 -0
- package/dist/templates/react-app/src/uikit/hooks/{useI18nGuard.ts → useRouterI18nGuard.ts} +7 -4
- package/dist/templates/react-app/tsconfig.app.json +1 -1
- package/package.json +3 -3
- package/dist/templates/react-app/__tests__/src/base/cases/AppError.test.ts +0 -102
- package/dist/templates/react-app/__tests__/src/main.integration.test.tsx +0 -61
- package/dist/templates/react-app/src/base/services/ProcesserExecutor.ts +0 -57
- package/dist/templates/react-app/src/uikit/components/ProcessExecutorProvider.tsx +0 -28
- package/dist/templates/react-app/src/uikit/components/UserAuthProvider.tsx +0 -16
|
@@ -14,8 +14,8 @@ import { RequestStatusCatcher } from '@/base/cases/RequestStatusCatcher';
|
|
|
14
14
|
import type { IocRegisterOptions } from '@/base/port/IOCInterface';
|
|
15
15
|
import type { UserServiceInterface } from '@/base/port/UserServiceInterface';
|
|
16
16
|
import { I18nService } from '@/base/services/I18nService';
|
|
17
|
-
import { ProcesserExecutor } from '@/base/services/ProcesserExecutor';
|
|
18
17
|
import { RouteService } from '@/base/services/RouteService';
|
|
18
|
+
import { UserGatewayPlugin } from '@/base/services/UserGatewayPlugin';
|
|
19
19
|
import { UserService } from '@/base/services/UserService';
|
|
20
20
|
import { ExecutorPageBridge } from '@/uikit/bridges/ExecutorPageBridge';
|
|
21
21
|
import { JSONStoragePageBridge } from '@/uikit/bridges/JSONStoragePageBridge';
|
|
@@ -28,7 +28,6 @@ import type {
|
|
|
28
28
|
IOCContainerInterface,
|
|
29
29
|
IOCRegisterInterface
|
|
30
30
|
} from '@qlover/corekit-bridge';
|
|
31
|
-
import type { SyncStorageInterface } from '@qlover/fe-corekit';
|
|
32
31
|
import type { LoggerInterface } from '@qlover/logger';
|
|
33
32
|
|
|
34
33
|
export class ClientIOCRegister
|
|
@@ -71,30 +70,34 @@ export class ClientIOCRegister
|
|
|
71
70
|
*/
|
|
72
71
|
protected registerImplement(ioc: IOCContainerInterface): void {
|
|
73
72
|
ioc.bind(I.I18nServiceInterface, new I18nService(this.options.pathname));
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
)
|
|
73
|
+
|
|
74
|
+
const routeService = new RouteService(
|
|
75
|
+
ioc.get(NavigateBridge),
|
|
76
|
+
ioc.get(I.I18nServiceInterface),
|
|
77
|
+
{
|
|
78
|
+
routes: useLocaleRoutes ? baseRoutes : baseNoLocaleRoutes,
|
|
79
|
+
logger: ioc.get(I.Logger),
|
|
80
|
+
hasLocalRoutes: useLocaleRoutes,
|
|
81
|
+
routerPrefix: routerPrefix
|
|
82
|
+
}
|
|
86
83
|
);
|
|
84
|
+
ioc.bind(I.RouteServiceInterface, routeService);
|
|
87
85
|
ioc.bind(
|
|
88
86
|
I.ThemeService,
|
|
89
87
|
new ThemeService({
|
|
90
88
|
...(themeConfig as unknown as ThemeServiceProps),
|
|
91
|
-
storage: ioc.get
|
|
89
|
+
storage: ioc.get(I.LocalStorage)
|
|
92
90
|
})
|
|
93
91
|
);
|
|
94
92
|
|
|
95
93
|
ioc.bind(I.I18nKeyErrorPlugin, ioc.get(I18nKeyErrorPlugin));
|
|
96
|
-
|
|
97
|
-
ioc.bind(
|
|
94
|
+
|
|
95
|
+
ioc.bind(
|
|
96
|
+
I.UserServiceInterface,
|
|
97
|
+
ioc
|
|
98
|
+
.get<UserServiceInterface>(UserService)
|
|
99
|
+
.use(new UserGatewayPlugin(routeService))
|
|
100
|
+
);
|
|
98
101
|
ioc.bind(I.RequestCatcherInterface, ioc.get(RequestStatusCatcher));
|
|
99
102
|
ioc.bind(I.ExecutorPageBridgeInterface, ioc.get(ExecutorPageBridge));
|
|
100
103
|
ioc.bind(I.JSONStoragePageInterface, ioc.get(JSONStoragePageBridge));
|
|
@@ -111,7 +114,10 @@ export class ClientIOCRegister
|
|
|
111
114
|
token: () =>
|
|
112
115
|
ioc.get<UserServiceInterface>(I.UserServiceInterface).getToken()
|
|
113
116
|
});
|
|
114
|
-
const apiMockPlugin = new ApiMockPlugin(
|
|
117
|
+
const apiMockPlugin = new ApiMockPlugin({
|
|
118
|
+
mockData: mockDataJson,
|
|
119
|
+
logger: logger
|
|
120
|
+
});
|
|
115
121
|
const apiCatchPlugin = new ApiCatchPlugin(
|
|
116
122
|
logger,
|
|
117
123
|
ioc.get(RequestStatusCatcher)
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
// ! global variables, don't import any dependencies and don't have side effects
|
|
2
2
|
import { loggerStyles } from '@config/common';
|
|
3
|
-
import {
|
|
4
|
-
ColorFormatter,
|
|
5
|
-
ConsoleHandler,
|
|
6
|
-
CookieStorage,
|
|
7
|
-
Logger
|
|
8
|
-
} from '@qlover/corekit-bridge';
|
|
3
|
+
import { ColorFormatter, CookieStorage } from '@qlover/corekit-bridge';
|
|
9
4
|
import {
|
|
10
5
|
Base64Serializer,
|
|
11
6
|
JSONSerializer,
|
|
12
7
|
ObjectStorage,
|
|
13
8
|
SyncStorage
|
|
14
9
|
} from '@qlover/fe-corekit';
|
|
10
|
+
import { Logger, ConsoleHandler } from '@qlover/logger';
|
|
15
11
|
import { AppConfig } from '@/base/cases/AppConfig';
|
|
16
12
|
import { DialogHandler } from '@/base/cases/DialogHandler';
|
|
17
13
|
import type { SyncStorageInterface } from '@qlover/fe-corekit';
|
|
@@ -37,11 +33,14 @@ export const JSON = new JSONSerializer();
|
|
|
37
33
|
/**
|
|
38
34
|
* Override localStorage to use the global local storage
|
|
39
35
|
*/
|
|
40
|
-
export const localStorage = new SyncStorage(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
36
|
+
export const localStorage = new SyncStorage<string>(
|
|
37
|
+
new ObjectStorage(),
|
|
38
|
+
[
|
|
39
|
+
JSON,
|
|
40
|
+
appConfig.isProduction ? new Base64Serializer() : undefined,
|
|
41
|
+
window.localStorage as unknown as SyncStorageInterface<string>
|
|
42
|
+
].filter(Boolean) as any[]
|
|
43
|
+
);
|
|
45
44
|
|
|
46
45
|
export const localStorageEncrypt = localStorage;
|
|
47
46
|
|
|
@@ -4,7 +4,7 @@ import { StrictMode } from 'react';
|
|
|
4
4
|
import { createRoot } from 'react-dom/client';
|
|
5
5
|
import App from './App.tsx';
|
|
6
6
|
import { BootstrapClient } from './core/bootstraps/BootstrapClient';
|
|
7
|
-
import { clientIOC } from './core/clientIoc/ClientIOC
|
|
7
|
+
import { clientIOC } from './core/clientIoc/ClientIOC';
|
|
8
8
|
|
|
9
9
|
BootstrapClient.main({
|
|
10
10
|
root: window,
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { useStore } from '@brain-toolkit/react-kit/hooks/useStore';
|
|
2
2
|
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
3
3
|
import { Navigate, Outlet } from 'react-router-dom';
|
|
4
|
-
import { useI18nGuard } from '@/uikit/hooks/useI18nGuard';
|
|
5
4
|
import { useIOC } from '@/uikit/hooks/useIOC';
|
|
5
|
+
import { useRouterI18nGuard } from '@/uikit/hooks/useRouterI18nGuard';
|
|
6
6
|
import { BaseHeader } from '../../uikit/components/BaseHeader';
|
|
7
7
|
|
|
8
8
|
export default function Layout() {
|
|
9
9
|
const userService = useIOC(IOCIdentifier.UserServiceInterface);
|
|
10
|
-
useStore(userService.
|
|
10
|
+
const state = useStore(userService.getStore());
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
useRouterI18nGuard();
|
|
13
13
|
|
|
14
14
|
// If user is authenticated, redirect to home page
|
|
15
|
-
if (userService.isAuthenticated()) {
|
|
15
|
+
if (userService.isAuthenticated() && userService.isUserInfo(state.result)) {
|
|
16
16
|
return <Navigate data-testid="Layout" to="/" replace />;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -3,7 +3,7 @@ import { register18n } from '@config/i18n/register18n';
|
|
|
3
3
|
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
4
4
|
import { Form, Input, Button, Checkbox } from 'antd';
|
|
5
5
|
import { useState } from 'react';
|
|
6
|
-
import type { RegisterFormData } from '@/base/
|
|
6
|
+
import type { RegisterFormData } from '@/base/apis/userApi/UserApiType';
|
|
7
7
|
import { useI18nInterface } from '@/uikit/hooks/useI18nInterface';
|
|
8
8
|
import { useIOC } from '@/uikit/hooks/useIOC';
|
|
9
9
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Outlet } from 'react-router-dom';
|
|
2
|
-
import {
|
|
2
|
+
import { BaseLayoutProvider } from '@/uikit/components/BaseLayoutProvider';
|
|
3
3
|
import { BaseHeader } from '../../uikit/components/BaseHeader';
|
|
4
4
|
|
|
5
5
|
export default function Layout() {
|
|
6
6
|
return (
|
|
7
|
-
<
|
|
7
|
+
<BaseLayoutProvider data-testid="Layout">
|
|
8
8
|
<div
|
|
9
9
|
data-testid="basic-layout"
|
|
10
10
|
className="text-base min-h-screen bg-primary"
|
|
@@ -15,6 +15,6 @@ export default function Layout() {
|
|
|
15
15
|
<Outlet />
|
|
16
16
|
</div>
|
|
17
17
|
</div>
|
|
18
|
-
</
|
|
18
|
+
</BaseLayoutProvider>
|
|
19
19
|
);
|
|
20
20
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useStore } from '@brain-toolkit/react-kit';
|
|
2
|
+
import { I } from '@config/IOCIdentifier';
|
|
3
|
+
import { BaseLayoutService } from '@/base/services/BaseLayoutService';
|
|
4
|
+
import { Loading } from './Loading';
|
|
5
|
+
import { useIOC } from '../hooks/useIOC';
|
|
6
|
+
import { useNavigateBridge } from '../hooks/useNavigateBridge';
|
|
7
|
+
import { useRouterI18nGuard } from '../hooks/useRouterI18nGuard';
|
|
8
|
+
import { useStrictEffect } from '../hooks/useStrictEffect';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* BaseLayoutProvider
|
|
12
|
+
*
|
|
13
|
+
* /pages/base/Layout.tsx 布局组件的服务提供者
|
|
14
|
+
*
|
|
15
|
+
* - 验证用户信息渲染组件,如果用户未登录则渲染 loading
|
|
16
|
+
*
|
|
17
|
+
* @param children - The children to render
|
|
18
|
+
* @returns
|
|
19
|
+
*/
|
|
20
|
+
export function BaseLayoutProvider({
|
|
21
|
+
children
|
|
22
|
+
}: {
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}) {
|
|
25
|
+
const ioc = useIOC();
|
|
26
|
+
const baseLayoutService = ioc(BaseLayoutService);
|
|
27
|
+
const userService = ioc(I.UserServiceInterface);
|
|
28
|
+
|
|
29
|
+
useStore(userService.getStore());
|
|
30
|
+
|
|
31
|
+
useRouterI18nGuard();
|
|
32
|
+
|
|
33
|
+
useNavigateBridge();
|
|
34
|
+
|
|
35
|
+
useStrictEffect(() => {
|
|
36
|
+
baseLayoutService.use(ioc(I.I18nKeyErrorPlugin)).starup(ioc);
|
|
37
|
+
}, []);
|
|
38
|
+
|
|
39
|
+
if (!userService.isAuthenticated()) {
|
|
40
|
+
return <Loading data-testid="BaseLayoutProviderLoading" fullscreen />;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return children;
|
|
44
|
+
}
|
|
@@ -3,6 +3,15 @@ import { useNavigate } from 'react-router-dom';
|
|
|
3
3
|
import { useIOC } from './useIOC';
|
|
4
4
|
import { NavigateBridge } from '../bridges/NavigateBridge';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* 使用 navigate 桥接
|
|
8
|
+
*
|
|
9
|
+
* 将 navigate 桥接到 NavigateBridge 中
|
|
10
|
+
*
|
|
11
|
+
* 这样就可以在服务层使用 navigate 了, 比如在某个service中需要跳转页面,就可以使用 navigateBridge.getUIBridge() 来跳转
|
|
12
|
+
*
|
|
13
|
+
* **但是它需要在 RouterProvider 中使用**
|
|
14
|
+
*/
|
|
6
15
|
export function useNavigateBridge() {
|
|
7
16
|
const navigate = useNavigate();
|
|
8
17
|
const navigateBridge = useIOC(NavigateBridge);
|
|
@@ -5,13 +5,16 @@ import type { LocaleType } from '@config/i18n/i18nConfig';
|
|
|
5
5
|
import { useIOC } from './useIOC';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
8
|
+
* 国际化路由国际化守卫
|
|
9
9
|
*
|
|
10
|
-
*
|
|
10
|
+
* 比如: 开启了 `@config/common 中useLocaleRoutes` 为 `true` 时,
|
|
11
|
+
*
|
|
12
|
+
* 访问 `/login` 则会自动重定向到 `/en/login`, 其中 en 为默认语言可配置
|
|
13
|
+
*
|
|
14
|
+
* 或者访问一个不支持的语言,则会自动重定向到 `/en/404`
|
|
11
15
|
*
|
|
12
|
-
* TODO: if language not found, use default language
|
|
13
16
|
*/
|
|
14
|
-
export function
|
|
17
|
+
export function useRouterI18nGuard() {
|
|
15
18
|
const { lng } = useParams<{ lng: LocaleType }>();
|
|
16
19
|
const { pathname } = useLocation();
|
|
17
20
|
const navigate = useNavigate();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qlover/create-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.1",
|
|
4
4
|
"description": "Create a new app with a single command",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -33,12 +33,12 @@
|
|
|
33
33
|
"ignore": "^7.0.3",
|
|
34
34
|
"lodash": "^4.17.21",
|
|
35
35
|
"ora": "^8.1.1",
|
|
36
|
-
"@qlover/logger": "0.
|
|
36
|
+
"@qlover/logger": "0.3.0"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"commander": "^13.1.0",
|
|
40
40
|
"inquirer": "^12.3.2",
|
|
41
|
-
"@qlover/scripts-context": "1.
|
|
41
|
+
"@qlover/scripts-context": "1.2.0"
|
|
42
42
|
},
|
|
43
43
|
"scripts": {
|
|
44
44
|
"lint": "eslint src --fix",
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AppError test suite
|
|
3
|
-
*
|
|
4
|
-
* Coverage:
|
|
5
|
-
* 1. constructor - Constructor initialization with different source types
|
|
6
|
-
* 2. inheritance - Verify inheritance from ExecutorError
|
|
7
|
-
* 3. error properties - Verify error properties are set correctly
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import { ExecutorError } from '@qlover/fe-corekit';
|
|
11
|
-
import { describe, it, expect } from 'vitest';
|
|
12
|
-
import { AppError } from '@/base/cases/AppError';
|
|
13
|
-
|
|
14
|
-
describe('AppError', () => {
|
|
15
|
-
describe('constructor', () => {
|
|
16
|
-
it('should create instance with id only', () => {
|
|
17
|
-
const error = new AppError('TEST_ERROR');
|
|
18
|
-
expect(error).toBeInstanceOf(AppError);
|
|
19
|
-
expect(error).toBeInstanceOf(ExecutorError);
|
|
20
|
-
expect(error).toBeInstanceOf(Error);
|
|
21
|
-
expect(error.id).toBe('TEST_ERROR');
|
|
22
|
-
expect(error.message).toBe('TEST_ERROR');
|
|
23
|
-
expect(error.source).toBeUndefined();
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should create instance with string source', () => {
|
|
27
|
-
const source = 'Test error message';
|
|
28
|
-
const error = new AppError('TEST_ERROR', source);
|
|
29
|
-
expect(error.id).toBe('TEST_ERROR');
|
|
30
|
-
expect(error.message).toBe(source);
|
|
31
|
-
expect(error.source).toBe(source);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it('should create instance with Error source', () => {
|
|
35
|
-
const source = new Error('Original error');
|
|
36
|
-
const error = new AppError('TEST_ERROR', source);
|
|
37
|
-
expect(error.id).toBe('TEST_ERROR');
|
|
38
|
-
expect(error.message).toBe('Original error');
|
|
39
|
-
expect(error.source).toBe(source);
|
|
40
|
-
});
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe('error properties', () => {
|
|
44
|
-
it('should have correct error type', () => {
|
|
45
|
-
const error = new AppError('TEST_ERROR');
|
|
46
|
-
expect(error.id).toBe('TEST_ERROR');
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it('should maintain id property', () => {
|
|
50
|
-
const error = new AppError('TEST_ERROR');
|
|
51
|
-
Object.defineProperty(error, 'id', { value: 'NEW_ID' });
|
|
52
|
-
expect(error.id).toBe('NEW_ID');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should maintain source property', () => {
|
|
56
|
-
const source = 'source';
|
|
57
|
-
const error = new AppError('TEST_ERROR', source);
|
|
58
|
-
Object.defineProperty(error, 'source', { value: 'new source' });
|
|
59
|
-
expect(error.source).toBe('new source');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
describe('error handling', () => {
|
|
64
|
-
it('should work in try-catch block', () => {
|
|
65
|
-
expect(() => {
|
|
66
|
-
throw new AppError('TEST_ERROR');
|
|
67
|
-
}).toThrow(AppError);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should preserve stack trace', () => {
|
|
71
|
-
const error = new AppError('TEST_ERROR');
|
|
72
|
-
expect(error.stack).toBeDefined();
|
|
73
|
-
expect(error.stack).toContain('AppError.test.ts');
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it('should handle nested errors', () => {
|
|
77
|
-
const originalError = new Error('Original error');
|
|
78
|
-
const wrappedError = new AppError('WRAPPED_ERROR', originalError);
|
|
79
|
-
expect(wrappedError.source).toBe(originalError);
|
|
80
|
-
expect(wrappedError.message).toBe('Original error');
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
describe('error message formatting', () => {
|
|
85
|
-
it('should use id as message when source is undefined', () => {
|
|
86
|
-
const error = new AppError('TEST_ERROR');
|
|
87
|
-
expect(error.message).toBe('TEST_ERROR');
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('should use source message when source is Error', () => {
|
|
91
|
-
const source = new Error('Source error message');
|
|
92
|
-
const error = new AppError('TEST_ERROR', source);
|
|
93
|
-
expect(error.message).toBe('Source error message');
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('should use source string when source is string', () => {
|
|
97
|
-
const source = 'Source error message';
|
|
98
|
-
const error = new AppError('TEST_ERROR', source);
|
|
99
|
-
expect(error.message).toBe('Source error message');
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
});
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { readFileSync, existsSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
|
|
4
|
-
describe('main.tsx Integration Tests', () => {
|
|
5
|
-
beforeAll(() => {
|
|
6
|
-
// Setup test environment
|
|
7
|
-
if (!document.getElementById('root')) {
|
|
8
|
-
const rootElement = document.createElement('div');
|
|
9
|
-
rootElement.id = 'root';
|
|
10
|
-
document.body.appendChild(rootElement);
|
|
11
|
-
}
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
afterAll(() => {
|
|
15
|
-
// Clean up DOM
|
|
16
|
-
const rootElement = document.getElementById('root');
|
|
17
|
-
if (rootElement) {
|
|
18
|
-
rootElement.remove();
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
beforeEach(() => {
|
|
23
|
-
// Clean up root element content
|
|
24
|
-
const rootElement = document.getElementById('root');
|
|
25
|
-
if (rootElement) {
|
|
26
|
-
rootElement.innerHTML = '';
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('should have main.tsx file', () => {
|
|
31
|
-
// Test if file exists
|
|
32
|
-
const mainPath = join(process.cwd(), 'src/main.tsx');
|
|
33
|
-
expect(existsSync(mainPath)).toBe(true);
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it('should have main.tsx with correct structure', () => {
|
|
37
|
-
// Test file content structure
|
|
38
|
-
const mainPath = join(process.cwd(), 'src/main.tsx');
|
|
39
|
-
const content = readFileSync(mainPath, 'utf-8');
|
|
40
|
-
|
|
41
|
-
// Check if it contains necessary imports
|
|
42
|
-
expect(content).toContain('import');
|
|
43
|
-
expect(content).toContain('BootstrapClient');
|
|
44
|
-
expect(content).toContain('createRoot');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should have App.tsx file', () => {
|
|
48
|
-
// Test if App.tsx file exists
|
|
49
|
-
const appPath = join(process.cwd(), 'src/App.tsx');
|
|
50
|
-
expect(existsSync(appPath)).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should have BootstrapClient.ts file', () => {
|
|
54
|
-
// Test if BootstrapClient.ts file exists
|
|
55
|
-
const bootstrapPath = join(
|
|
56
|
-
process.cwd(),
|
|
57
|
-
'src/core/bootstraps/BootstrapClient.ts'
|
|
58
|
-
);
|
|
59
|
-
expect(existsSync(bootstrapPath)).toBe(true);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
2
|
-
import {
|
|
3
|
-
AsyncExecutor,
|
|
4
|
-
ExecutorContext,
|
|
5
|
-
ExecutorPlugin
|
|
6
|
-
} from '@qlover/fe-corekit';
|
|
7
|
-
import { injectable, inject } from 'inversify';
|
|
8
|
-
import type { ProcesserExecutorInterface } from '../port/ProcesserExecutorInterface';
|
|
9
|
-
import type { RouteServiceInterface } from '../port/RouteServiceInterface';
|
|
10
|
-
import type { BootstrapContextValue } from '@qlover/corekit-bridge';
|
|
11
|
-
import type { LoggerInterface } from '@qlover/logger';
|
|
12
|
-
|
|
13
|
-
@injectable()
|
|
14
|
-
export class ProcesserExecutor implements ProcesserExecutorInterface {
|
|
15
|
-
pluginName = 'ProcesserExecutor';
|
|
16
|
-
protected executor: AsyncExecutor = new AsyncExecutor();
|
|
17
|
-
|
|
18
|
-
constructor(
|
|
19
|
-
@inject(IOCIdentifier.Logger) protected logger: LoggerInterface,
|
|
20
|
-
@inject(IOCIdentifier.RouteServiceInterface)
|
|
21
|
-
protected routeService: RouteServiceInterface
|
|
22
|
-
) {}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @override
|
|
26
|
-
*/
|
|
27
|
-
onBefore({
|
|
28
|
-
parameters: { ioc }
|
|
29
|
-
}: ExecutorContext<BootstrapContextValue>): void | Promise<void> {
|
|
30
|
-
this.use(ioc.get(IOCIdentifier.I18nKeyErrorPlugin));
|
|
31
|
-
this.use(ioc.get(IOCIdentifier.UserServiceInterface));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
use(plugin: ExecutorPlugin): this {
|
|
35
|
-
this.executor.use(plugin);
|
|
36
|
-
return this;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
handler(): Promise<{ success: boolean }> {
|
|
40
|
-
return Promise.resolve({
|
|
41
|
-
success: true
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async starup(): Promise<unknown> {
|
|
46
|
-
this.logger.info('PageProcesser startup');
|
|
47
|
-
|
|
48
|
-
try {
|
|
49
|
-
const result = await this.executor.exec(this.handler);
|
|
50
|
-
return result;
|
|
51
|
-
} catch (err) {
|
|
52
|
-
this.logger.error('PageProcesser init failed', err);
|
|
53
|
-
|
|
54
|
-
this.routeService.gotoLogin();
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
2
|
-
import { UserAuthProvider } from './UserAuthProvider';
|
|
3
|
-
import { useI18nGuard } from '../hooks/useI18nGuard';
|
|
4
|
-
import { useIOC } from '../hooks/useIOC';
|
|
5
|
-
import { useNavigateBridge } from '../hooks/useNavigateBridge';
|
|
6
|
-
import { useStrictEffect } from '../hooks/useStrictEffect';
|
|
7
|
-
|
|
8
|
-
export function ProcessExecutorProvider({
|
|
9
|
-
children
|
|
10
|
-
}: {
|
|
11
|
-
children: React.ReactNode;
|
|
12
|
-
}) {
|
|
13
|
-
const processerExecutor = useIOC(IOCIdentifier.ProcesserExecutorInterface);
|
|
14
|
-
|
|
15
|
-
useI18nGuard();
|
|
16
|
-
|
|
17
|
-
useNavigateBridge();
|
|
18
|
-
|
|
19
|
-
useStrictEffect(() => {
|
|
20
|
-
processerExecutor.starup();
|
|
21
|
-
}, []);
|
|
22
|
-
|
|
23
|
-
return (
|
|
24
|
-
<UserAuthProvider data-testid="ProcessExecutorProvider">
|
|
25
|
-
{children}
|
|
26
|
-
</UserAuthProvider>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { useStore } from '@brain-toolkit/react-kit/hooks/useStore';
|
|
2
|
-
import { IOCIdentifier } from '@config/IOCIdentifier';
|
|
3
|
-
import { Loading } from './Loading';
|
|
4
|
-
import { useIOC } from '../hooks/useIOC';
|
|
5
|
-
|
|
6
|
-
export function UserAuthProvider({ children }: { children: React.ReactNode }) {
|
|
7
|
-
const userService = useIOC(IOCIdentifier.UserServiceInterface);
|
|
8
|
-
|
|
9
|
-
useStore(userService.store);
|
|
10
|
-
|
|
11
|
-
if (!userService.isAuthenticated()) {
|
|
12
|
-
return <Loading data-testid="UserAuthProvider" fullscreen />;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return children;
|
|
16
|
-
}
|