Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* Copyright since 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute } from '@angular/router';
import { of, throwError } from 'rxjs';
Comment thread
coderabbitai[bot] marked this conversation as resolved.
import { AcceptClientTransferComponent } from './accept-client-transfer.component';
import { ClientsService } from 'app/clients/clients.service';
import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { provideNativeDateAdapter } from '@angular/material/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { ClientActionNotifierService } from '../client-action-notifier.service';

import { TranslateModule } from '@ngx-translate/core';
import { describe, it, expect, jest, beforeEach } from '@jest/globals';

Comment thread
coderabbitai[bot] marked this conversation as resolved.
describe('AcceptClientTransferComponent', () => {
let component: AcceptClientTransferComponent;
let fixture: ComponentFixture<AcceptClientTransferComponent>;

let clientsService: Partial<jest.Mocked<ClientsService>>;
let settingsService: Partial<SettingsService>;
let dates: Partial<jest.Mocked<Dates>>;
let notifier: Partial<jest.Mocked<ClientActionNotifierService>>;

const clientId = '456';
const transferDate = new Date(2025, 10, 1).toISOString();

const setup = async () => {
clientsService = {
executeClientCommand: jest.fn(() => of({}))
};

settingsService = {
language: { code: 'en' },
dateFormat: 'dd MMMM yyyy'
} as SettingsService;

dates = {
formatDate: jest.fn(() => '01 November 2025')
};

notifier = {
notifyAndNavigate: jest.fn(),
notify: jest.fn()
};

await TestBed.configureTestingModule({
imports: [
AcceptClientTransferComponent,
TranslateModule.forRoot()
],
providers: [
{
provide: ActivatedRoute,
useValue: {
data: of({ clientActionData: transferDate }),
parent: { snapshot: { params: { clientId } } }
}
},
{ provide: ClientsService, useValue: clientsService },
{ provide: SettingsService, useValue: settingsService },
{ provide: Dates, useValue: dates },
{ provide: ClientActionNotifierService, useValue: notifier },
provideNativeDateAdapter(),
provideAnimationsAsync()
]
}).compileComponents();

fixture = TestBed.createComponent(AcceptClientTransferComponent);
component = fixture.componentInstance;
fixture.detectChanges();
};

beforeEach(setup);

it('should initialize correctly', () => {
expect(component.clientId).toBe(clientId);
expect(component.acceptClientTransferForm).toBeDefined();
});

it('should call API with acceptTransfer command on submit', () => {
component.submit();

expect(clientsService.executeClientCommand).toHaveBeenCalledWith(clientId, 'acceptTransfer', expect.any(Object));
});

it('should notify and navigate after successful submission', () => {
component.submit();

expect(notifier.notifyAndNavigate).toHaveBeenCalledWith(
'clients.actions.acceptTransfer.success',
TestBed.inject(ActivatedRoute)
);
});

it('should notify failure if API call fails', () => {
(clientsService.executeClientCommand as jest.Mock).mockReturnValueOnce(throwError(() => new Error('API error')));

component.submit();

expect(notifier.notify).toHaveBeenCalledWith('clients.actions.acceptTransfer.failure');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

/** Angular Imports */
import { Component, OnInit, inject } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { UntypedFormGroup, UntypedFormBuilder } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';

/** Custom Services */
import { ClientsService } from 'app/clients/clients.service';
import { Dates } from 'app/core/utils/dates';
import { SettingsService } from 'app/settings/settings.service';
import { ClientActionNotifierService } from '../client-action-notifier.service';
import { CdkTextareaAutosize } from '@angular/cdk/text-field';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';

Expand All @@ -31,12 +32,12 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
]
})
export class AcceptClientTransferComponent implements OnInit {
private formBuilder = inject(UntypedFormBuilder);
private clientsService = inject(ClientsService);
private settingsService = inject(SettingsService);
private dateUtils = inject(Dates);
private route = inject(ActivatedRoute);
private router = inject(Router);
private readonly formBuilder = inject(UntypedFormBuilder);
private readonly clientsService = inject(ClientsService);
private readonly settingsService = inject(SettingsService);
private readonly dateUtils = inject(Dates);
private readonly route = inject(ActivatedRoute);
private readonly notifier = inject(ClientActionNotifierService);

/** Accept Client Transfer form. */
acceptClientTransferForm: UntypedFormGroup;
Expand All @@ -46,12 +47,7 @@ export class AcceptClientTransferComponent implements OnInit {
transferDate: any;

/**
* @param {FormBuilder} formBuilder Form Builder
* @param {ClientsService} clientsService Clients Service
* @param {SettingsService} settingsService Settings Service.
* @param {Dates} dateUtils Date Utils
* @param {ActivatedRoute} route Activated Route
* @param {Router} router Router
* constructor
*/
constructor() {
this.route.data.subscribe((data: { clientActionData: any }) => {
Expand Down Expand Up @@ -91,8 +87,9 @@ export class AcceptClientTransferComponent implements OnInit {
const data = {
...acceptClientTransferFormData
};
this.clientsService.executeClientCommand(this.clientId, 'acceptTransfer', data).subscribe(() => {
this.router.navigate(['../../'], { relativeTo: this.route });
this.clientsService.executeClientCommand(this.clientId, 'acceptTransfer', data).subscribe({
next: () => this.notifier.notifyAndNavigate('clients.actions.acceptTransfer.success', this.route),
error: () => this.notifier.notify('clients.actions.acceptTransfer.failure')
});
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,29 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
import { of, BehaviorSubject } from 'rxjs';
import { ActivatedRoute } from '@angular/router';
import { of } from 'rxjs';
import { ActivateClientComponent } from './activate-client.component';
import { ClientsService } from 'app/clients/clients.service';
import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
import { TranslateModule } from '@ngx-translate/core';
import { provideNativeDateAdapter } from '@angular/material/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { ClientActionNotifierService } from '../client-action-notifier.service';

import { TranslateModule } from '@ngx-translate/core';
import { describe, it, expect, jest, beforeEach } from '@jest/globals';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';

describe('ActivateClientComponent - Unit Tests', () => {
let component: ActivateClientComponent;
let fixture: ComponentFixture<ActivateClientComponent>;
let mockRouter: jest.Mocked<Router>;
let mockClientsService: jest.Mocked<ClientsService>;
let mockSettingsService: jest.Mocked<SettingsService>;
let mockDates: jest.Mocked<Dates>;
let mockNotifier: jest.Mocked<ClientActionNotifierService>;

const mockClientId = '123';
const mockBusinessDate = new Date(2026, 0, 15);

beforeEach(async () => {
mockRouter = {
navigate: jest.fn()
} as any;

mockClientsService = {
executeClientCommand: jest.fn(() => of({ resourceId: 123 }))
} as unknown as jest.Mocked<ClientsService>;
Expand All @@ -41,13 +38,14 @@ describe('ActivateClientComponent - Unit Tests', () => {
formatDate: jest.fn((date: Date, format: string) => '15 January 2026')
} as unknown as jest.Mocked<Dates>;

mockNotifier = { notifyAndNavigate: jest.fn() } as any;

await TestBed.configureTestingModule({
imports: [
ActivateClientComponent,
TranslateModule.forRoot()
],
providers: [
{ provide: Router, useValue: mockRouter },
{
provide: ActivatedRoute,
useValue: {
Expand All @@ -61,6 +59,7 @@ describe('ActivateClientComponent - Unit Tests', () => {
{ provide: ClientsService, useValue: mockClientsService },
{ provide: SettingsService, useValue: mockSettingsService },
{ provide: Dates, useValue: mockDates },
{ provide: ClientActionNotifierService, useValue: mockNotifier },
provideNativeDateAdapter(),
provideAnimationsAsync()
]
Expand Down Expand Up @@ -188,20 +187,26 @@ describe('ActivateClientComponent - Unit Tests', () => {
expect.objectContaining({ activationDate: '10 January 2026' })
);
});
});

describe('Navigation', () => {
beforeEach(() => {
fixture.detectChanges();
it('should notify and navigate after successful submission', () => {
component.activateClientForm.patchValue({ activationDate: new Date(2026, 0, 10) });

component.submit();

expect(mockNotifier.notifyAndNavigate).toHaveBeenCalledWith(
'clients.actions.activate.success',
TestBed.inject(ActivatedRoute)
);
});

it('should navigate to parent route after successful activation', () => {
const mockActivatedRoute = TestBed.inject(ActivatedRoute);
component.activateClientForm.patchValue({ activationDate: new Date() });
it('should not notify and navigate if API call fails', () => {
const { throwError } = require('rxjs');
mockClientsService.executeClientCommand.mockReturnValueOnce(throwError(() => new Error('API error')));
component.activateClientForm.patchValue({ activationDate: new Date(2026, 0, 10) });

component.submit();

expect(mockRouter.navigate).toHaveBeenCalledWith(['../../'], { relativeTo: mockActivatedRoute });
expect(mockNotifier.notifyAndNavigate).not.toHaveBeenCalled();
});
});

Expand Down Expand Up @@ -274,8 +279,8 @@ describe('ActivateClientComponent - Unit Tests', () => {
})
);

// 6. Navigation occurs
expect(mockRouter.navigate).toHaveBeenCalled();
// 6. Notification and navigation occur
expect(mockNotifier.notifyAndNavigate).toHaveBeenCalled();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

/** Angular Imports */
import { Component, OnInit, inject } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';

/** Custom Services */
import { ClientsService } from 'app/clients/clients.service';
import { Dates } from 'app/core/utils/dates';
import { SettingsService } from 'app/settings/settings.service';
import { ClientActionNotifierService } from '../client-action-notifier.service';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';

/**
Expand All @@ -29,12 +29,12 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
]
})
export class ActivateClientComponent implements OnInit {
private formBuilder = inject(UntypedFormBuilder);
private clientsService = inject(ClientsService);
private dateUtils = inject(Dates);
private route = inject(ActivatedRoute);
private router = inject(Router);
private settingsService = inject(SettingsService);
private readonly formBuilder = inject(UntypedFormBuilder);
private readonly clientsService = inject(ClientsService);
private readonly dateUtils = inject(Dates);
private readonly route = inject(ActivatedRoute);
private readonly settingsService = inject(SettingsService);
private readonly notifier = inject(ClientActionNotifierService);

/** Minimum date allowed. */
minDate = new Date(2000, 0, 1);
Expand Down Expand Up @@ -94,8 +94,9 @@ export class ActivateClientComponent implements OnInit {
dateFormat,
locale
};
this.clientsService.executeClientCommand(this.clientId, 'activate', data).subscribe(() => {
this.router.navigate(['../../'], { relativeTo: this.route });
this.clientsService.executeClientCommand(this.clientId, 'activate', data).subscribe({
next: () => this.notifier.notifyAndNavigate('clients.actions.activate.success', this.route),
error: () => this.notifier.notify('clients.actions.activate.failure')
});
}
}
Loading
Loading