diff --git a/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts b/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts index ebcd72e6ba..34dee9e287 100644 --- a/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +++ b/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts @@ -600,7 +600,6 @@ export function readAbstract(rootEl: XmlElement): string { } export function readContacts(rootEl: XmlElement): Individual[] { - console.log('converter : readContacts') return pipe( findChildrenElement('gmd:contact', false), mapArray(findChildElement('gmd:CI_ResponsibleParty', false)), @@ -609,7 +608,6 @@ export function readContacts(rootEl: XmlElement): Individual[] { } export function readContactsForResource(rootEl: XmlElement): Individual[] { - console.log('converter : readContactsForResource') return pipe( findIdentification(), combine( diff --git a/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts b/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts index 38ec9f59b4..8471232292 100644 --- a/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +++ b/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts @@ -782,7 +782,6 @@ export function writeStatus(record: DatasetRecord, rootEl: XmlElement) { } export function writeContacts(record: CatalogRecord, rootEl: XmlElement) { - console.log('converter : writeContacts') pipe( removeChildrenByName('gmd:contact'), appendChildren( @@ -797,7 +796,6 @@ export function writeContactsForResource( record: CatalogRecord, rootEl: XmlElement ) { - console.log('converter : writeContactsForResource') pipe( findOrCreateIdentification(), removeChildrenByName('gmd:pointOfContact'), diff --git a/libs/common/fixtures/src/index.ts b/libs/common/fixtures/src/index.ts index 71987343f8..232576ae5d 100644 --- a/libs/common/fixtures/src/index.ts +++ b/libs/common/fixtures/src/index.ts @@ -10,6 +10,7 @@ export * from './lib/record-link.fixtures' export * from './lib/records.fixtures' export * from './lib/repository.fixtures' export * from './lib/user.fixtures' +export * from './lib/individual.fixtures' export * from './lib/user-feedbacks.fixtures' export * from './lib/editor' diff --git a/libs/common/fixtures/src/lib/individual.fixtures.ts b/libs/common/fixtures/src/lib/individual.fixtures.ts new file mode 100644 index 0000000000..149281d954 --- /dev/null +++ b/libs/common/fixtures/src/lib/individual.fixtures.ts @@ -0,0 +1,61 @@ +import { Individual } from '@geonetwork-ui/common/domain/model/record' +import { barbieIncOrganizationFixture } from './organisations.fixture' + +export const createIndividualFixture = ( + overrides: Partial = {} +): Individual => ({ + firstName: 'Arnaud', + lastName: 'Demaison', + email: 'a.demaison@geo2france.fr', + organization: barbieIncOrganizationFixture(), + role: 'point_of_contact', + ...overrides, +}) + +export const barbieIndividualFixture = (): Individual => + createIndividualFixture({ + firstName: 'Barbara', + lastName: 'Roberts', + email: 'barbie@email.org', + organization: barbieIncOrganizationFixture(), + role: 'point_of_contact', + }) + +export const someIndividualsFixture = (): Individual[] => [ + barbieIndividualFixture(), + { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@email.com', + organization: barbieIncOrganizationFixture(), + role: 'owner', + }, + { + firstName: 'Alice', + lastName: 'Smith', + email: 'alice.smith@workplace.com', + organization: barbieIncOrganizationFixture(), + role: 'author', + }, + { + firstName: 'Michael', + lastName: 'Johnson', + email: 'michael.j@company.org', + organization: barbieIncOrganizationFixture(), + role: 'contributor', + }, + { + firstName: 'Emma', + lastName: 'Williams', + email: 'emma.w@business.io', + organization: barbieIncOrganizationFixture(), + role: 'rights_holder', + }, + { + firstName: 'David', + lastName: 'Brown', + email: 'david.brown@enterprise.net', + organization: barbieIncOrganizationFixture(), + role: 'stakeholder', + }, +] diff --git a/libs/common/fixtures/src/lib/user.fixtures.ts b/libs/common/fixtures/src/lib/user.fixtures.ts index b6399d0476..5d110f9835 100644 --- a/libs/common/fixtures/src/lib/user.fixtures.ts +++ b/libs/common/fixtures/src/lib/user.fixtures.ts @@ -28,6 +28,19 @@ export const barbieUserFixture = (): UserModel => 'https://www.gravatar.com/avatar/dbdffd183622800bcf8587328daf43a6?d=mp', }) +export const johnDoeUserFixture = (): UserModel => + createUserFixture({ + id: '12345', + profile: 'User', + username: 'johndoe', + name: 'John', + surname: 'Doe', + email: 'johndoe@email.com', + organisation: 'Doe Enterprises', + profileIcon: + 'https://www.gravatar.com/avatar/5f6d74eabcb57186a12f7c8ba40b4c9f?d=mp', + }) + export const ghostUserFixture = (): UserModel => createUserFixture({ id: '161', diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.spec.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.spec.ts index 0f21913bb4..9bd44bc4e2 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.spec.ts +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.spec.ts @@ -1,75 +1,71 @@ import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormFieldContactsComponent } from './form-field-contacts.component' +import { ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core' import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface' -import { BehaviorSubject } from 'rxjs' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' +import { BehaviorSubject, of } from 'rxjs' import { Individual, Organization, - Role, } from '@geonetwork-ui/common/domain/model/record' -import { ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core' -import { UserModel } from '@geonetwork-ui/common/domain/model/user' -import { CommonModule } from '@angular/common' +import { MockBuilder, MockInstance, MockProvider } from 'ng-mocks' import { TranslateModule } from '@ngx-translate/core' -import { ContactCardComponent } from '../../../contact-card/contact-card.component' import { - AutocompleteComponent, - DropdownSelectorComponent, - UiInputsModule, -} from '@geonetwork-ui/ui/inputs' -import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' -import { FormControl } from '@angular/forms' + barbieIncOrganizationFixture, + barbieUserFixture, + someIndividualsFixture, + someOrganizationsFixture, + someUsersFixture, +} from '@geonetwork-ui/common/fixtures' -const organizationBarbie: Organization = { - name: 'Barbie Inc.', -} +describe('FormFieldContactsComponent', () => { + MockInstance.scope() -const organizationGoogle: Organization = { - name: 'Google', -} - -class MockPlatformServiceInterface { - getUsers = jest.fn(() => new BehaviorSubject([])) -} - -class MockOrganizationsServiceInterface { - organisations$ = new BehaviorSubject([organizationBarbie, organizationGoogle]) -} - -describe('FormFieldContactsForResourceComponent', () => { let component: FormFieldContactsComponent let fixture: ComponentFixture + let platformServiceInterface: PlatformServiceInterface + let organizationsServiceInterface: OrganizationsServiceInterface + let changeDetectorRef: ChangeDetectorRef - beforeEach(async () => { - await TestBed.configureTestingModule({ - imports: [ - FormFieldContactsComponent, - CommonModule, - TranslateModule.forRoot(), - UiInputsModule, - ContactCardComponent, - DropdownSelectorComponent, - ], + beforeEach(() => { + return MockBuilder(FormFieldContactsComponent) + }) + + const mockUsers = someUsersFixture() + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [FormFieldContactsComponent, TranslateModule.forRoot()], providers: [ - { - provide: PlatformServiceInterface, - useClass: MockPlatformServiceInterface, - }, - { - provide: OrganizationsServiceInterface, - useClass: MockOrganizationsServiceInterface, - }, - ChangeDetectorRef, + MockProvider(OrganizationsServiceInterface), + MockProvider(PlatformServiceInterface, { + getUsers: jest.fn().mockReturnValue(of(mockUsers)), + }), + MockProvider(ChangeDetectorRef, { + markForCheck: jest.fn().mockReturnValue({}), + }), ], + }).overrideComponent(FormFieldContactsComponent, { + set: { + changeDetection: ChangeDetectionStrategy.Default, + }, }) - .overrideComponent(AutocompleteComponent, { - set: { changeDetection: ChangeDetectionStrategy.Default }, - }) - .compileComponents() fixture = TestBed.createComponent(FormFieldContactsComponent) component = fixture.componentInstance - component.control = new FormControl([]) + + changeDetectorRef = TestBed.inject(ChangeDetectorRef) + platformServiceInterface = TestBed.inject(PlatformServiceInterface) + organizationsServiceInterface = TestBed.inject( + OrganizationsServiceInterface + ) + + organizationsServiceInterface.organisations$ = new BehaviorSubject( + someOrganizationsFixture() + ) + + component.value = [] + fixture.detectChanges() }) @@ -77,148 +73,121 @@ describe('FormFieldContactsForResourceComponent', () => { expect(component).toBeTruthy() }) - describe('ngOnInit', () => { - it('should initialize organizations', async () => { - await component.ngOnInit() + describe('ngOnChanges', () => { + it('should initialize allOrganizations on first change', async () => { + const orgs: Organization[] = [{ name: 'Org1' }, { name: 'Org2' }] + organizationsServiceInterface.organisations$ = of(orgs) + + component.value = [] + await component.ngOnChanges({ + value: { + currentValue: [], + previousValue: undefined, + firstChange: true, + isFirstChange: () => true, + }, + }) expect(component.allOrganizations.size).toBe(2) + expect(component.allOrganizations.get('Org1')).toEqual({ name: 'Org1' }) + expect(component.allOrganizations.get('Org2')).toEqual({ name: 'Org2' }) }) - }) - - describe('addRoleToDisplay', () => { - it('should add role to display and filter roles to pick', () => { - const initialRolesToPick = [...component.rolesToPick] - const roleToAdd = initialRolesToPick[0] - - component.addRoleToDisplay(roleToAdd) - - expect(component.roleSectionsToDisplay).toContain(roleToAdd) - expect(component.rolesToPick).not.toContain(roleToAdd) - }) - }) - - describe('filterRolesToPick', () => { - it('should filter roles already in roleSectionsToDisplay', () => { - component.rolesToPick = ['custodian', 'owner'] as Role[] - component.roleSectionsToDisplay = ['custodian'] as Role[] - - component.filterRolesToPick() - - expect(component.rolesToPick).toEqual(['owner']) - }) - }) - - describe('updateContactsForRessource', () => { - it('should update contactsForRessourceByRole and contactsAsDynElemByRole', () => { - const mockContact: Individual = { - role: 'owner', - organization: { name: 'Org1' } as Organization, - } as Individual - component.allOrganizations.set('Org1', { name: 'Org1' } as Organization) - component.control.setValue([mockContact]) + it('should update contacts and mark for check when value changes', async () => { + jest.spyOn(component, 'updateContacts') - component.updateContacts() + component.value = [] + await component.ngOnChanges({ + value: { + currentValue: [], + previousValue: null, + firstChange: false, + isFirstChange: () => false, + }, + }) - expect(component.contactsForRessourceByRole.get('owner')).toEqual([ - mockContact, - ]) - expect(component.contactsAsDynElemByRole.get('owner').length).toBe(1) + expect(component.updateContacts).toHaveBeenCalled() }) }) - describe('manageRoleSectionsToDisplay', () => { - it('should add new roles to roleSectionsToDisplay', () => { - const mockContact: Individual = { - role: 'owner', - organization: { name: 'Org1' } as Organization, - } as Individual - - component.manageRoleSectionsToDisplay([mockContact]) - - expect(component.roleSectionsToDisplay).toContain('owner') + describe('updateContacts', () => { + beforeEach(async () => { + await component.ngOnChanges({ + value: { + currentValue: [], + previousValue: undefined, + firstChange: true, + isFirstChange: () => true, + }, + }) }) - }) - describe('removeContact', () => { - it('should remove contact at specified index', () => { - const mockContacts: Individual[] = [ + it('should update contacts with complete organization data', () => { + component.value = [ { - role: 'owner', - organization: { name: 'Org1' } as Organization, - } as Individual, + ...someIndividualsFixture()[0], + organization: { name: barbieIncOrganizationFixture().name }, + }, { - role: 'custodian', - organization: { name: 'Org2' } as Organization, - } as Individual, + ...someIndividualsFixture()[1], + organization: { name: barbieIncOrganizationFixture().name }, + }, ] - component.control.setValue(mockContacts) - component.removeContact(0) + component.updateContacts() - expect(component.control.value.length).toBe(1) - expect(component.control.value[0]).toEqual(mockContacts[1]) + expect(component.contacts.length).toBe(2) + expect(component.contacts[0].organization).toEqual( + barbieIncOrganizationFixture() + ) + expect(component.contacts[1].organization).toEqual( + barbieIncOrganizationFixture() + ) }) }) describe('handleContactsChanged', () => { - it('should update contacts based on reordered dynamic elements', () => { - const mockContacts: Individual[] = [ - { - role: 'owner', - organization: { name: 'Org1' } as Organization, - } as Individual, - { - role: 'owner', - organization: { name: 'Org2' } as Organization, - } as Individual, - ] + it('should update contacts and emit valueChange', () => { + const contacts: Individual[] = someIndividualsFixture() + jest.spyOn(component.valueChange, 'emit') - component.contactsForRessourceByRole.set('owner', [mockContacts[0]]) - component.contactsForRessourceByRole.set('owner', [mockContacts[1]]) + component.handleContactsChanged(contacts) - const reorderedElements = [ - { inputs: { contact: mockContacts[1] } } as any, - { inputs: { contact: mockContacts[0] } } as any, - ] - - component.handleContactsChanged(reorderedElements) - - const newControlValue = component.control.value - expect(newControlValue[0]).toEqual(mockContacts[1]) - expect(newControlValue[1]).toEqual(mockContacts[0]) + expect(component.contacts).toEqual(contacts) + expect(component.valueChange.emit).toHaveBeenCalledWith(contacts) }) }) describe('addContact', () => { - it('should add a new contact to the control value', () => { - const mockUser: UserModel = { - username: 'user1', - name: 'John', - surname: 'Doe', - organisation: 'Org1', - } as UserModel - + it('should add the contact and emit new contacts', () => { + const spy = jest.spyOn(component.valueChange, 'emit') + const mockUser = barbieUserFixture() component.allOrganizations.set('Org1', { name: 'Org1' } as Organization) - const initialContacts = component.control.value.length - component.addContact(mockUser, 'owner') + component.addContact(mockUser) - expect(component.control.value.length).toBe(initialContacts + 1) - expect(component.control.value[initialContacts].role).toBe('owner') - expect(component.control.value[initialContacts].organization.name).toBe( - 'Org1' - ) + expect(spy).toHaveBeenCalledWith([ + { + address: '', + email: 'barbie@email.org', + firstName: 'Barbara', + lastName: 'Roberts', + organization: { name: 'Barbie Inc.' } as Organization, + phone: '', + position: '', + role: 'point_of_contact', + }, + ]) }) }) describe('ngOnDestroy', () => { - it('should unsubscribe from all subscriptions', () => { - const subscriptionSpy = jest.spyOn(component.subscription, 'unsubscribe') + it('should unsubscribe from subscriptions', () => { + jest.spyOn(component.subscription, 'unsubscribe') component.ngOnDestroy() - expect(subscriptionSpy).toHaveBeenCalled() + expect(component.subscription.unsubscribe).toHaveBeenCalled() }) }) }) diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts index 3261c014a5..909557f721 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts @@ -19,7 +19,6 @@ import { UiWidgetsModule } from '@geonetwork-ui/ui/widgets' import { Individual, Organization, - Role, } from '@geonetwork-ui/common/domain/model/record' import { TranslateModule } from '@ngx-translate/core' import { @@ -65,8 +64,6 @@ export class FormFieldContactsComponent implements OnDestroy, OnChanges { allUsers$: Observable - rolesToPick: Role[] = ['point_of_contact'] - allOrganizations: Map = new Map() constructor(