diff --git a/e2e/client-legal-form-search.spec.ts b/e2e/client-legal-form-search.spec.ts new file mode 100644 index 000000000..5ea887d44 --- /dev/null +++ b/e2e/client-legal-form-search.spec.ts @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/** + * Regression coverage for #534: the main /clients list hardcoded legalForm 1 (Person) when + * calling getClients, which silently excluded Entity clients from the list. Seeded through the + * API against a real Fineract, because the bug was in what the app asks the backend for — a + * page.route() mock would echo back whatever list we hand it and could never have caught this. + * + * npx playwright test e2e/client-legal-form-search.spec.ts --project=backend --workers=1 + */ + +import { test, expect } from './fixtures'; +import { login } from './utils/fineract-login'; +import { createApiContext, seedEntityClient } from './utils/seed-api'; + +test.describe('Client list: legal form filtering', () => { + test('an entity client appears in the main client list', async ({ page }) => { + await login(page); + const api = await createApiContext(); + let entity; + try { + entity = await seedEntityClient(api); + } finally { + await api.dispose(); + } + + await page.goto('/clients'); + await expect(page.getByText(entity.displayName)).toBeVisible({ timeout: 10_000 }); + }); +}); diff --git a/e2e/utils/seed-api.ts b/e2e/utils/seed-api.ts index 8d179ae12..9f2bf4e0e 100644 --- a/e2e/utils/seed-api.ts +++ b/e2e/utils/seed-api.ts @@ -308,6 +308,24 @@ export async function seedClient( return { clientId, firstName, lastName, displayName: `${firstName} ${lastName}` }; } +export async function seedEntityClient( + api: APIRequestContext, + namePrefix = 'E2ESeedEntity', + officeId = 1, +): Promise { + const fullname = `${namePrefix}${seedSuffix()} Pvt Ltd`; + const { clientId } = await post<{ clientId: number }>(api, '/clients', { + officeId, + fullname, + legalFormId: 2, + active: true, + activationDate: fineractDate(), + dateFormat: DATE_FORMAT, + locale: LOCALE, + }); + return { clientId, firstName: fullname, lastName: '', displayName: fullname }; +} + export interface SeededFixedDeposit { accountId: number; clientId: number; diff --git a/playwright.config.ts b/playwright.config.ts index 822a10599..028ff8105 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -59,6 +59,7 @@ const BACKEND_SPECS = [ 'rbac-backend-restricted-user.spec.ts', 'rbac-multi-permission.spec.ts', 'client-transfer.spec.ts', + 'client-legal-form-search.spec.ts', 'deposit-account-servicing.spec.ts', 'deposit-product-configuration.spec.ts', 'full-demo.spec.ts', diff --git a/src/app/features/clients/clients-list.component.test.ts b/src/app/features/clients/clients-list.component.test.ts new file mode 100644 index 000000000..e8fdfaa0d --- /dev/null +++ b/src/app/features/clients/clients-list.component.test.ts @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { createSpyObj, SpyObj } from '../../testing/mocks'; +import { provideNoopAnimations } from '@angular/platform-browser/animations'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; +import { of } from 'rxjs'; + +import { ClientsListComponent } from './clients-list.component'; +import { ClientService } from '../../api'; +import { provideIonicTesting } from '../../testing/ionic-testing'; +import { provideTranslateTesting } from '../../testing/i18n-testing'; + +describe('ClientsListComponent', () => { + let fixture: ComponentFixture; + let serviceSpy: SpyObj; + + beforeEach(async () => { + serviceSpy = createSpyObj(['getClients']); + serviceSpy.getClients.mockReturnValue( + of({ totalFilteredRecords: 0, pageItems: [] }) as unknown as ReturnType< + ClientService['getClients'] + >, + ); + + await TestBed.configureTestingModule({ + imports: [ClientsListComponent], + providers: [ + provideNoopAnimations(), + provideIonicTesting(), + ...provideTranslateTesting(), + { provide: ClientService, useValue: serviceSpy }, + { provide: Router, useValue: createSpyObj(['navigate']) }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ClientsListComponent); + fixture.detectChanges(); + }); + + it('does not filter the main list by legal form', () => { + // 13th positional argument to getClients — see #534. + const legalForm = serviceSpy.getClients.mock.calls[0][12]; + expect(legalForm).toBeUndefined(); + }); +}); diff --git a/src/app/features/clients/clients-list.component.ts b/src/app/features/clients/clients-list.component.ts index 506261479..215218f44 100644 --- a/src/app/features/clients/clients-list.component.ts +++ b/src/app/features/clients/clients-list.component.ts @@ -231,7 +231,7 @@ export class ClientsListComponent { orderBy, sortOrder, false, - 1, + undefined, ) .pipe( tap(() => this.hasError.set(false)),