From 1cc3bf3c2038928d4802597def34c6581f1940a6 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Mon, 24 Jun 2024 13:02:48 +0200 Subject: [PATCH 1/6] chore(contract): 1037 reference to assets for contract id --- .../contracts/contracts.component.ts | 17 +++++++++++++++-- .../app/modules/page/parts/model/parts.model.ts | 4 ++++ .../modules/shared/assembler/parts.assembler.ts | 2 ++ .../parts-as-built-configuration.model.ts | 1 + .../parts-as-planned-configuration.model.ts | 1 + frontend/src/assets/locales/de/common.json | 7 +++++-- frontend/src/assets/locales/en/common.json | 7 +++++-- 7 files changed, 33 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.ts b/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.ts index 8726d3840c..923d740666 100644 --- a/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.ts +++ b/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { Component, EventEmitter } from '@angular/core'; import { Router } from '@angular/router'; import { Pagination } from '@core/model/pagination.model'; import { AdminFacade } from '@page/admin/core/admin.facade'; @@ -22,6 +22,7 @@ export class ContractsComponent { selectedContracts: Contract[]; contractFilter: any; pagination: TableEventConfig; + viewAssetsClicked: EventEmitter = new EventEmitter; constructor(public readonly adminFacade: AdminFacade, private readonly contractsFacade: ContractsFacade, private readonly router: Router) {} @@ -33,13 +34,25 @@ export class ContractsComponent { } else { this.contractsFacade.setContracts(0,10,[null,null]); } + + this.viewAssetsClicked.subscribe((data) => { + this.router.navigate([ 'parts' ], { queryParams: { contractId: data?.['contractId'] } }); + }); }) this.pagination = { page: 0, pageSize: 10, sorting: [ '', null ] }; this.tableConfig = { displayedColumns: [ 'select', 'contractId', 'counterpartyAddress', 'creationDate', 'endDate', 'state', 'menu' ], header: CreateHeaderFromColumns([ 'contractId', 'counterpartyAddress', 'creationDate', 'endDate', 'state', 'menu' ], 'pageAdmin.contracts'), - menuActionsConfig: [], + menuActionsConfig: [ + { + label: 'actions.viewParts', + icon: 'build', + action: (data: Record) => { + this.viewAssetsClicked.emit(data); + }, + }, + ], sortableColumns: { select: false, contractId: true, diff --git a/frontend/src/app/modules/page/parts/model/parts.model.ts b/frontend/src/app/modules/page/parts/model/parts.model.ts index 4296355d39..2e942e1a1f 100644 --- a/frontend/src/app/modules/page/parts/model/parts.model.ts +++ b/frontend/src/app/modules/page/parts/model/parts.model.ts @@ -42,6 +42,7 @@ export interface Part { owner: Owner; semanticDataModel: SemanticDataModel; classification: string; + contractAgreementId?: string; mainAspectType: MainAspectType; @@ -100,6 +101,7 @@ export interface PartResponse { receivedQualityInvestigationIdsInStatusActive: string[] importNote?: string, importState?: ImportState, + contractAgreementId?: string, tombstone?: string, } @@ -153,6 +155,7 @@ export interface AssetAsBuiltFilter { partId?: string, manufacturerPartId?: string, customerPartId?: string, + contractAgreementId?: string, classification?: string, nameAtCustomer?: string, semanticModelId?: string, @@ -170,6 +173,7 @@ export interface AssetAsPlannedFilter { businessPartner?: string, manufacturerPartId?: string, classification?: string, + contractAgreementId?: string, semanticDataModel?: string[], semanticModelId?: string, validityPeriodFrom?: string, diff --git a/frontend/src/app/modules/shared/assembler/parts.assembler.ts b/frontend/src/app/modules/shared/assembler/parts.assembler.ts index db8cbeebb3..1d6ebf7c04 100644 --- a/frontend/src/app/modules/shared/assembler/parts.assembler.ts +++ b/frontend/src/app/modules/shared/assembler/parts.assembler.ts @@ -89,6 +89,7 @@ export class PartsAssembler { semanticDataModel: partResponse.semanticDataModel, classification: partResponse.classification, semanticModel: createdSemanticModel, + contractAgreementId: partResponse.contractAgreementId, mainAspectType: mainAspectType, @@ -291,6 +292,7 @@ export class PartsAssembler { [ 'semanticDataModel', 'semanticDataModel' ], [ 'classification', 'classification' ], [ 'customerPartId', 'customerPartId' ], + [ 'contractAgreementId', 'contractAgreementId' ], [ 'nameAtCustomer', 'nameAtCustomer' ], [ 'manufacturingDate', 'manufacturingDate' ], [ 'manufacturingCountry', 'manufacturingCountry' ], diff --git a/frontend/src/app/modules/shared/components/parts-table/parts-as-built-configuration.model.ts b/frontend/src/app/modules/shared/components/parts-table/parts-as-built-configuration.model.ts index d997f3245e..e8448f6b4b 100644 --- a/frontend/src/app/modules/shared/components/parts-table/parts-as-built-configuration.model.ts +++ b/frontend/src/app/modules/shared/components/parts-table/parts-as-built-configuration.model.ts @@ -42,6 +42,7 @@ export class PartsAsBuiltConfigurationModel extends TableFilterConfiguration { sentActiveInvestigations: true, importState: true, importNote: true, + contractAgreementId: true, menu: false, }; diff --git a/frontend/src/app/modules/shared/components/parts-table/parts-as-planned-configuration.model.ts b/frontend/src/app/modules/shared/components/parts-table/parts-as-planned-configuration.model.ts index 011068b42f..3c7bc22542 100644 --- a/frontend/src/app/modules/shared/components/parts-table/parts-as-planned-configuration.model.ts +++ b/frontend/src/app/modules/shared/components/parts-table/parts-as-planned-configuration.model.ts @@ -41,6 +41,7 @@ export class PartsAsPlannedConfigurationModel extends TableFilterConfiguration { functionValidUntil: true, importState: true, importNote: true, + contractAgreementId: true, menu: false, }; diff --git a/frontend/src/assets/locales/de/common.json b/frontend/src/assets/locales/de/common.json index 3392a0067e..37ea435d04 100644 --- a/frontend/src/assets/locales/de/common.json +++ b/frontend/src/assets/locales/de/common.json @@ -69,7 +69,9 @@ "maximizeTable": "Volle Breite", "userSettings" : "Tabellen Einstellung", "uploadFile" : "Richtlinien JSON-Datei hochladen", - "downloadFile" : "Vorlage als Datei herunterladen" + "downloadFile" : "Vorlage als Datei herunterladen", + "viewParts" : "Produkte ansehen", + "viewPartsTooltip" : "Produkte mit diesem Vertrag ansehen" }, "publisher": { "selectedAssets": "Ausgewählte Produkte", @@ -175,7 +177,8 @@ "policyName" : "Richtlinienname", "bpn" : "BPN Selektion", "constraints" : "Bedingungen", - "accessType" : "Zugriffsart" + "accessType" : "Zugriffsart", + "contractAgreementId" : "Vertragsvereinbarungs-ID" } }, "dataLoading": { diff --git a/frontend/src/assets/locales/en/common.json b/frontend/src/assets/locales/en/common.json index 3538e19c9f..3ec25d26ad 100644 --- a/frontend/src/assets/locales/en/common.json +++ b/frontend/src/assets/locales/en/common.json @@ -67,7 +67,9 @@ "maximizeTable": "Full width", "userSettings" : "Table settings", "uploadFile" : "Upload policy JSON file", - "downloadFile" : "Download template as file" + "downloadFile" : "Download template as file", + "viewParts" : "View parts", + "viewPartsTooltip" : "View parts using this contract" }, "publisher": { "selectedAssets": "Assets selected", @@ -171,7 +173,8 @@ "policyName" : "Policy name", "bpn" : "BPN selection", "constraints" : "Constraints", - "accessType" : "Access type" + "accessType" : "Access type", + "contractAgreementId" : "Contract agreement ID" } }, "dataLoading": { From 0cf1217ddbef2249cc3de25226f9690fd7c5a6fb Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Mon, 24 Jun 2024 15:48:22 +0200 Subject: [PATCH 2/6] chore(contract): 1037 reference to assets for contract id --- .../multi-select-autocomplete.component.ts | 10 +++++++++- .../components/parts-table/parts-table.component.html | 1 + .../components/parts-table/parts-table.component.ts | 5 ++++- .../shared/components/table/table.component.html | 10 ++++++++++ frontend/src/assets/locales/de/common.json | 2 +- frontend/src/assets/locales/en/common.json | 2 +- 6 files changed, 26 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.ts b/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.ts index 59d574c0d4..453c446046 100644 --- a/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.ts +++ b/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.ts @@ -106,6 +106,7 @@ export class MultiSelectAutocompleteComponent implements OnChanges { suggestionError: boolean = false; isLoadingSuggestions: boolean; + @Input() prefilterValue?: string; constructor(public datePipe: DatePipe, public _adapter: DateAdapter, @Inject(MAT_DATE_LOCALE) public _locale: string, @Inject(LOCALE_ID) private locale: string, public partsService: PartsService, @@ -125,6 +126,13 @@ export class MultiSelectAutocompleteComponent implements OnChanges { } }); + if (this.prefilterValue?.length > 0) { + this.searchElement = this.prefilterValue; + this.selectedValue = [ this.searchElement ]; + this.formControl.patchValue(this.selectedValue); + this.updateOptionsAndSelections(); + } + } ngOnChanges(): void { @@ -184,7 +192,7 @@ export class MultiSelectAutocompleteComponent implements OnChanges { filterItem(value: any): void { - if (!this.searchElement.length) { + if (!this.searchElement?.length) { return; } diff --git a/frontend/src/app/modules/shared/components/parts-table/parts-table.component.html b/frontend/src/app/modules/shared/components/parts-table/parts-table.component.html index c889fd4827..80c4249da0 100644 --- a/frontend/src/app/modules/shared/components/parts-table/parts-table.component.html +++ b/frontend/src/app/modules/shared/components/parts-table/parts-table.component.html @@ -135,6 +135,7 @@ [isDate]="filter.isDate" [singleSearch]="filter.singleSearch" [filterColumn]="filter.filterKey" + [prefilterValue]="filter.headerKey === 'filtercontractAgreementId' ? preFilter : null" [tableType]="tableType" [inAssetIds]="assetIdsForAutoCompleteFilter" [placeholderMultiple]="('multiSelect.multipleResults' | i18n)" diff --git a/frontend/src/app/modules/shared/components/parts-table/parts-table.component.ts b/frontend/src/app/modules/shared/components/parts-table/parts-table.component.ts index 9ec976ecd5..673ab802c7 100644 --- a/frontend/src/app/modules/shared/components/parts-table/parts-table.component.ts +++ b/frontend/src/app/modules/shared/components/parts-table/parts-table.component.ts @@ -34,7 +34,7 @@ import { MatDialog, MatDialogConfig } from '@angular/material/dialog'; import { MatPaginator, PageEvent } from '@angular/material/paginator'; import { MatSort, Sort } from '@angular/material/sort'; import { MatTableDataSource } from '@angular/material/table'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { EmptyPagination, Pagination } from '@core/model/pagination.model'; import { RoleService } from '@core/user/role.service'; import { TableSettingsService } from '@core/user/table-settings.service'; @@ -88,6 +88,7 @@ export class PartsTableComponent implements OnInit { @Input() assetIdsForAutoCompleteFilter: string[]; + preFilter: string; public tableConfig: TableConfig; @Input() set paginationData({ page, pageSize, totalItems, content }: Pagination) { @@ -134,9 +135,11 @@ export class PartsTableComponent implements OnInit { public readonly userSettingsService: BomLifecycleSettingsService, private dialog: MatDialog, private router: Router, + private route: ActivatedRoute, private deeplinkService: DeeplinkService, public roleService: RoleService, ) { + this.preFilter = route.snapshot.queryParams['contractId']; } handleKeyDownOpenDialog(event: KeyboardEvent) { diff --git a/frontend/src/app/modules/shared/components/table/table.component.html b/frontend/src/app/modules/shared/components/table/table.component.html index ed659cab80..83c43ef666 100644 --- a/frontend/src/app/modules/shared/components/table/table.component.html +++ b/frontend/src/app/modules/shared/components/table/table.component.html @@ -341,7 +341,17 @@

{{ 'table.noResultFound' | i18n }}

mat-menu-item > {{ config.icon }} +
{{ config.label | i18n }} +
diff --git a/frontend/src/assets/locales/de/common.json b/frontend/src/assets/locales/de/common.json index 37ea435d04..179d7f857e 100644 --- a/frontend/src/assets/locales/de/common.json +++ b/frontend/src/assets/locales/de/common.json @@ -178,7 +178,7 @@ "bpn" : "BPN Selektion", "constraints" : "Bedingungen", "accessType" : "Zugriffsart", - "contractAgreementId" : "Vertragsvereinbarungs-ID" + "contractAgreementId" : "Vertrags-ID" } }, "dataLoading": { diff --git a/frontend/src/assets/locales/en/common.json b/frontend/src/assets/locales/en/common.json index 3ec25d26ad..83e801f30a 100644 --- a/frontend/src/assets/locales/en/common.json +++ b/frontend/src/assets/locales/en/common.json @@ -174,7 +174,7 @@ "bpn" : "BPN selection", "constraints" : "Constraints", "accessType" : "Access type", - "contractAgreementId" : "Contract agreement ID" + "contractAgreementId" : "Contract ID" } }, "dataLoading": { From f4b807992cdaa0d247f1b46c354af670b8deefc5 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Mon, 24 Jun 2024 16:04:30 +0200 Subject: [PATCH 3/6] chore(contract): 1037 fixed tests --- CHANGELOG.md | 1 + .../src/app/modules/core/user/table-settings.service.spec.ts | 4 ++-- .../components/parts-table/parts-table.component.spec.ts | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a306348c07..7bbde90420 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ _**For better traceability add the corresponding GitHub issue number in each cha - #737 Added concept: Contract table -> parts link action - XXX Added interceptor to EdcRestTemplates to log requests - #915 Added section to documentation: EDC-BPN configuration +- #1037 Added link from contracts view to the corresponding filtered part table view ### Removed diff --git a/frontend/src/app/modules/core/user/table-settings.service.spec.ts b/frontend/src/app/modules/core/user/table-settings.service.spec.ts index 6e2aed7296..bafd29d9a1 100644 --- a/frontend/src/app/modules/core/user/table-settings.service.spec.ts +++ b/frontend/src/app/modules/core/user/table-settings.service.spec.ts @@ -42,12 +42,12 @@ describe('TableSettingsService', () => { it('should return PartsAsPlannedConfigurationModel for AS_PLANNED_OWN', () => { const result: TableViewConfig = service.initializeTableViewSettings(TableType.AS_PLANNED_OWN); - expect(result.displayedColumns.length).toBe(20); + expect(result.displayedColumns.length).toBe(21); }); it('should return PartsAsBuiltConfigurationModel for AS_BUILT_OWN', () => { const result: TableViewConfig = service.initializeTableViewSettings(TableType.AS_BUILT_OWN); - expect(result.displayedColumns.length).toBe(22); + expect(result.displayedColumns.length).toBe(23); }); it('should return NotificationsSentConfigurationModel for SENT_NOTIFICATION', () => { diff --git a/frontend/src/app/modules/shared/components/parts-table/parts-table.component.spec.ts b/frontend/src/app/modules/shared/components/parts-table/parts-table.component.spec.ts index c901166e82..c3d1315501 100644 --- a/frontend/src/app/modules/shared/components/parts-table/parts-table.component.spec.ts +++ b/frontend/src/app/modules/shared/components/parts-table/parts-table.component.spec.ts @@ -140,6 +140,7 @@ describe('PartsTableComponent', () => { 'filtersentActiveInvestigations', 'filterimportState', 'filterimportNote', + 'filtercontractAgreementId', 'Menu', ]); }); @@ -170,6 +171,7 @@ describe('PartsTableComponent', () => { 'filterfunctionValidUntil', 'filterimportState', 'filterimportNote', + 'filtercontractAgreementId', 'Menu', ]); }); From 54fb90b533ead8f7be89de340dda2a7e5a00f49e Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Mon, 24 Jun 2024 17:30:37 +0200 Subject: [PATCH 4/6] chore(contract): 1037 added tests --- .../contracts/contracts.component.spec.ts | 27 +++++++++++++++++-- .../contracts/contracts.component.ts | 7 ++--- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.spec.ts b/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.spec.ts index 141ea7e247..d5c94b99a7 100644 --- a/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.spec.ts +++ b/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.spec.ts @@ -1,4 +1,5 @@ import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; import { AdminModule } from '@page/admin/admin.module'; import { AdminFacade } from '@page/admin/core/admin.facade'; import { assembleContract } from '@page/admin/core/admin.model'; @@ -16,16 +17,20 @@ describe('ContractTableComponent', () => { getContracts: jasmine.createSpy().and.returnValue(of(getContracts)), }; + const routerMock = { + navigate: jasmine.createSpy('navigate'), + }; + const renderContractTableComponent = () => renderComponent(ContractsComponent, { imports: [ AdminModule ], - providers: [ { provide: AdminFacade, useValue: mockAdminFacade } ], + providers: [ { provide: AdminFacade, useValue: mockAdminFacade }, { provide: Router, useValue: routerMock } ], }); let createElementSpy: jasmine.Spy; beforeEach(() => { TestBed.configureTestingModule({ declarations: [ ContractsComponent ], - providers: [ AdminFacade, AdminService ], + providers: [ AdminFacade, AdminService, { provide: Router, useValue: routerMock } ], }); createElementSpy = spyOn(document, 'createElement').and.callThrough(); @@ -82,6 +87,24 @@ describe('ContractTableComponent', () => { }); + it('should navigate if viewAssets clicked', async () => { + const { fixture } = await renderContractTableComponent(); + const { componentInstance } = fixture; + componentInstance.viewAssetsClicked.emit({ contractId: 'test' }); + + expect(routerMock.navigate).toHaveBeenCalled(); + }); + + it('should emit viewAssetsClicked', async () => { + const { fixture } = await renderContractTableComponent(); + const { componentInstance } = fixture; + let spy = spyOn(componentInstance.viewAssetsClicked, 'emit'); + const viewAssetsAction = componentInstance.tableConfig.menuActionsConfig.filter(action => action.label === 'actions.viewParts')[0]; + viewAssetsAction.action(null); + expect(spy).toHaveBeenCalled(); + + }); + it('should convert data to csv', async () => { const { fixture } = await renderContractTableComponent(); const { componentInstance } = fixture; diff --git a/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.ts b/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.ts index 923d740666..8c861c9dff 100644 --- a/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.ts +++ b/frontend/src/app/modules/page/admin/presentation/contracts/contracts.component.ts @@ -35,11 +35,12 @@ export class ContractsComponent { this.contractsFacade.setContracts(0,10,[null,null]); } - this.viewAssetsClicked.subscribe((data) => { - this.router.navigate([ 'parts' ], { queryParams: { contractId: data?.['contractId'] } }); - }); }) + this.viewAssetsClicked.subscribe((data) => { + this.router.navigate([ 'parts' ], { queryParams: { contractId: data?.['contractId'] } }); + }); + this.pagination = { page: 0, pageSize: 10, sorting: [ '', null ] }; this.tableConfig = { displayedColumns: [ 'select', 'contractId', 'counterpartyAddress', 'creationDate', 'endDate', 'state', 'menu' ], From 9bd807172308bbe6d8b4ab44dff35a8549ae56fb Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Mon, 24 Jun 2024 17:40:34 +0200 Subject: [PATCH 5/6] chore(contract): 1037 added tests --- .../multi-select-autocomplete.component.spec.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.spec.ts b/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.spec.ts index c281983241..228af8eea9 100644 --- a/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.spec.ts +++ b/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.spec.ts @@ -7,14 +7,14 @@ import { SharedModule } from '@shared/shared.module'; import { renderComponent } from '@tests/test-render.utils'; describe('MultiSelectAutocompleteComponent', () => { - const renderMultiSelectAutoCompleteComponent = (multiple = true) => { + const renderMultiSelectAutoCompleteComponent = (multiple = true, prefilterValue = null) => { const placeholder = 'test'; const options = [ SemanticDataModel.PARTASPLANNED, SemanticDataModel.BATCH ]; return renderComponent(MultiSelectAutocompleteComponent, { imports: [ SharedModule ], providers: [ DatePipe, FormatPartSemanticDataModelToCamelCasePipe ], - componentProperties: { placeholder: placeholder, options: options }, + componentProperties: { placeholder: placeholder, options: options, prefilterValue: prefilterValue }, }); }; @@ -265,6 +265,17 @@ describe('MultiSelectAutocompleteComponent', () => { expect(option).toEqual([]); }); + it('should set prefilter value', async () => { + const { fixture } = await renderMultiSelectAutoCompleteComponent(false, 'hello'); + const { componentInstance } = fixture; + let formSpy = spyOn(componentInstance.formControl, 'patchValue'); + let updateSpy = spyOn(componentInstance, 'updateOptionsAndSelections'); + + expect(componentInstance.searchElement).toEqual('hello'); + expect(componentInstance.selectedValue).toEqual([ 'hello' ]); + + }); + it('should return when calling filterItem() without value', async () => { const { fixture } = await renderMultiSelectAutoCompleteComponent(); const { componentInstance } = fixture; From e76a1b17647ee84961e0864e447cf558417035e5 Mon Sep 17 00:00:00 2001 From: Martin Maul Date: Mon, 24 Jun 2024 17:42:59 +0200 Subject: [PATCH 6/6] chore(contract): 1037 added tests --- .../multi-select-autocomplete.component.spec.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.spec.ts b/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.spec.ts index 228af8eea9..e82a52275b 100644 --- a/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.spec.ts +++ b/frontend/src/app/modules/shared/components/multi-select-autocomplete/multi-select-autocomplete.component.spec.ts @@ -268,8 +268,6 @@ describe('MultiSelectAutocompleteComponent', () => { it('should set prefilter value', async () => { const { fixture } = await renderMultiSelectAutoCompleteComponent(false, 'hello'); const { componentInstance } = fixture; - let formSpy = spyOn(componentInstance.formControl, 'patchValue'); - let updateSpy = spyOn(componentInstance, 'updateOptionsAndSelections'); expect(componentInstance.searchElement).toEqual('hello'); expect(componentInstance.selectedValue).toEqual([ 'hello' ]);