Skip to content

Commit c784a4d

Browse files
committed
feat: Enhance CommonService with new methods and improve test coverage for UserServices and BaseService
1 parent a4eebfb commit c784a4d

File tree

5 files changed

+155
-15
lines changed

5 files changed

+155
-15
lines changed

.github/instructions/general.instructions.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ mixcore/javascript-sdk/
5959

6060
### Extraction & Refactoring Requirements
6161

62-
- Audit and extract all shared, base, and API code from `.legacy` (legacy portal) into the correct domain-driven package.
62+
- Audit and extract all shared, base, and API code from `/.legacy` (legacy portal) into the correct domain-driven package.
6363
- Exclude all SPA/UI-coupled logic (routing, DOM, framework state, etc.).
6464
- Refactor all imports/exports to use package-relative paths.
6565
- Remove or abstract any SPA-coupled dependencies (e.g., window, document, SPA-specific stores).
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { BaseService, BaseServiceConfig } from '../src/base-service';
2+
3+
class TestService extends BaseService {
4+
constructor(config: BaseServiceConfig) {
5+
super(config);
6+
}
7+
8+
public handleError(error: Error): void {
9+
console.error('TestService error:', error);
10+
}
11+
}
12+
13+
describe('BaseService', () => {
14+
const config: BaseServiceConfig = {
15+
apiBaseUrl: 'https://api.example.com',
16+
getApiResult: jest.fn().mockResolvedValue({}),
17+
getRestApiResult: jest.fn().mockResolvedValue({}),
18+
getAnonymousApiResult: jest.fn().mockResolvedValue({}),
19+
};
20+
21+
const service = new TestService(config);
22+
23+
it('should instantiate with config', () => {
24+
expect(service).toBeInstanceOf(TestService);
25+
expect(service['config']).toEqual(config);
26+
});
27+
28+
it('should handle errors', () => {
29+
const mockError = new Error('Test error');
30+
jest.spyOn(console, 'error').mockImplementation(() => {});
31+
32+
expect(() => service.handleError(mockError)).not.toThrow();
33+
expect(console.error).toHaveBeenCalledWith('TestService error:', mockError);
34+
});
35+
});

packages/shared/src/common-services.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,37 @@ describe('CommonService', () => {
1919
await service.showAlertMsg('Title', 'Message');
2020
expect(config.onAlert).toHaveBeenCalledWith('Title', 'Message');
2121
});
22+
23+
it('sendMail should call getApiResult', async () => {
24+
await service.sendMail('Subject', 'Body');
25+
expect(config.getApiResult).toHaveBeenCalledWith({
26+
method: 'POST',
27+
url: '/portal/sendmail',
28+
data: { subject: 'Subject', body: 'Body' }
29+
});
30+
});
31+
32+
it('getAllSettings should call getRestApiResult', async () => {
33+
await service.getAllSettings('en-US');
34+
expect(config.getRestApiResult).toHaveBeenCalledWith({
35+
method: 'GET',
36+
url: '/rest/shared/en-US/get-shared-settings'
37+
}, false, true);
38+
});
39+
40+
it('generateSitemap should call getApiResult', async () => {
41+
await service.generateSitemap();
42+
expect(config.getApiResult).toHaveBeenCalledWith({
43+
method: 'GET',
44+
url: '/portal/sitemap'
45+
});
46+
});
47+
48+
it('getPermissions should call getRestApiResult', async () => {
49+
await service.getPermissions();
50+
expect(config.getRestApiResult).toHaveBeenCalledWith({
51+
method: 'GET',
52+
url: '/rest/shared/permissions'
53+
});
54+
});
2255
});

packages/shared/src/common-services.ts

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,27 @@
22
/**
33
* CommonService: Framework-agnostic utility for Mixcore SDK
44
* All SPA/Angular dependencies removed. Configuration must be injected.
5-
*
6-
* TODO: Implement actual logic and types for each method.
75
*/
6+
7+
interface ApiResponse {
8+
isSucceed: boolean;
9+
data?: any;
10+
message?: string;
11+
}
12+
13+
interface SharedSettings {
14+
[key: string]: any;
15+
culture?: string;
16+
}
17+
18+
interface SitemapResult {
19+
urls: string[];
20+
generatedAt: string;
21+
}
22+
23+
interface PermissionSet {
24+
[permission: string]: boolean;
25+
}
826
export interface CommonServiceConfig {
927
apiBaseUrl: string;
1028
getApiResult: (req: any) => Promise<any>;
@@ -63,17 +81,26 @@ export class CommonService {
6381
return validExts.includes(ext);
6482
}
6583

66-
async sendMail(subject: string, body: string): Promise<any> {
67-
// TODO: Implement using config.getApiResult
84+
/**
85+
* Sends an email using the configured API endpoint
86+
* @param subject - Email subject
87+
* @param body - Email body content
88+
* @returns Promise with API response
89+
*/
90+
async sendMail(subject: string, body: string): Promise<ApiResponse> {
6891
return this.config.getApiResult({
6992
method: 'POST',
7093
url: '/portal/sendmail',
7194
data: { subject, body }
7295
});
7396
}
7497

75-
async getAllSettings(culture?: string): Promise<any> {
76-
// TODO: Implement using config.getRestApiResult
98+
/**
99+
* Gets all shared settings, optionally filtered by culture
100+
* @param culture - Optional culture code to filter settings
101+
* @returns Promise with settings data
102+
*/
103+
async getAllSettings(culture?: string): Promise<SharedSettings> {
77104
let url = '/rest/shared';
78105
if (culture) url += `/${culture}`;
79106
url += '/get-shared-settings';
@@ -83,16 +110,22 @@ export class CommonService {
83110
}, false, true);
84111
}
85112

86-
async genrateSitemap(): Promise<any> {
87-
// TODO: Implement using config.getApiResult
113+
/**
114+
* Generates a sitemap using the portal API
115+
* @returns Promise with sitemap generation result
116+
*/
117+
async generateSitemap(): Promise<SitemapResult> {
88118
return this.config.getApiResult({
89119
method: 'GET',
90120
url: '/portal/sitemap'
91121
});
92122
}
93123

94-
async getPermissions(): Promise<any> {
95-
// TODO: Implement using config.getRestApiResult
124+
/**
125+
* Gets user permissions from the API
126+
* @returns Promise with permissions data
127+
*/
128+
async getPermissions(): Promise<PermissionSet> {
96129
return this.config.getRestApiResult({
97130
method: 'GET',
98131
url: '/rest/shared/permissions'

packages/user/tests/user-services.test.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { ApiService } from './src/api-services';
2-
import { UserServices } from './src/user-services';
1+
import { ApiService } from '@mixcore/api';
2+
import { UserServices } from '../src/user-services';
33

44
describe('UserServices', () => {
55
let api: ApiService;
@@ -15,7 +15,6 @@ describe('UserServices', () => {
1515
});
1616

1717
it('should call getMyProfile', async () => {
18-
// Mock fetch
1918
globalThis.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ id: 'me' }) });
2019
const result = await userServices.getMyProfile();
2120
expect(result).toEqual({ id: 'me' });
@@ -27,5 +26,45 @@ describe('UserServices', () => {
2726
expect(result).toEqual({ success: true });
2827
});
2928

30-
// Add more tests for other methods as needed
29+
it('should call register', async () => {
30+
globalThis.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ id: 'new' }) });
31+
const result = await userServices.register({ name: 'New User' });
32+
expect(result).toEqual({ id: 'new' });
33+
});
34+
35+
it('should call getUser', async () => {
36+
globalThis.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ id: '123' }) });
37+
const result = await userServices.getUser('123', 'basic');
38+
expect(result).toEqual({ id: '123' });
39+
});
40+
41+
it('should call getUsers', async () => {
42+
globalThis.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ users: [] }) });
43+
const result = await userServices.getUsers({ page: 1 });
44+
expect(result).toEqual({ users: [] });
45+
});
46+
47+
it('should call importUsers', async () => {
48+
globalThis.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ imported: 5 }) });
49+
const result = await userServices.importUsers('base64data');
50+
expect(result).toEqual({ imported: 5 });
51+
});
52+
53+
it('should call removeUser', async () => {
54+
globalThis.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ success: true }) });
55+
const result = await userServices.removeUser('123');
56+
expect(result).toEqual({ success: true });
57+
});
58+
59+
it('should call updateRoleStatus', async () => {
60+
globalThis.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ updated: true }) });
61+
const result = await userServices.updateRoleStatus({ userId: '1', roleId: 'admin' });
62+
expect(result).toEqual({ updated: true });
63+
});
64+
65+
it('should call getUserDemographicInfo', async () => {
66+
globalThis.fetch = jest.fn().mockResolvedValue({ ok: true, json: async () => ({ info: {} }) });
67+
const result = await userServices.getUserDemographicInfo();
68+
expect(result).toEqual({ info: {} });
69+
});
3170
});

0 commit comments

Comments
 (0)