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
Expand Up @@ -171,8 +171,8 @@
<mat-label>{{ 'labels.inputs.Incorporation Validity Till Date' | translate }}</mat-label>
<input
matInput
[min]="minDate"
[max]="maxDate"
[min]="createClientForm.value.dateOfBirth || minDate"
[max]="maxFutureDate"
[matDatepicker]="incorpValidityTillDateDatePicker"
formControlName="incorpValidityTillDate"
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/**
* 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 { By } from '@angular/platform-browser';
import { provideNativeDateAdapter } from '@angular/material/core';
import { MatDatepickerInput } from '@angular/material/datepicker';
import { provideNoopAnimations } from '@angular/platform-browser/animations';
import { CdkStepper } from '@angular/cdk/stepper';
import { FaIconLibrary } from '@fortawesome/angular-fontawesome';
import * as solidIcons from '@fortawesome/free-solid-svg-icons';
import { TranslateModule } from '@ngx-translate/core';
import { of } from 'rxjs';
import { describe, it, expect, jest, beforeEach } from '@jest/globals';

import { ClientGeneralStepComponent } from './client-general-step.component';
import { ClientsService } from 'app/clients/clients.service';
import { LegalFormId } from 'app/clients/models/legal-form.enum';
import { Dates } from 'app/core/utils/dates';
import { SettingsService } from 'app/settings/settings.service';

describe('ClientGeneralStepComponent WEB-1103 incorporation validity date', () => {
let fixture: ComponentFixture<ClientGeneralStepComponent>;
let component: ClientGeneralStepComponent;

const businessDate = new Date(2026, 0, 15);
const maxFutureDate = new Date(2100, 0, 1);

const clientTemplate: any = {
officeOptions: [{ id: 1, name: 'Head Office' }],
staffOptions: [],
clientLegalFormOptions: [
{ id: LegalFormId.PERSON, value: 'PERSON' },
{ id: LegalFormId.ENTITY, value: 'ENTITY' }
],
clientTypeOptions: [],
clientClassificationOptions: [],
clientNonPersonMainBusinessLineOptions: [],
clientNonPersonConstitutionOptions: [],
genderOptions: [],
savingProductOptions: []
};

/** The `Incorporation Validity Till Date` datepicker, as bound in the template. */
function incorpValidityDatePicker(): MatDatepickerInput<Date> {
return fixture.debugElement
.query(By.css('input[formControlName="incorpValidityTillDate"]'))
.injector.get<MatDatepickerInput<Date>>(MatDatepickerInput);
}

function incorpValidityControl() {
return component.createClientForm.get('clientNonPersonDetails.incorpValidityTillDate');
}

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
ClientGeneralStepComponent,
TranslateModule.forRoot()
],
providers: [
provideNativeDateAdapter(),
provideNoopAnimations(),
{ provide: CdkStepper, useValue: { next: jest.fn(), previous: jest.fn() } },
{ provide: Dates, useValue: { formatDate: jest.fn(() => '15 January 2026') } },
{
provide: SettingsService,
useValue: { businessDate, maxFutureDate, dateFormat: 'dd MMMM yyyy', language: { code: 'en-US' } }
},
{ provide: ClientsService, useValue: { getClientWithOfficeTemplate: jest.fn(() => of(clientTemplate)) } }
]
}).compileComponents();

const faIconLibrary = TestBed.inject(FaIconLibrary);
faIconLibrary.addIcons(
...Object.keys(solidIcons)
.filter((key) => key !== 'fas' && key !== 'prefix' && key.startsWith('fa'))
.map((icon) => (solidIcons as any)[icon])
);

fixture = TestBed.createComponent(ClientGeneralStepComponent);
component = fixture.componentInstance;
component.clientTemplate = clientTemplate;
fixture.detectChanges();

component.createClientForm.get('legalFormId').patchValue(LegalFormId.ENTITY);
fixture.detectChanges();
});

it('allows future incorporation validity dates', () => {
expect(incorpValidityDatePicker().max).toEqual(maxFutureDate);
});

it('falls back to the generic minimum when no incorporation date is set', () => {
expect(incorpValidityDatePicker().min).toEqual(component.minDate);
});

it('rejects validity dates before the incorporation date', () => {
const incorporationDate = new Date(2026, 5, 10);
component.createClientForm.get('dateOfBirth').patchValue(incorporationDate);
fixture.detectChanges();

expect(incorpValidityDatePicker().min).toEqual(incorporationDate);

incorpValidityControl().patchValue(new Date(2026, 4, 10));

expect(incorpValidityControl().hasError('matDatepickerMin')).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ export class ClientGeneralStepComponent implements OnInit {
minDate = new Date(2000, 0, 1);
/** Maximum date allowed. */
maxDate = new Date();
/** Maximum date allowed for fields that accept future dates. */
maxFutureDate = this.settingsService.maxFutureDate;

/** Client Template */
@Input() clientTemplate: any;
Expand Down
4 changes: 2 additions & 2 deletions src/app/clients/edit-client/edit-client.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,8 @@ <h3 class="form-section-title">{{ 'labels.heading.' + productionGeneralHeading |
[placeholder]="'labels.inputs.Incorporation Validity Till Date' | translate"
[title]="'labels.inputs.Incorporation Validity Till Date' | translate"
matInput
[min]="minDate"
[max]="maxDate"
[min]="editClientForm.value.dateOfBirth || minDate"
[max]="maxFutureDate"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
[matDatepicker]="incorpValidityTillDateDatePicker"
formControlName="incorpValidityTillDate"
/>
Expand Down
56 changes: 56 additions & 0 deletions src/app/clients/edit-client/edit-client.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ActivatedRoute, Router } from '@angular/router';
import { provideNativeDateAdapter } from '@angular/material/core';
import { MatDatepickerInput } from '@angular/material/datepicker';
import { By } from '@angular/platform-browser';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { TranslateModule } from '@ngx-translate/core';
import { of, throwError } from 'rxjs';
Expand All @@ -29,6 +31,8 @@ describe('EditClientComponent WEB-1161 production edit flow', () => {
let router: jest.Mocked<Router>;
let originalProductionMode: boolean;

const maxFutureDate = new Date(2100, 0, 1);

const clientDataAndTemplate: any = {
id: 1,
officeId: 1,
Expand Down Expand Up @@ -284,6 +288,7 @@ describe('EditClientComponent WEB-1161 production edit flow', () => {
provide: SettingsService,
useValue: {
businessDate: new Date(2024, 0, 15),
maxFutureDate,
language: { code: 'en' },
dateFormat: 'dd MMMM yyyy'
}
Expand Down Expand Up @@ -854,4 +859,55 @@ describe('EditClientComponent WEB-1161 production edit flow', () => {
expect.objectContaining({ is_pep: true, pep_details: 'Public office' })
);
});

describe('WEB-1103 incorporation validity till date', () => {
/** The `Incorporation Validity Till Date` datepicker, as bound in the edit template. */
function incorpValidityDatePicker(): MatDatepickerInput<Date> {
return fixture.debugElement
.query(By.css('input[formControlName="incorpValidityTillDate"]'))
.injector.get<MatDatepickerInput<Date>>(MatDatepickerInput);
}

function configureEntityClient(dateOfBirth: any) {
configureTestingModule(true, {
legalForm: { id: 2 },
fullname: 'Acme Holdings LLC',
dateOfBirth,
clientNonPersonDetails: {
constitution: { id: 1 },
mainBusinessLine: { id: 2 },
incorpNumber: 'INC-1'
},
clientNonPersonConstitutionOptions: [{ id: 1, name: 'LLC' }],
clientNonPersonMainBusinessLineOptions: [{ id: 2, name: 'Services' }]
});
}

it('allows future incorporation validity dates', () => {
configureEntityClient(null);

expect(incorpValidityDatePicker().max).toEqual(maxFutureDate);
});

it('falls back to the generic minimum when no incorporation date is set', () => {
configureEntityClient(null);

expect(incorpValidityDatePicker().min).toEqual(component.minDate);
});

it('rejects validity dates before the incorporation date', () => {
const incorporationDate = new Date(2026, 5, 10);
configureEntityClient(null);

component.editClientForm.get('dateOfBirth').patchValue(incorporationDate);
fixture.detectChanges();

expect(incorpValidityDatePicker().min).toEqual(incorporationDate);

const validityControl = component.editClientForm.get('clientNonPersonDetails.incorpValidityTillDate');
validityControl.patchValue(new Date(2026, 4, 10));

expect(validityControl.hasError('matDatepickerMin')).toBe(true);
});
});
});
2 changes: 2 additions & 0 deletions src/app/clients/edit-client/edit-client.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ export class EditClientComponent implements OnInit {
minDate = new Date(2000, 0, 1);
/** Maximum date allowed. */
maxDate = new Date();
/** Maximum date allowed for fields that accept future dates. */
maxFutureDate = this.settingsService.maxFutureDate;

/** Client Data and Template */
clientDataAndTemplate: any;
Expand Down
Loading