Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
61784da
Story #1696
Nandan37 Dec 18, 2025
0242c32
Translation, SelectAllX changes
Nandan37 Dec 18, 2025
cbf906a
Merge pull request #785 from Nandan37/release-3.3.0
vishwanath1004 Dec 18, 2025
7abfa74
TS requirement : 1695
Nandan37 Dec 18, 2025
366c761
SelectAll 1696
Nandan37 Dec 18, 2025
5e60bc0
Merge pull request #786 from Nandan37/release-3.3.0
vishwanath1004 Dec 18, 2025
c8b21a8
story 1696
Nandan37 Dec 19, 2025
562093d
checboxDisabled changes
Nandan37 Dec 19, 2025
e6e996f
1696 minor fix
Nandan37 Dec 19, 2025
9567982
Merge pull request #787 from Nandan37/release-3.3.0
vishwanath1004 Dec 19, 2025
130ceda
Story #1696
Nandan37 Dec 23, 2025
c2651dd
story #1696 fix
Nandan37 Dec 24, 2025
3500aa5
Merge pull request #788 from Nandan37/release-3.3.0
vishwanath1004 Dec 24, 2025
3ff1368
Bug fix #4401
Nandan37 Jan 7, 2026
f6d2959
Merge pull request #794 from Nandan37/release-3.3.0
vishwanath1004 Jan 7, 2026
1368be2
Bug fix - 4408
Nandan37 Jan 9, 2026
eae6f2d
bug fix id- 4408
Nandan37 Jan 9, 2026
1747c3b
bug fix 4408
Nandan37 Jan 9, 2026
ce2b46d
Paginator constants & Label change
Nandan37 Jan 9, 2026
1c97fc5
Merge pull request #796 from Nandan37/release-3.3.0
vishwanath1004 Jan 9, 2026
68a7f09
Bug Fix Id :- 4411
Nandan37 Jan 9, 2026
5b91d5e
Merge pull request #798 from Nandan37/release-3.3.0
vishwanath1004 Jan 9, 2026
270162f
Bug fix :- 4425
Nandan37 Jan 13, 2026
7961944
Bug fix :- 4424 , Issue 1
Nandan37 Jan 13, 2026
51a2e9a
Merge pull request #799 from Nandan37/release-3.3.0
vishwanath1004 Jan 13, 2026
36f7411
refactor(home): Adopt Angular Signals for reactive state management a…
rohxnn Feb 17, 2026
3ccf501
refactor: Migrate private and session card components to Angular sign…
rohxnn Feb 19, 2026
a54548b
refactor: profile components to Angular's new control flow syntax and…
rohxnn Feb 19, 2026
eb32ece
refactor(core): rename private modules
rohxnn Feb 19, 2026
d4c2dc7
refactor(core): rename private modules
rohxnn Feb 19, 2026
34c05e3
Merge branch 'release-3.3.0' of https://github.com/ELEVATE-Project/me…
rohxnn Feb 23, 2026
85bec10
Merge branch 'release-3.4.0' of github.com:rohxnn/mentoring-mobile-ap…
rohxnn Feb 23, 2026
3b8f46c
feat(home): add test cases for home page components
rohxnn Feb 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions android/app/version.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
#Wed Feb 11 16:46:19 IST 2026
VERSION_CODE=3228
#Thu Feb 12 15:00:28 IST 2026
VERSION_CODE=3230
6,256 changes: 4,100 additions & 2,156 deletions package-lock.json

Large diffs are not rendered by default.

35 changes: 17 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^20.3.16",
"@angular/cdk": "^20.2.14",
"@angular/common": "^20.3.16",
"@angular/core": "^20.3.16",
"@angular/forms": "^20.3.16",
"@angular/material": "^20.2.14",
"@angular/platform-browser": "^20.3.16",
"@angular/platform-browser-dynamic": "^20.3.16",
"@angular/router": "^20.3.16",
"@angular/service-worker": "^20.3.16",
"@angular/animations": "^21.1.4",
"@angular/cdk": "^21.1.4",
"@angular/common": "^21.1.4",
"@angular/core": "^21.1.4",
"@angular/forms": "^21.1.4",
"@angular/material": "^21.1.4",
"@angular/platform-browser": "^21.1.4",
"@angular/platform-browser-dynamic": "^21.1.4",
"@angular/router": "^21.1.4",
"@angular/service-worker": "^21.1.4",
"@babel/core": "^7.23.3",
"@babel/runtime": "^7.23.2",
"@capacitor/android": "^6.0.0",
Expand All @@ -42,7 +42,7 @@
"@capacitor/splash-screen": "^6.0.0",
"@capacitor/status-bar": "^6.0.0",
"@danielmoncada/angular-datetime-picker": "^20.0.1",
"@ionic/angular": "^7.8.6",
"@ionic/angular": "^8.7.17",
"@ionic/core": "^8.3.1",
"@ionic/pwa-elements": "^3.3.0",
"@ionic/storage": "^4.0.0",
Expand All @@ -67,19 +67,18 @@
"ng-otp-input": "^1.8.5",
"papaparse": "^5.4.1",
"rxjs": "^7.5.0",
"rxjs-compat": "^6.6.7",
"sl-chat-library": "^1.3.2",
"sl-generic-table": "^1.3.12",
"tslib": "^2.2.0",
"yargs": "^17.7.2",
"zone.js": "^0.15.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "^20.3.14",
"@angular/cli": "^20.3.14",
"@angular/compiler": "^20.3.16",
"@angular/compiler-cli": "^20.3.16",
"@angular/language-service": "^20.3.16",
"@angular-devkit/build-angular": "^21.1.4",
"@angular/cli": "^21.1.4",
"@angular/compiler": "^21.1.4",
"@angular/compiler-cli": "^21.1.4",
"@angular/language-service": "^21.1.4",
"@capacitor/cli": "^6.0.0",
"@ionic/angular-toolkit": "^6.0.0",
"@types/jasmine": "~3.6.0",
Expand All @@ -102,7 +101,7 @@
"tailwindcss": "^3.4.17",
"ts-node": "~8.3.0",
"tslib": "^2.7.0",
"typescript": "~5.7.0"
"typescript": "~5.9.3"
},
"description": "An Ionic project"
}
2 changes: 1 addition & 1 deletion src/app/core/constants/paginatorConstants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const paginatorConstants = {
defaultPageSize: 5,
pageSizeOptions: [5,10,25,100]
pageSizeOptions: [5,10,25,50,75,100]
};
294 changes: 294 additions & 0 deletions src/app/core/services/appinit.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
import { NgZone } from '@angular/core';
import { TestBed } from '@angular/core/testing';
import { fakeAsync, tick } from '@angular/core/testing';
import { Location } from '@angular/common';
import { Router } from '@angular/router';
import { AlertController, MenuController, Platform } from '@ionic/angular';
import { TranslateService } from '@ngx-translate/core';
import { Subject, of } from 'rxjs';
import { localKeys } from 'src/app/core/constants/localStorage.keys';
import { PAGE_IDS } from 'src/app/core/constants/page.ids';
import { CommonRoutes } from 'src/global.routes';
import { PrivateService } from './appinit.service';
import {
AuthService,
DbService,
LocalStorageService,
NetworkService,
UserService,
UtilService,
} from '.';
import { ProfileService } from './profile/profile.service';
import { PermissionService } from './permission/permission.service';
import {
FrontendChatLibraryService,
RocketChatApiService,
} from 'sl-chat-library';

describe('PrivateService (appinit.service)', () => {
let service: PrivateService;
let localStorageService: jasmine.SpyObj<LocalStorageService>;
let translateService: jasmine.SpyObj<TranslateService>;
let utilService: jasmine.SpyObj<UtilService>;
let authService: jasmine.SpyObj<AuthService>;
let menuController: jasmine.SpyObj<MenuController>;
let profileService: jasmine.SpyObj<ProfileService>;
let router: jasmine.SpyObj<Router>;
let permissionService: jasmine.SpyObj<PermissionService>;
let chatService: { initialBadge: boolean; showBadge: Subject<boolean> };
let rocketChatService: jasmine.SpyObj<RocketChatApiService>;

beforeEach(() => {
const userEvents$ = new Subject<any>();
const backButtonSubscription = { unsubscribe: jasmine.createSpy('unsubscribe') };

localStorageService = jasmine.createSpyObj('LocalStorageService', ['getLocalData', 'setLocalData']);
translateService = jasmine.createSpyObj('TranslateService', ['use', 'get']);
menuController = jasmine.createSpyObj('MenuController', ['enable', 'toggle']);
utilService = jasmine.createSpyObj('UtilService', ['alertPopup', 'setHasBadge']);
authService = jasmine.createSpyObj('AuthService', ['logoutAccount', 'setUserInLocal']);
profileService = jasmine.createSpyObj('ProfileService', [
'getProfileDetailsFromAPI',
'getRequestCount',
'viewRolesModal',
'getUserRole',
'getChatToken',
]);
router = jasmine.createSpyObj('Router', ['navigate', 'navigateByUrl']);
permissionService = jasmine.createSpyObj('PermissionService', ['hasAdminAcess']);
rocketChatService = jasmine.createSpyObj('RocketChatApiService', ['initializeWebSocketAndCheckUnread']);

chatService = {
initialBadge: false,
showBadge: new Subject<boolean>(),
};

TestBed.configureTestingModule({
providers: [
PrivateService,
{ provide: TranslateService, useValue: translateService },
{
provide: Platform,
useValue: {
ready: jasmine.createSpy('ready').and.resolveTo(true),
backButton: {
subscribeWithPriority: jasmine.createSpy('subscribeWithPriority').and.returnValue(backButtonSubscription),
},
},
},
{ provide: LocalStorageService, useValue: localStorageService },
{ provide: MenuController, useValue: menuController },
{
provide: UserService,
useValue: {
getUserValue: jasmine.createSpy('getUserValue'),
userEventEmitted$: userEvents$.asObservable(),
},
},
{ provide: UtilService, useValue: utilService },
{ provide: DbService, useValue: jasmine.createSpyObj('DbService', ['init']) },
{ provide: Router, useValue: router },
{ provide: NetworkService, useValue: jasmine.createSpyObj('NetworkService', ['netWorkCheck']) },
{ provide: AuthService, useValue: authService },
{ provide: ProfileService, useValue: profileService },
{ provide: NgZone, useValue: { run: (fn: Function) => fn() } },
{ provide: Location, useValue: jasmine.createSpyObj('Location', ['isCurrentPathEqualTo', 'back']) },
{ provide: AlertController, useValue: jasmine.createSpyObj('AlertController', ['create']) },
{ provide: PermissionService, useValue: permissionService },
{ provide: FrontendChatLibraryService, useValue: chatService },
{ provide: RocketChatApiService, useValue: rocketChatService },
],
});

(profileService as any).isMentor = false;
permissionService.hasAdminAcess.and.returnValue(true);
service = TestBed.inject(PrivateService);
});

afterEach(() => {
chatService.showBadge.complete();
});

it('should create', () => {
expect(service).toBeTruthy();
});

it('should apply theme from localStorage', () => {
spyOn(localStorage, 'getItem').and.returnValue(
JSON.stringify({ primaryColor: '#111111', secondaryColor: '#222222' })
);
const setPropertySpy = spyOn(document.documentElement.style, 'setProperty');

service.applyTheme();

expect(setPropertySpy).toHaveBeenCalledWith('--ion-color-primary', '#111111');
expect(setPropertySpy).toHaveBeenCalledWith('--ion-color-secondary', '#222222');
});

it('should set badge flag true when any page has badge', () => {
service.appPages.update((pages) =>
pages.map((p) => (p.pageId === PAGE_IDS.messages ? { ...p, badge: true } : p))
);

service.updateBadgeFlag();

expect(utilService.setHasBadge).toHaveBeenCalledWith(true);
});

it('should set badge flag false when no page has badge', () => {
service.appPages.update((pages) => pages.map((p) => ({ ...p, badge: false })));

service.updateBadgeFlag();

expect(utilService.setHasBadge).toHaveBeenCalledWith(false);
});

it('should use stored language in languageSetting', async () => {
localStorageService.getLocalData.and.resolveTo('hi');

service.languageSetting();
await Promise.resolve();

expect(localStorageService.getLocalData).toHaveBeenCalledWith(localKeys.SELECTED_LANGUAGE);
expect(translateService.use).toHaveBeenCalledWith('hi');
});

it('should fallback to english when no stored language exists', async () => {
localStorageService.getLocalData.and.resolveTo(null);
spyOn(service, 'setLanguage');

service.languageSetting();
await Promise.resolve();

expect(service.setLanguage).toHaveBeenCalledWith('en');
});

it('should fallback to english when language fetch fails', fakeAsync(() => {
localStorageService.getLocalData.and.returnValue(Promise.reject('error'));
spyOn(service, 'setLanguage');

service.languageSetting();
tick();

expect(service.setLanguage).toHaveBeenCalledWith('en');
}));

it('should persist and use language in setLanguage', async () => {
localStorageService.setLocalData.and.resolveTo(true);

service.setLanguage('ta');
await Promise.resolve();

expect(localStorageService.setLocalData).toHaveBeenCalledWith(localKeys.SELECTED_LANGUAGE, 'ta');
expect(translateService.use).toHaveBeenCalledWith('ta');
});

it('should still use language when setLanguage persistence fails', fakeAsync(() => {
localStorageService.setLocalData.and.returnValue(Promise.reject('error'));

service.setLanguage('en');
tick();

expect(translateService.use).toHaveBeenCalledWith('en');
}));

it('should execute logout flow when alert confirms', fakeAsync(() => {
utilService.alertPopup.and.resolveTo(true);
localStorageService.setLocalData.and.resolveTo(true);
authService.logoutAccount.and.resolveTo();

service.logout();
tick();

expect(localStorageService.setLocalData).toHaveBeenCalledWith(localKeys.SELECTED_LANGUAGE, 'en');
expect(translateService.use).toHaveBeenCalledWith('en');
expect(authService.logoutAccount).toHaveBeenCalled();
expect(menuController.enable).toHaveBeenCalledWith(false);
}));

it('should not execute logout flow when alert is cancelled', async () => {
utilService.alertPopup.and.resolveTo(false);

service.logout();
await Promise.resolve();

expect(authService.logoutAccount).not.toHaveBeenCalled();
expect(menuController.enable).not.toHaveBeenCalled();
});

it('should update user/adminAccess and navigate to edit-profile in getUser', async () => {
const profileDetails = {
organizations: [{ id: 1 }],
permissions: [{ action: 'admin' }],
profile_mandatory_fields: ['bio'],
about: '',
};
spyOn(service, 'applyTheme');
profileService.getProfileDetailsFromAPI.and.resolveTo(profileDetails);
(profileService as any).isMentor = true;

service.getUser();
await Promise.resolve();

expect(service.applyTheme).toHaveBeenCalled();
expect(authService.setUserInLocal).toHaveBeenCalledWith(profileDetails);
expect(permissionService.hasAdminAcess).toHaveBeenCalled();
expect(service.user()).toEqual(profileDetails);
expect(service.isMentor()).toBeTrue();
expect(router.navigate).toHaveBeenCalledWith(
[`/${CommonRoutes.EDIT_PROFILE}`],
{ replaceUrl: true, queryParams: { redirectUrl: '/tabs/home' } }
);
});

it('should view roles from local storage', async () => {
const roles = ['mentor', 'mentee'];
localStorageService.getLocalData.and.resolveTo(roles);

await service.viewRoles();

expect(localStorageService.getLocalData).toHaveBeenCalledWith(localKeys.USER_ROLES);
expect(profileService.viewRolesModal).toHaveBeenCalledWith(roles);
});

it('should navigate to profile from goToProfilePage', () => {
service.goToProfilePage();

expect(menuController.toggle).toHaveBeenCalled();
expect(router.navigate).toHaveBeenCalledWith([`${CommonRoutes.TABS}/${CommonRoutes.PROFILE}`]);
});

it('should navigate to menu item url in menuItemAction', async () => {
await service.menuItemAction({ url: '/tabs/home' });

expect(router.navigate).toHaveBeenCalledWith(['/tabs/home']);
});

it('should update request and message badges in checkBadges', async () => {
service.isMentor.set(true);
profileService.getRequestCount.and.resolveTo({
result: { sessionRequestCount: 1, connectionRequestCount: 0 },
});
rocketChatService.initializeWebSocketAndCheckUnread.and.resolveTo();
chatService.initialBadge = true;

await service.checkBadges();

const requestsPage = service.appPages().find((p) => p.pageId === PAGE_IDS.requests);
const messagesPage = service.appPages().find((p) => p.pageId === PAGE_IDS.messages);
expect(requestsPage?.badge).toBeTrue();
expect(messagesPage?.badge).toBeTrue();
expect(utilService.setHasBadge).toHaveBeenCalled();

chatService.showBadge.next(false);
expect(service.appPages().find((p) => p.pageId === PAGE_IDS.messages)?.badge).toBeFalse();
});

it('should call setHeader and request current user value', () => {
const userService = TestBed.inject(UserService) as jasmine.SpyObj<UserService>;

service.setHeader();

expect(userService.getUserValue).toHaveBeenCalled();
});
});
Loading
Loading