diff --git a/e2e/process-services-cloud/process/process-filter-results.e2e.ts b/e2e/process-services-cloud/process/process-filter-results.e2e.ts index fe6362a6ba..6c20f55fdd 100644 --- a/e2e/process-services-cloud/process/process-filter-results.e2e.ts +++ b/e2e/process-services-cloud/process/process-filter-results.e2e.ts @@ -17,7 +17,6 @@ import { createApiService, AppListCloudPage, - DateUtil, GroupIdentityService, IdentityService, LocalStorageUtil, @@ -67,17 +66,26 @@ describe('Process filters cloud', () => { const queryService = new QueryService(apiService); const tasksService = new TasksService(apiService); - const beforeDate = format(subDays(new Date(), 1), 'dd/MM/yyyy'); - const currentDate = DateUtil.formatDate('DD/MM/YYYY'); - const afterDate = format(addDays(new Date(), 1), 'dd/MM/yyyy'); + const beforeDate = format(subDays(new Date(), 2), 'dd/MM/yyyy'); + const currentDate = format(new Date(), 'dd/MM/yyyy'); + const afterDate = format(addDays(new Date(), 2), 'dd/MM/yyyy'); const processListCloudConfiguration = new ProcessListCloudConfiguration(); const editProcessFilterConfiguration = new EditProcessFilterConfiguration(); const processListCloudConfigFile = processListCloudConfiguration.getConfiguration(); const editProcessFilterConfigFile = editProcessFilterConfiguration.getConfiguration(); - let completedProcess; let runningProcessInstance; let suspendProcessInstance; let testUser; let anotherUser; let groupInfo; - let anotherProcessInstance; let processDefinition; let anotherProcessDefinition; - let differentAppUserProcessInstance; let simpleAppProcessDefinition; + let completedProcess: any; + let runningProcessInstance: any; + let suspendProcessInstance: any; + let testUser: any; + let anotherUser: any; + let groupInfo: any; + let anotherProcessInstance: any; + let processDefinition: any; + let anotherProcessDefinition: any; + let differentAppUserProcessInstance: any; + let simpleAppProcessDefinition: any; + const candidateBaseApp = browser.params.resources.ACTIVITI_CLOUD_APPS.CANDIDATE_BASE_APP.name; const simpleApp = browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name; @@ -252,7 +260,7 @@ describe('Process filters cloud', () => { await processList.checkContentIsNotDisplayedByName(runningProcessInstance.entry.name); }); - it('[C306892] Should be able to filter by process status - Running', async () => { + it('[C306892-1] Should be able to filter by process status - Running', async () => { await editProcessFilter.openFilter(); await editProcessFilter.setStatusFilterDropDown(PROCESS_STATUS.RUNNING); await processList.getDataTable().waitTillContentLoaded(); @@ -269,7 +277,7 @@ describe('Process filters cloud', () => { await processList.checkContentIsNotDisplayedByName(completedProcess.entry.name); }); - it('[C306892] Should be able to filter by process status - Completed', async () => { + it('[C306892-2] Should be able to filter by process status - Completed', async () => { await editProcessFilter.openFilter(); await editProcessFilter.setStatusFilterDropDown(PROCESS_STATUS.COMPLETED); await processList.getDataTable().waitTillContentLoaded(); @@ -286,7 +294,7 @@ describe('Process filters cloud', () => { await processList.checkContentIsNotDisplayedByName(anotherProcessInstance.entry.name); }); - it('[C306892] Should be able to filter by process status - Suspended', async () => { + it('[C306892-3] Should be able to filter by process status - Suspended', async () => { await editProcessFilter.openFilter(); await editProcessFilter.setStatusFilterDropDown(PROCESS_STATUS.SUSPENDED); await processList.getDataTable().waitTillContentLoaded(); @@ -303,7 +311,7 @@ describe('Process filters cloud', () => { await processList.checkContentIsNotDisplayedByName(completedProcess.entry.name); }); - it('[C306892] Should be able to filter by process status - All', async () => { + it('[C306892-4] Should be able to filter by process status - All', async () => { await processCloudDemoPage.processFilterCloudComponent.clickAllProcessesFilter(); await editProcessFilter.openFilter(); @@ -315,17 +323,17 @@ describe('Process filters cloud', () => { await processList.checkContentIsDisplayedByName(completedProcess.entry.name); }); - it('[C311318] Should be able to filter by lastModifiedFrom - displays record when date = currentDate', async () => { + it('[C311318-1] Should be able to filter by lastModifiedFrom - displays record when date = currentDate', async () => { await setProcessName(currentDate, 'lastModifiedFrom'); await processList.checkContentIsDisplayedByName(runningProcessInstance.entry.name); }); - it('[C311318] Should be able to filter by lastModifiedFrom - displays record when date = beforeDate', async () => { + it('[C311318-2] Should be able to filter by lastModifiedFrom - displays record when date = beforeDate', async () => { await setProcessName(beforeDate, 'lastModifiedFrom'); await processList.checkContentIsDisplayedByName(runningProcessInstance.entry.name); }); - it('[C311318] Should be able to filter by lastModifiedFrom - does not display record when date = afterDate', async () => { + it('[C311318-3] Should be able to filter by lastModifiedFrom - does not display record when date = afterDate', async () => { await editProcessFilter.openFilter(); await editProcessFilter.setProcessName(runningProcessInstance.entry.name); await processList.getDataTable().waitTillContentLoaded(); @@ -333,17 +341,17 @@ describe('Process filters cloud', () => { await processList.checkContentIsNotDisplayedByName(runningProcessInstance.entry.name); }); - it('[C311319] Should be able to filter by lastModifiedTo - displays record when date = currentDate', async () => { + it('[C311319-1] Should be able to filter by lastModifiedTo - displays record when date = currentDate', async () => { await setProcessName(currentDate); await processList.checkContentIsDisplayedByName(runningProcessInstance.entry.name); }); - it('[C311319] Should be able to filter by lastModifiedTo - does not display record when date = beforeDate', async () => { + it('[C311319-2] Should be able to filter by lastModifiedTo - does not display record when date = beforeDate', async () => { await setProcessName(beforeDate); await processList.checkContentIsNotDisplayedByName(runningProcessInstance.entry.name); }); - it('[C311319] Should be able to filter by lastModifiedTo - displays record when date = afterDate', async () => { + it('[C311319-3] Should be able to filter by lastModifiedTo - displays record when date = afterDate', async () => { await editProcessFilter.openFilter(); await editProcessFilter.setProperty('lastModifiedTo', afterDate); await processList.getDataTable().waitTillContentLoaded(); @@ -351,7 +359,7 @@ describe('Process filters cloud', () => { await processList.checkContentIsDisplayedByName(runningProcessInstance.entry.name); }); - it('[C311319] Should not display any processes when the lastModifiedFrom and lastModifiedTo are set to a future date', async () => { + it('[C311319-4] Should not display any processes when the lastModifiedFrom and lastModifiedTo are set to a future date', async () => { await editProcessFilter.openFilter(); await editProcessFilter.setProperty('lastModifiedFrom', afterDate); await processList.getDataTable().waitTillContentLoaded(); diff --git a/e2e/process-services-cloud/task-list/task-list-properties.e2e.ts b/e2e/process-services-cloud/task-list/task-list-properties.e2e.ts index 2123c90c80..4a69b74a75 100644 --- a/e2e/process-services-cloud/task-list/task-list-properties.e2e.ts +++ b/e2e/process-services-cloud/task-list/task-list-properties.e2e.ts @@ -20,7 +20,7 @@ import { StringUtil, TasksService, ProcessDefinitionsService, ProcessInstancesService, LoginPage, createApiService, - AppListCloudPage, LocalStorageUtil, IdentityService, GroupIdentityService, DateUtil + AppListCloudPage, LocalStorageUtil, IdentityService, GroupIdentityService } from '@alfresco/adf-testing'; import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; import { TasksCloudDemoPage } from './../pages/tasks-cloud-demo.page'; @@ -50,12 +50,21 @@ describe('Edit task filters and task list properties', () => { const processInstancesService = new ProcessInstancesService(apiService); const noTasksFoundMessage = 'No Tasks Found'; - let createdTask; let notAssigned; let notDisplayedTask; let processDefinition; let processInstance; let priorityTask; let subTask; - let otherOwnerTask; let testUser; let groupInfo; let simpleTask; + let createdTask: any; + let notAssigned: any; + let notDisplayedTask: any; + let processDefinition: any; + let processInstance: any; + let priorityTask: any; + let subTask: any; + let otherOwnerTask: any; + let testUser: any; + let groupInfo: any; + let simpleTask: any; const priority = 1; const beforeDate = format(subDays(new Date(), 1), 'dd/MM/yyyy'); - const currentDate = DateUtil.formatDate('DD/MM/YYYY'); + const currentDate = format(new Date(), 'dd/MM/yyyy'); const afterDate = format(addDays(new Date(), 1), 'dd/MM/yyyy'); beforeAll(async () => { diff --git a/e2e/process-services/process/process-instance-details.e2e.ts b/e2e/process-services/process/process-instance-details.e2e.ts index 85642fa4b9..ec60fab47f 100644 --- a/e2e/process-services/process/process-instance-details.e2e.ts +++ b/e2e/process-services/process/process-instance-details.e2e.ts @@ -23,7 +23,7 @@ import { ProcessServiceTabBarPage } from '../pages/process-service-tab-bar.page' import { ProcessListPage } from '../pages/process-list.page'; import { ProcessDetailsPage } from '../pages/process-details.page'; import { AppDefinitionRepresentation, ProcessInstanceRepresentation, ProcessInstancesApi } from '@alfresco/js-api'; -import { DateFnsUtils } from '../../../lib/core/src/lib/common/utils/date-fns-utils'; +import { format } from 'date-fns'; describe('Process Instance Details', () => { const app = browser.params.resources.Files.SIMPLE_APP_WITH_USER_FORM; @@ -45,8 +45,6 @@ describe('Process Instance Details', () => { let process: ProcessInstanceRepresentation; let user: UserModel; - const PROCESS_DATE_FORMAT = 'll'; - beforeAll(async () => { await apiService.loginWithProfile('admin'); @@ -76,6 +74,10 @@ describe('Process Instance Details', () => { it('[C307031] Should display the created date in the default format', async () => { await processDetailsPage.checkProcessHeaderDetailsAreVisible(); - await expect(await processDetailsPage.getCreated()).toEqual(DateFnsUtils.formatDate(process.started, PROCESS_DATE_FORMAT)); + + const created = await processDetailsPage.getCreated(); + const expected = format(process.started, 'PP'); + + expect(created).toEqual(expected); }); }); diff --git a/e2e/protractor.excludes.json b/e2e/protractor.excludes.json index 5dcf9bf955..7c542acc77 100644 --- a/e2e/protractor.excludes.json +++ b/e2e/protractor.excludes.json @@ -7,5 +7,8 @@ "C216430": "https://alfresco.atlassian.net/browse/ACS-4595", "C280063": "https://alfresco.atlassian.net/browse/ACS-4595", "C280064": "https://alfresco.atlassian.net/browse/ACS-4595", - "C313200": "https://alfresco.atlassian.net/browse/APPS-2234" + "C313200": "https://alfresco.atlassian.net/browse/APPS-2234", + "C311318-3": "https://alfresco.atlassian.net/browse/APPS-2254", + "C311319-2": "https://alfresco.atlassian.net/browse/APPS-2254", + "C311319-4": "https://alfresco.atlassian.net/browse/APPS-2254" } diff --git a/e2e/search/components/search-date-range.e2e.ts b/e2e/search/components/search-date-range.e2e.ts index 424af603ba..a4b156ac0a 100644 --- a/e2e/search/components/search-date-range.e2e.ts +++ b/e2e/search/components/search-date-range.e2e.ts @@ -15,30 +15,18 @@ * limitations under the License. */ -import { - BrowserActions, - DataTableComponentPage, - DatePickerCalendarPage, - DateUtil, - LocalStorageUtil, - LoginPage -} from '@alfresco/adf-testing'; +import { BrowserActions, DataTableComponentPage, DateUtil, LoginPage } from '@alfresco/adf-testing'; import { browser, ElementFinder } from 'protractor'; import { SearchBarPage } from '../pages/search-bar.page'; -import { NavigationBarPage } from '../../core/pages/navigation-bar.page'; import { SearchFiltersPage } from '../pages/search-filters.page'; import { SearchResultsPage } from '../pages/search-results.page'; -import { SearchConfiguration } from '../search.config'; describe('Search Date Range Filter', () => { - const loginPage = new LoginPage(); const searchBarPage = new SearchBarPage(); const searchFilters = new SearchFiltersPage(); const dateRangeFilter = searchFilters.createdDateRangeFilterPage(); const searchResults = new SearchResultsPage(); - const datePicker = new DatePickerCalendarPage(); - const navigationBar = new NavigationBarPage(); const dataTable = new DataTableComponentPage(); beforeAll(async () => { @@ -58,29 +46,6 @@ describe('Search Date Range Filter', () => { afterEach(async () => { await browser.refresh(); - }); - - it('[C277106] Should display default values for Date Range widget', async () => { - await dateRangeFilter.checkFromFieldIsDisplayed(); - await dateRangeFilter.checkFromDateToggleIsDisplayed(); - await dateRangeFilter.checkToFieldIsDisplayed(); - await dateRangeFilter.checkToDateToggleIsDisplayed(); - await dateRangeFilter.checkApplyButtonIsDisplayed(); - await dateRangeFilter.checkApplyButtonIsDisabled(); - await dateRangeFilter.checkClearButtonIsDisplayed(); - }); - - it('[C277104] Should be able to set dates using date pickers', async () => { - await dateRangeFilter.checkFromDateToggleIsDisplayed(); - const fromDatePicker = await dateRangeFilter.openFromDatePicker(); - await fromDatePicker.selectTodayDate(); - await expect(await dateRangeFilter.getFromDate()).toEqual(await dateRangeFilter.getFromCalendarSelectedDate()); - }); - - it('[C277105] Should be able to type a date', async () => { - const date = '01-May-18'; - await dateRangeFilter.putFromDate(date); - await expect(await dateRangeFilter.getFromCalendarSelectedDate()).toEqual(await dateRangeFilter.getFromDate()); }); it('[C277119] FROM and TO dates should depend on each other', async () => { @@ -129,7 +94,7 @@ describe('Search Date Range Filter', () => { await searchResults.sortByCreated('ASC'); - const results = await dataTable.geCellElementDetail('Created') as ElementFinder[]; + const results = (await dataTable.geCellElementDetail('Created')) as ElementFinder[]; for (const currentResult of results) { const currentDate = await BrowserActions.getAttribute(currentResult, 'title'); const currentDateFormatted = DateUtil.parse(currentDate, 'MMM DD, YYYY, h:mm:ss a'); @@ -138,66 +103,4 @@ describe('Search Date Range Filter', () => { await expect(currentDateFormatted >= DateUtil.parse(fromDate, 'DD-MM-YY')).toBe(true); } }); - - it('[C277108] Should display a warning message when user doesn\'t set the date range at all', async () => { - await dateRangeFilter.checkFromFieldIsDisplayed(); - await dateRangeFilter.clickFromField(); - await dateRangeFilter.clickToField(); - await dateRangeFilter.checkFromErrorMessageIsDisplayed('Required value'); - await dateRangeFilter.clickFromField(); - await dateRangeFilter.checkToErrorMessageIsDisplayed('Required value'); - }); - - it('[C277114] Should display warning message if user doesn\'t set the date range properly', async () => { - const toDate = '01-May-18'; - const fromDate = '16-May-18'; - - await dateRangeFilter.checkToFieldIsDisplayed(); - await dateRangeFilter.putToDate(toDate); - await dateRangeFilter.checkFromFieldIsDisplayed(); - await dateRangeFilter.putFromDate(fromDate); - await dateRangeFilter.clickFromField(); - await dateRangeFilter.checkToErrorMessageIsDisplayed('No days selected.'); - }); - - it('[C277115] Should display warning message if user types a date later than today\'s date', async () => { - await dateRangeFilter.checkFromFieldIsDisplayed(); - await dateRangeFilter.putFromDate(DateUtil.formatDate('DD-MMM-YY', new Date(), 1)); - await dateRangeFilter.checkFromErrorMessageIsDisplayed('The date is beyond the maximum date.'); - }); - - it('[C277108] Should display a warning message when user doesn\'t set the date range at all', async () => { - await dateRangeFilter.checkFromFieldIsDisplayed(); - await dateRangeFilter.putFromDate('Wrong Format'); - await dateRangeFilter.clickToField(); - await dateRangeFilter.checkFromErrorMessageIsDisplayed('Invalid date. The date must be in the format \'DD-MMM-YY\''); - await dateRangeFilter.putFromDate('01-May-18'); - await dateRangeFilter.checkFromErrorMessageIsNotDisplayed(); - }); - - describe('configuration change', () => { - it('[C277117] Should be able to change date format', async () => { - await navigationBar.navigateToContentServices(); - - const jsonFile= SearchConfiguration.getConfiguration(); - jsonFile.categories[4].component.settings.dateFormat = 'MM-DD-YY'; - - await LocalStorageUtil.setConfigField('search', JSON.stringify(jsonFile)); - - await searchBarPage.clickOnSearchIcon(); - await searchBarPage.enterTextAndPressEnter('*'); - await searchResults.dataTable.waitTillContentLoaded(); - - await searchFilters.checkCreatedRangeFilterIsDisplayed(); - await searchFilters.clickCreatedRangeFilterHeader(); - await searchFilters.checkCreatedRangeFilterIsExpanded(); - await dateRangeFilter.checkFromFieldIsDisplayed(); - await dateRangeFilter.openFromDatePicker(); - - const todayDate = DateUtil.formatDate('MM-DD-YY'); - await datePicker.selectTodayDate(); - - await expect(await dateRangeFilter.getFromDate()).toEqual(todayDate); - }); - }); }); diff --git a/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.spec.ts b/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.spec.ts index 6d4aa6587a..0e43d82647 100644 --- a/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.spec.ts +++ b/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.spec.ts @@ -16,22 +16,19 @@ */ import { SearchDateRangeComponent } from './search-date-range.component'; -import { DateAdapter } from '@angular/material/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ContentTestingModule } from '../../../testing/content.testing.module'; import { TranslateModule } from '@ngx-translate/core'; -import { MomentDateAdapter } from '@angular/material-moment-adapter'; - -declare let moment: any; +import { MatDatepickerInputEvent } from '@angular/material/datepicker'; +import { startOfDay, endOfDay, isValid, addDays, format } from 'date-fns'; describe('SearchDateRangeComponent', () => { let fixture: ComponentFixture; let component: SearchDateRangeComponent; - let adapter: MomentDateAdapter; - const fromDate = '2016-10-16'; - const toDate = '2017-10-16'; - const maxDate = '10-Mar-20'; - const dateFormatFixture = 'DD-MMM-YY'; + + const dateFormatFixture = 'dd-MMM-yy'; + const fromDate = new Date('2016-10-16'); + const toDate = new Date('2017-10-16'); beforeEach(() => { TestBed.configureTestingModule({ @@ -41,7 +38,6 @@ describe('SearchDateRangeComponent', () => { ] }); fixture = TestBed.createComponent(SearchDateRangeComponent); - adapter = fixture.debugElement.injector.get(DateAdapter) as MomentDateAdapter; component = fixture.componentInstance; }); @@ -59,31 +55,26 @@ describe('SearchDateRangeComponent', () => { component.settings = { field: 'cm:created', dateFormat: dateFormatFixture }; fixture.detectChanges(); - const inputString = '20-feb-18'; - const momentFromInput = moment(inputString, dateFormatFixture); + const date = new Date('20-feb-18'); + expect(isValid(date)).toBeTrue(); - expect(momentFromInput.isValid()).toBeTruthy(); - - component.onChangedHandler({ value: inputString }, component.from); - - expect(component.from.value.toString()).toEqual(momentFromInput.toString()); + component.onChangedHandler({ value: date } as MatDatepickerInputEvent, component.from); + expect(component.from.value.toString()).toEqual(date.toString()); }); it('should NOT setup form control with invalid date on change', () => { component.settings = { field: 'cm:created', dateFormat: dateFormatFixture }; fixture.detectChanges(); - const inputString = '20.f.18'; - const momentFromInput = moment(inputString, dateFormatFixture); + const date = new Date('20.f.18'); + expect(isValid(date)).toBeFalse(); - expect(momentFromInput.isValid()).toBeFalsy(); - - component.onChangedHandler({ value: inputString }, component.from); - - expect(component.from.value.toString()).not.toEqual(momentFromInput.toString()); + component.onChangedHandler({ value: date } as MatDatepickerInputEvent, component.from); + expect(component.from.value).not.toEqual(date); }); it('should reset form', () => { + fixture.detectChanges(); component.form.setValue({ from: fromDate, to: toDate }); @@ -92,9 +83,9 @@ describe('SearchDateRangeComponent', () => { component.reset(); - expect(component.from.value).toEqual(''); - expect(component.to.value).toEqual(''); - expect(component.form.value).toEqual({ from: '', to: '' }); + expect(component.from.value).toBeNull(); + expect(component.to.value).toBeNull(); + expect(component.form.value).toEqual({ from: null, to: null }); }); it('should reset fromMaxDate on reset', () => { @@ -143,8 +134,8 @@ describe('SearchDateRangeComponent', () => { to: toDate }, true); - const startDate = moment(fromDate).startOf('day').format(); - const endDate = moment(toDate).endOf('day').format(); + const startDate = startOfDay(fromDate).toISOString(); + const endDate = endOfDay(toDate).toISOString(); const expectedQuery = `cm:created:['${startDate}' TO '${endDate}']`; @@ -156,7 +147,7 @@ describe('SearchDateRangeComponent', () => { fixture.detectChanges(); const input = fixture.debugElement.nativeElement.querySelector('[data-automation-id="date-range-from-input"]'); - input.value = '10-05-18'; + input.value = '10-f-18'; input.dispatchEvent(new Event('input')); fixture.detectChanges(); @@ -165,11 +156,82 @@ describe('SearchDateRangeComponent', () => { expect(component.getFromValidationMessage()).toEqual('SEARCH.FILTER.VALIDATION.INVALID-DATE'); }); + it('should hide date-format error when correcting input', async () => { + fixture.detectChanges(); + + const input = fixture.debugElement.nativeElement.querySelector('[data-automation-id="date-range-from-input"]'); + input.value = '10-f-18'; + input.dispatchEvent(new Event('input')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.getFromValidationMessage()).toEqual('SEARCH.FILTER.VALIDATION.INVALID-DATE'); + + input.value = '10-10-2018'; + input.dispatchEvent(new Event('input')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.getFromValidationMessage()).toEqual(''); + }); + + it('should show error for max date constraint', async () => { + component.settings = { field: 'cm:created', maxDate: 'today' }; + fixture.detectChanges(); + + const input = fixture.debugElement.nativeElement.querySelector('[data-automation-id="date-range-from-input"]'); + input.value = format(addDays(new Date(), 1), 'dd-MM-yyyy'); + input.dispatchEvent(new Event('input')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.getFromValidationMessage()).toEqual('SEARCH.FILTER.VALIDATION.BEYOND-MAX-DATE'); + }); + + it('should show error for required constraint', async () => { + fixture.detectChanges(); + + const fromInput = fixture.debugElement.nativeElement.querySelector('[data-automation-id="date-range-from-input"]'); + fromInput.value = ''; + fromInput.dispatchEvent(new Event('input')); + + const toInput = fixture.debugElement.nativeElement.querySelector('[data-automation-id="date-range-to-input"]'); + toInput.value = ''; + toInput.dispatchEvent(new Event('input')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.getFromValidationMessage()).toEqual('SEARCH.FILTER.VALIDATION.REQUIRED-VALUE'); + expect(component.getToValidationMessage()).toEqual('SEARCH.FILTER.VALIDATION.REQUIRED-VALUE'); + }); + + it('should show error for incorrect date range', async () => { + fixture.detectChanges(); + + const fromInput = fixture.debugElement.nativeElement.querySelector('[data-automation-id="date-range-from-input"]'); + fromInput.value = '11-10-2018'; + fromInput.dispatchEvent(new Event('input')); + + const toInput = fixture.debugElement.nativeElement.querySelector('[data-automation-id="date-range-to-input"]'); + toInput.value = '10-10-2018'; + toInput.dispatchEvent(new Event('input')); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(component.getFromValidationMessage()).toEqual(''); + expect(component.getToValidationMessage()).toEqual('SEARCH.FILTER.VALIDATION.NO-DAYS'); + }); + it('should not show date-format error when valid found', async () => { fixture.detectChanges(); const input = fixture.debugElement.nativeElement.querySelector('[data-automation-id="date-range-from-input"]'); - input.value = '10/10/2018'; + input.value = '10-10-2018'; input.dispatchEvent(new Event('input')); fixture.detectChanges(); @@ -182,31 +244,22 @@ describe('SearchDateRangeComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - expect(fixture.debugElement.nativeElement.querySelector('input[ng-reflect-max]')).toBeNull(); + expect(component.maxDate).toBeUndefined(); }); it('should be able to set a fixed maximum date', async () => { - component.settings = { field: 'cm:created', dateFormat: dateFormatFixture, maxDate }; + component.settings = { field: 'cm:created', dateFormat: dateFormatFixture, maxDate: '10-Mar-20' }; fixture.detectChanges(); - const inputs = fixture.debugElement.nativeElement.querySelectorAll('input[ng-reflect-max="Tue Mar 10 2020 23:59:59 GMT+0"]'); - - expect(inputs[0]).toBeDefined(); - expect(inputs[0]).not.toBeNull(); - expect(inputs[1]).toBeDefined(); - expect(inputs[1]).not.toBeNull(); + const expected = endOfDay(new Date(2020, 2, 10)); + expect(component.maxDate).toEqual(expected); }); it('should be able to set the maximum date to today', async () => { component.settings = { field: 'cm:created', dateFormat: dateFormatFixture, maxDate: 'today' }; fixture.detectChanges(); - const today = adapter.today().endOf('day').toString().slice(0, -3); + const today = endOfDay(new Date()); - const inputs = fixture.debugElement.nativeElement.querySelectorAll('input[ng-reflect-max="' + today + '"]'); - - expect(inputs[0]).toBeDefined(); - expect(inputs[0]).not.toBeNull(); - expect(inputs[1]).toBeDefined(); - expect(inputs[1]).not.toBeNull(); + expect(component.maxDate).toEqual(today); }); }); diff --git a/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts b/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts index 43aa764dd2..7c06504e56 100644 --- a/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts +++ b/lib/content-services/src/lib/search/components/search-date-range/search-date-range.component.ts @@ -15,60 +15,60 @@ * limitations under the License. */ -import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { FormControl, FormGroup, Validators } from '@angular/forms'; -import { DateAdapter } from '@angular/material/core'; -import { MomentDateAdapter, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; +import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; +import { ADF_DATE_FORMATS, AdfDateFnsAdapter } from '@alfresco/adf-core'; import { SearchWidget } from '../../models/search-widget.interface'; import { SearchWidgetSettings } from '../../models/search-widget-settings.interface'; import { SearchQueryBuilderService } from '../../services/search-query-builder.service'; import { LiveErrorStateMatcher } from '../../forms/live-error-state-matcher'; -import { Moment } from 'moment'; import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; -import { MAT_MOMENT_DATE_ADAPTER_OPTIONS } from '@angular/material-moment-adapter'; +import { MatDatepickerInputEvent } from '@angular/material/datepicker'; +import { startOfDay, endOfDay, isBefore, isValid as isValidDate } from 'date-fns'; export interface DateRangeValue { from: string; to: string; } -declare let moment: any; - const DEFAULT_FORMAT_DATE: string = 'DD/MM/YYYY'; +interface DateRangeForm { + from: FormControl; + to: FormControl; +} + @Component({ selector: 'adf-search-date-range', templateUrl: './search-date-range.component.html', styleUrls: ['./search-date-range.component.scss'], providers: [ - { provide: DateAdapter, useClass: MomentDateAdapter }, - { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { strict: true } } + { provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS }, + { provide: DateAdapter, useClass: AdfDateFnsAdapter } ], encapsulation: ViewEncapsulation.None, host: { class: 'adf-search-date-range' } }) -export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy { - from: FormControl; - to: FormControl; +export class SearchDateRangeComponent implements SearchWidget, OnInit { + from: FormControl; + to: FormControl; - form: FormGroup; + form: FormGroup; matcher = new LiveErrorStateMatcher(); id: string; settings?: SearchWidgetSettings; context?: SearchQueryBuilderService; datePickerFormat: string; - maxDate: any; - fromMaxDate: any; + maxDate: Date; + fromMaxDate: Date; isActive = false; startValue: any; enableChangeUpdate: boolean; - displayValue$: Subject = new Subject(); + displayValue$ = new Subject(); - private onDestroy$ = new Subject(); - - constructor(private dateAdapter: DateAdapter, private userPreferencesService: UserPreferencesService) {} + constructor(private dateAdapter: DateAdapter) {} getFromValidationMessage(): string { return this.from.hasError('invalidOnChange') || this.hasParseError(this.from) @@ -95,21 +95,16 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy ngOnInit() { this.datePickerFormat = this.settings?.dateFormat ? this.settings.dateFormat : DEFAULT_FORMAT_DATE; - this.userPreferencesService - .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) - .subscribe((locale) => this.setLocale(locale)); - - const customDateAdapter = this.dateAdapter as MomentDateAdapter; - customDateAdapter.overrideDisplayFormat = this.datePickerFormat; + const customDateAdapter = this.dateAdapter as AdfDateFnsAdapter; + customDateAdapter.displayFormat = this.datePickerFormat; const validators = Validators.compose([Validators.required]); if (this.settings?.maxDate) { if (this.settings.maxDate === 'today') { - this.maxDate = this.dateAdapter.today().endOf('day'); + this.maxDate = endOfDay(this.dateAdapter.today()); } else { - this.maxDate = moment(this.settings.maxDate).endOf('day'); + this.maxDate = endOfDay(this.dateAdapter.parse(this.settings.maxDate, this.datePickerFormat)); } } @@ -120,8 +115,8 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy this.from = new FormControl(fromValue, validators); this.to = new FormControl(toValue, validators); } else { - this.from = new FormControl('', validators); - this.to = new FormControl('', validators); + this.from = new FormControl(null, validators); + this.to = new FormControl(null, validators); } this.form = new FormGroup({ @@ -133,17 +128,12 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy this.enableChangeUpdate = this.settings?.allowUpdateOnChange ?? true; } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - - apply(model: { from: string; to: string }, isValid: boolean) { + apply(model: Partial<{ from: Date; to: Date }>, isValid: boolean) { if (isValid && this.id && this.context && this.settings && this.settings.field) { this.isActive = true; - const start = moment(model.from).startOf('day').format(); - const end = moment(model.to).endOf('day').format(); + const start = startOfDay(model.from).toISOString(); + const end = endOfDay(model.to).toISOString(); this.context.queryFragments[this.id] = `${this.settings.field}:['${start}' TO '${end}']`; @@ -192,11 +182,12 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy this.to.markAsTouched(); this.submitValues(); } + clear() { this.isActive = false; this.form.reset({ - from: '', - to: '' + from: null, + to: null }); if (this.id && this.context) { @@ -205,6 +196,7 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy this.updateQuery(); } } + this.setFromMaxDate(); } @@ -220,12 +212,12 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy } } - onChangedHandler(event: any, formControl: FormControl) { + onChangedHandler(event: MatDatepickerInputEvent, formControl: FormControl) { const inputValue = event.value; - const formatDate = this.dateAdapter.parse(inputValue, this.datePickerFormat); - if (formatDate?.isValid()) { - formControl.setValue(formatDate); - } else if (formatDate) { + + if (isValidDate(inputValue)) { + formControl.setValue(inputValue); + } else if (inputValue) { formControl.setErrors({ invalidOnChange: true }); @@ -234,11 +226,6 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy this.setFromMaxDate(); } - setLocale(locale: string) { - this.dateAdapter.setLocale(locale); - moment.locale(locale); - } - hasParseError(formControl: FormControl): boolean { return formControl.hasError('matDatepickerParse') && formControl.getError('matDatepickerParse').text; } @@ -248,6 +235,6 @@ export class SearchDateRangeComponent implements SearchWidget, OnInit, OnDestroy } setFromMaxDate() { - this.fromMaxDate = !this.to.value || (this.maxDate && moment(this.maxDate).isBefore(this.to.value)) ? this.maxDate : moment(this.to.value); + this.fromMaxDate = !this.to.value || (this.maxDate && isBefore(this.maxDate, this.to.value)) ? this.maxDate : this.to.value; } } diff --git a/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.html b/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.html index 7571ae251b..2f94858a09 100644 --- a/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.html +++ b/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.html @@ -1,54 +1,50 @@ -
+
- {{'DATE-WIDGET.MESSAGES.START-LESS-THAN-END-DATE' | translate}} + {{'DATE-WIDGET.MESSAGES.START-LESS-THAN-END-DATE' | translate}} - {{'DATE-WIDGET.MESSAGES.START-DATE-REQUIRED' | translate}} + {{'DATE-WIDGET.MESSAGES.START-DATE-REQUIRED' | translate}} - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + +
diff --git a/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.spec.ts b/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.spec.ts new file mode 100644 index 0000000000..1b2a848a43 --- /dev/null +++ b/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.spec.ts @@ -0,0 +1,97 @@ +/*! + * @license + * Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved. + * + * Licensed 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 { ComponentFixture, TestBed } from '@angular/core/testing'; +import { DateRangeWidgetComponent } from './date-range.widget'; +import { TranslateModule } from '@ngx-translate/core'; +import { InsightsTestingModule } from '../../../../testing/insights.testing.module'; +import { ReportParameterDetailsModel } from '../../../../diagram/models/report/report-parameter-details.model'; +import { format } from 'date-fns'; + +describe('DateRangeWidgetComponent', () => { + let fixture: ComponentFixture; + let widget: DateRangeWidgetComponent; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot(), + InsightsTestingModule + ] + }); + + fixture = TestBed.createComponent(DateRangeWidgetComponent); + widget = fixture.componentInstance; + }); + + it('should init the start date and end date from the field model', async () => { + const field = new ReportParameterDetailsModel({ + value: { + startDate: '2023-03-13', + endDate: '2023-04-14' + } + }); + + widget.field = field; + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(format(widget.startDateValue, 'yyyy-MM-dd')).toEqual('2023-03-13'); + expect(format(widget.endDateValue, 'yyyy-MM-dd')).toEqual('2023-04-14'); + }); + + it('should emit dateRangeChanged with empty time/zone', async () => { + const field = new ReportParameterDetailsModel({ + value: { + startDate: '2023-03-13', + endDate: '2023-04-14' + } + }); + + widget.field = field; + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(widget.dateRange.valid).toBeTrue(); + + let emitted: { startDate: string; endDate: string }; + widget.dateRangeChanged.subscribe((value) => emitted = value); + + widget.onGroupValueChanged(); + + expect(emitted.startDate).toBe('2023-03-13T00:00:00.000Z'); + expect(emitted.endDate).toBe('2023-04-14T00:00:00.000Z'); + }); + + it('should validate date range', async () => { + const field = new ReportParameterDetailsModel({ + value: { + startDate: '2023-03-13', + endDate: '2023-01-14' + } + }); + + widget.field = field; + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(widget.dateRange.valid).toBeFalse(); + }); +}); diff --git a/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.ts b/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.ts index 6df647822c..162c54b60d 100644 --- a/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.ts +++ b/lib/insights/src/lib/analytics-process/components/widgets/date-range/date-range.widget.ts @@ -17,70 +17,71 @@ /* eslint-disable @angular-eslint/no-input-rename */ -import { MOMENT_DATE_FORMATS, MomentDateAdapter, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; -import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy } from '@angular/core'; -import { AbstractControl, UntypedFormControl, UntypedFormGroup, Validators } from '@angular/forms'; +import { ADF_DATE_FORMATS, AdfDateFnsAdapter } from '@alfresco/adf-core'; +import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; -import moment, { Moment } from 'moment'; -import { Subject } from 'rxjs'; -import { takeUntil } from 'rxjs/operators'; +import { ReportParameterDetailsModel } from '../../../../diagram/models/report/report-parameter-details.model'; +import { isAfter } from 'date-fns'; -const FORMAT_DATE_ACTIVITI = 'YYYY-MM-DD'; -const SHOW_FORMAT = 'DD/MM/YYYY'; +const FORMAT_DATE_ACTIVITI = 'yyyy-MM-dd'; +const DISPLAY_FORMAT = 'dd/MM/yyyy'; + +interface DateRangeProps { + startDate: FormControl; + endDate: FormControl; +} @Component({ selector: 'adf-date-range-widget', templateUrl: './date-range.widget.html', styleUrls: ['./date-range.widget.scss'], providers: [ - { provide: DateAdapter, useClass: MomentDateAdapter }, - { provide: MAT_DATE_FORMATS, useValue: MOMENT_DATE_FORMATS } + { provide: DateAdapter, useClass: AdfDateFnsAdapter }, + { provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS } ], encapsulation: ViewEncapsulation.None }) -export class DateRangeWidgetComponent implements OnInit, OnDestroy { +export class DateRangeWidgetComponent implements OnInit { @Input('group') - dateRange: UntypedFormGroup; + dateRange: FormGroup; @Input() - field: any; + field: ReportParameterDetailsModel; @Output() - dateRangeChanged = new EventEmitter(); + dateRangeChanged = new EventEmitter<{ startDate: string; endDate: string }>(); - minDate: Moment; - maxDate: Moment; - startDatePicker: Moment = moment(); - endDatePicker: Moment = moment(); + minDate: Date; + maxDate: Date; + startDateValue = new Date(); + endDateValue = new Date(); - private onDestroy$ = new Subject(); - - constructor(private dateAdapter: DateAdapter, private userPreferencesService: UserPreferencesService) {} + constructor(private dateAdapter: DateAdapter) {} ngOnInit() { - this.userPreferencesService - .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) - .subscribe((locale) => this.dateAdapter.setLocale(locale)); - - const momentDateAdapter = this.dateAdapter as MomentDateAdapter; - momentDateAdapter.overrideDisplayFormat = SHOW_FORMAT; + const momentDateAdapter = this.dateAdapter as AdfDateFnsAdapter; + momentDateAdapter.displayFormat = DISPLAY_FORMAT; if (this.field) { if (this.field.value?.startDate) { - this.startDatePicker = moment(this.field.value.startDate, FORMAT_DATE_ACTIVITI); + this.startDateValue = this.dateAdapter.parse(this.field.value.startDate, FORMAT_DATE_ACTIVITI); } if (this.field.value?.endDate) { - this.endDatePicker = moment(this.field.value.endDate, FORMAT_DATE_ACTIVITI); + this.endDateValue = this.dateAdapter.parse(this.field.value.endDate, FORMAT_DATE_ACTIVITI); } } - const startDateControl = new UntypedFormControl(this.startDatePicker); + if (!this.dateRange) { + this.dateRange = new FormGroup({} as any); + } + + const startDateControl = new FormControl(this.startDateValue); startDateControl.setValidators(Validators.required); this.dateRange.addControl('startDate', startDateControl); - const endDateControl = new UntypedFormControl(this.endDatePicker); + const endDateControl = new FormControl(this.endDateValue); endDateControl.setValidators(Validators.required); this.dateRange.addControl('endDate', endDateControl); @@ -88,27 +89,24 @@ export class DateRangeWidgetComponent implements OnInit, OnDestroy { this.dateRange.valueChanges.subscribe(() => this.onGroupValueChanged()); } - ngOnDestroy() { - this.onDestroy$.next(true); - this.onDestroy$.complete(); - } - onGroupValueChanged() { if (this.dateRange.valid) { - const dateStart = this.convertToMomentDateWithTime(this.dateRange.controls.startDate.value); - const endStart = this.convertToMomentDateWithTime(this.dateRange.controls.endDate.value); + const dateStart = this.formatDateTime(this.dateRange.controls.startDate.value); + const endStart = this.formatDateTime(this.dateRange.controls.endDate.value); this.dateRangeChanged.emit({ startDate: dateStart, endDate: endStart }); } } - convertToMomentDateWithTime(date: string) { - return moment(date, FORMAT_DATE_ACTIVITI, true).format(FORMAT_DATE_ACTIVITI) + 'T00:00:00.000Z'; + private formatDateTime(date: Date) { + const datePart = this.dateAdapter.format(date, FORMAT_DATE_ACTIVITI); + return `${datePart}T00:00:00.000Z`; } - dateCheck(formControl: AbstractControl) { - const startDate = moment(formControl.get('startDate').value); - const endDate = moment(formControl.get('endDate').value); - const isAfterCheck = startDate.isAfter(endDate); + dateCheck(formControl: FormGroup) { + const startDate = formControl.get('startDate').value; + const endDate = formControl.get('endDate').value; + const isAfterCheck = isAfter(startDate, endDate); + return isAfterCheck ? { greaterThan: true } : null; } diff --git a/lib/insights/src/lib/diagram/models/report/report-parameter-details.model.ts b/lib/insights/src/lib/diagram/models/report/report-parameter-details.model.ts index c4c36d0226..4f3f6377f5 100644 --- a/lib/insights/src/lib/diagram/models/report/report-parameter-details.model.ts +++ b/lib/insights/src/lib/diagram/models/report/report-parameter-details.model.ts @@ -26,7 +26,7 @@ export class ReportParameterDetailsModel { options: ParameterValueModel[]; dependsOn: string; - constructor(obj?: any) { + constructor(obj?: Partial) { this.id = obj?.id; this.name = obj?.name; this.nameKey = obj?.nameKey; diff --git a/lib/process-services-cloud/src/lib/common/date-range-filter/date-range-filter.component.ts b/lib/process-services-cloud/src/lib/common/date-range-filter/date-range-filter.component.ts index 43e4ffc483..a90e003bab 100644 --- a/lib/process-services-cloud/src/lib/common/date-range-filter/date-range-filter.component.ts +++ b/lib/process-services-cloud/src/lib/common/date-range-filter/date-range-filter.component.ts @@ -18,17 +18,21 @@ import { Component, Input, EventEmitter, Output, OnInit } from '@angular/core'; import { MatSelectChange } from '@angular/material/select'; import { ProcessFilterProperties, ProcessFilterOptions } from '../../process/process-filters/models/process-filter-cloud.model'; -import { UntypedFormGroup, UntypedFormControl } from '@angular/forms'; +import { FormGroup, FormControl } from '@angular/forms'; import { DateRangeFilter, DateCloudFilterType } from '../../models/date-cloud-filter.model'; -import { endOfDay, startOfDay } from 'date-fns'; +import { endOfDay, isValid, startOfDay } from 'date-fns'; + +interface DateRangeFormProps { + from: FormControl; + to: FormControl; +} @Component({ - selector: 'adf-cloud-date-range-filter', - styleUrls: ['./date-range-filter.component.scss'], - templateUrl: './date-range-filter.component.html' - }) - export class DateRangeFilterComponent implements OnInit { - + selector: 'adf-cloud-date-range-filter', + styleUrls: ['./date-range-filter.component.scss'], + templateUrl: './date-range-filter.component.html' +}) +export class DateRangeFilterComponent implements OnInit { @Input() processFilterProperty: ProcessFilterProperties; @@ -43,9 +47,9 @@ import { endOfDay, startOfDay } from 'date-fns'; type: DateCloudFilterType; filteredProperties: ProcessFilterOptions[] = []; - dateRangeForm = new UntypedFormGroup({ - from: new UntypedFormControl(), - to: new UntypedFormControl() + dateRangeForm = new FormGroup({ + from: new FormControl(), + to: new FormControl() }); ngOnInit() { @@ -69,14 +73,17 @@ import { endOfDay, startOfDay } from 'date-fns'; } onDateRangeClosed() { + const startDate = isValid(this.dateRangeForm.controls.from.value) ? startOfDay(this.dateRangeForm.controls.from.value).toISOString() : null; + const endDate = isValid(this.dateRangeForm.controls.to.value) ? endOfDay(this.dateRangeForm.controls.to.value).toISOString() : null; + const dateRange = { - startDate: startOfDay(new Date(this.dateRangeForm.controls.from.value)).toISOString(), - endDate: endOfDay(new Date(this.dateRangeForm.controls.to.value)).toISOString() + startDate, + endDate }; this.dateChanged.emit(dateRange); } - private hasPreselectedValues() { + private hasPreselectedValues(): boolean { return !!this.processFilterProperty?.attributes && !!this.processFilterProperty?.value; } @@ -94,7 +101,7 @@ import { endOfDay, startOfDay } from 'date-fns'; return this.processFilterProperty.attributes[key]; } - private getFilterValue(attribute: string) { + private getFilterValue(attribute: string): T { return this.processFilterProperty.value[attribute]; } @@ -115,7 +122,7 @@ import { endOfDay, startOfDay } from 'date-fns'; } private createDefaultDateOptions(): ProcessFilterOptions[] { - return [ + return [ { value: DateCloudFilterType.NO_DATE, label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.DATE_RANGE.NO_DATE' @@ -154,4 +161,4 @@ import { endOfDay, startOfDay } from 'date-fns'; } ]; } - } +} diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.html b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.html index 1358956db9..25444fc4cc 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.html +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.html @@ -67,7 +67,7 @@ { let component: EditProcessFilterCloudComponent; let service: ProcessFilterCloudService; let fixture: ComponentFixture; + let nativeElement: HTMLElement; let dialog: MatDialog; let appsService: AppsProcessCloudService; let processService: ProcessCloudService; @@ -89,6 +89,7 @@ describe('EditProcessFilterCloudComponent', () => { }); fixture = TestBed.createComponent(EditProcessFilterCloudComponent); component = fixture.componentInstance; + nativeElement = fixture.debugElement.nativeElement; service = TestBed.inject(ProcessFilterCloudService); appsService = TestBed.inject(AppsProcessCloudService); processService = TestBed.inject(ProcessCloudService); @@ -111,6 +112,29 @@ describe('EditProcessFilterCloudComponent', () => { fixture.destroy(); }); + const clickExpansionPanel = async () => { + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); + expansionPanel.click(); + + fixture.detectChanges(); + await fixture.whenStable(); + }; + + const clickDeleteButton = async () => { + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + deleteButton.click(); + + fixture.detectChanges(); + await fixture.whenStable(); + }; + + const clickPropertyStatus = () => { + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); + stateElement.click(); + + fixture.detectChanges(); + }; + it('should not raise filter change events if filter remains the same', () => { let count = 0; component.filterChange.subscribe(() => count++); @@ -145,8 +169,8 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-process-filter-title-id'); - const subTitle = fixture.debugElement.nativeElement.querySelector('#adf-edit-process-filter-sub-title-id'); + const title = nativeElement.querySelector('#adf-edit-process-filter-title-id'); + const subTitle = nativeElement.querySelector('#adf-edit-process-filter-sub-title-id'); expect(title).toBeDefined(); expect(subTitle).toBeDefined(); @@ -162,7 +186,7 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-process-filter-title-id'); + const title = nativeElement.querySelector('#adf-edit-process-filter-title-id'); expect(title).toBeNull(); }); @@ -173,9 +197,9 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-process-filter-title-id'); - const subTitle = fixture.debugElement.nativeElement.querySelector('#adf-edit-process-filter-sub-title-id'); - const matSpinnerElement = fixture.debugElement.nativeElement.querySelector('.adf-cloud-edit-process-filter-loading-margin'); + const title = nativeElement.querySelector('#adf-edit-process-filter-title-id'); + const subTitle = nativeElement.querySelector('#adf-edit-process-filter-sub-title-id'); + const matSpinnerElement = nativeElement.querySelector('.adf-cloud-edit-process-filter-loading-margin'); expect(matSpinnerElement).toBeNull(); expect(title).toBeDefined(); @@ -192,7 +216,7 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const matSpinnerElement = fixture.debugElement.nativeElement.querySelector('.adf-cloud-edit-process-filter-loading-margin'); + const matSpinnerElement = nativeElement.querySelector('.adf-cloud-edit-process-filter-loading-margin'); expect(matSpinnerElement).toBeDefined(); }); @@ -225,34 +249,28 @@ describe('EditProcessFilterCloudComponent', () => { describe('Save & Delete buttons', () => { it('should enable delete button for custom process filters', async () => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(deleteButton.disabled).toEqual(false); }); it('should enable save button if the filter is changed for custom process filters', (done) => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); component.editProcessFilterForm.valueChanges .pipe(debounceTime(500)) .subscribe(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); fixture.detectChanges(); expect(saveButton.disabled).toBe(false); done(); }); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); - fixture.detectChanges(); + clickPropertyStatus(); const stateOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); stateOptions[2].nativeElement.click(); @@ -261,13 +279,9 @@ describe('EditProcessFilterCloudComponent', () => { it('should disable save button if the filter is not changed for custom filter', async () => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); expect(saveButton.disabled).toBe(true); }); }); @@ -288,25 +302,17 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); expect(saveButton.disabled).toEqual(true); }); it('should disable saveAs button if the process filter is not changed for custom filter', async () => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); expect(saveButton.disabled).toEqual(true); }); @@ -324,22 +330,20 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); component.editProcessFilterForm.valueChanges .pipe(debounceTime(500)) .subscribe(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); fixture.detectChanges(); expect(saveButton.disabled).toEqual(false); done(); }); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); - fixture.detectChanges(); + clickPropertyStatus(); const stateOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); stateOptions[2].nativeElement.click(); @@ -348,22 +352,20 @@ describe('EditProcessFilterCloudComponent', () => { it('should enable saveAs button if the filter values are changed for custom filter', (done) => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); component.editProcessFilterForm.valueChanges .pipe(debounceTime(500)) .subscribe(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); fixture.detectChanges(); expect(saveButton.disabled).toEqual(false); done(); }); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); - fixture.detectChanges(); + clickPropertyStatus(); const stateOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); stateOptions[2].nativeElement.click(); @@ -374,16 +376,11 @@ describe('EditProcessFilterCloudComponent', () => { it('should display current process filter details', async () => { fixture.detectChanges(); await fixture.whenStable(); + await clickExpansionPanel(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - - fixture.detectChanges(); - await fixture.whenStable(); - - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"]'); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"]'); - const orderElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-order"]'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"]'); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"]'); + const orderElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-order"]'); expect(stateElement).toBeDefined(); expect(sortElement).toBeDefined(); expect(orderElement).toBeDefined(); @@ -395,12 +392,9 @@ describe('EditProcessFilterCloudComponent', () => { it('should display state drop down', async () => { fixture.detectChanges(); await fixture.whenStable(); + await clickExpansionPanel(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); + clickPropertyStatus(); fixture.detectChanges(); await fixture.whenStable(); @@ -412,10 +406,9 @@ describe('EditProcessFilterCloudComponent', () => { it('should display sort drop down', async () => { fixture.detectChanges(); await fixture.whenStable(); + await clickExpansionPanel(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"] .mat-select-trigger'); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"] .mat-select-trigger'); sortElement.click(); fixture.detectChanges(); @@ -428,10 +421,9 @@ describe('EditProcessFilterCloudComponent', () => { it('should display order drop down', async () => { fixture.detectChanges(); await fixture.whenStable(); + await clickExpansionPanel(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - const orderElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-order"] .mat-select-trigger'); + const orderElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-order"] .mat-select-trigger'); orderElement.click(); fixture.detectChanges(); @@ -604,7 +596,7 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); const controller = component.editProcessFilterForm.get('appVersionMultiple'); - const appVersionMultiple = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-appVersionMultiple"]'); + const appVersionMultiple = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-appVersionMultiple"]'); appVersionMultiple.click(); fixture.detectChanges(); @@ -633,7 +625,7 @@ describe('EditProcessFilterCloudComponent', () => { await fixture.whenStable(); const controller = component.editProcessFilterForm.get('processDefinitionName'); - const processDefinitionNamesElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-processDefinitionName"]'); + const processDefinitionNamesElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-processDefinitionName"]'); processDefinitionNamesElement.click(); fixture.detectChanges(); @@ -653,13 +645,9 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"]'); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"]'); sortElement.click(); fixture.detectChanges(); @@ -691,13 +679,9 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"]'); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"]'); sortElement.click(); fixture.detectChanges(); @@ -727,13 +711,9 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"]'); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-sort"]'); sortElement.click(); fixture.detectChanges(); @@ -761,14 +741,14 @@ describe('EditProcessFilterCloudComponent', () => { component.toggleFilterActions = true; const saveFilterSpy = spyOn(service, 'updateFilter').and.returnValue(of([fakeFilter])); const saveSpy: jasmine.Spy = spyOn(component.action, 'emit'); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); component.editProcessFilterForm.valueChanges .pipe(debounceTime(500)) .subscribe(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); fixture.detectChanges(); expect(saveButton.disabled).toBe(false); saveButton.click(); @@ -777,9 +757,7 @@ describe('EditProcessFilterCloudComponent', () => { done(); }); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); - fixture.detectChanges(); + clickPropertyStatus(); const stateOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); stateOptions[2].nativeElement.click(); @@ -794,23 +772,13 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); + clickPropertyStatus(); fixture.detectChanges(); await fixture.whenStable(); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); - - fixture.detectChanges(); - await fixture.whenStable(); - - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); - deleteButton.click(); - - fixture.detectChanges(); - await fixture.whenStable(); + await clickDeleteButton(); expect(deleteFilterSpy).toHaveBeenCalled(); expect(deleteSpy).toHaveBeenCalled(); @@ -820,14 +788,14 @@ describe('EditProcessFilterCloudComponent', () => { component.toggleFilterActions = true; const saveAsFilterSpy = spyOn(service, 'addFilter').and.callThrough(); const saveAsSpy = spyOn(component.action, 'emit'); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); component.editProcessFilterForm.valueChanges .pipe(debounceTime(500)) .subscribe(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); fixture.detectChanges(); expect(saveButton.disabled).toBe(false); saveButton.click(); @@ -837,9 +805,7 @@ describe('EditProcessFilterCloudComponent', () => { done(); }); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); - fixture.detectChanges(); + clickPropertyStatus(); const stateOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); stateOptions[2].nativeElement.click(); @@ -848,15 +814,11 @@ describe('EditProcessFilterCloudComponent', () => { it('should display default filter actions', async () => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const saveAsButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(component.processFilterActions).toBeDefined(); expect(component.processFilterActions.length).toEqual(3); expect(saveButton).toBeDefined(); @@ -902,12 +864,10 @@ describe('EditProcessFilterCloudComponent', () => { await fixture.whenStable(); const saveDefaultFilterSpy = spyOn(service, 'updateFilter').and.returnValue(of([fakeFilter])); - const saveDefaultFilterEmitSpy: jasmine.Spy = spyOn(component.action, 'emit'); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); + const saveDefaultFilterEmitSpy = spyOn(component.action, 'emit'); + await clickExpansionPanel(); - const saveDefaultFilterButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="adf-filter-action-${PROCESS_FILTER_ACTION_SAVE_DEFAULT}"]`); + const saveDefaultFilterButton = nativeElement.querySelector(`[data-automation-id="adf-filter-action-${PROCESS_FILTER_ACTION_SAVE_DEFAULT}"]`); fixture.detectChanges(); expect(saveDefaultFilterButton.disabled).toBe(false); saveDefaultFilterButton.click(); @@ -942,11 +902,9 @@ describe('EditProcessFilterCloudComponent', () => { const restoreDefaultProcessFiltersSpy = spyOn(service, 'getProcessFilters').and.returnValue(of([fakeFilter])); const resetDefaultsFilterSpy = spyOn(service, 'resetProcessFilterToDefaults').and.returnValue(of([fakeFilter])); const resetDefaultsEmitSpy: jasmine.Spy = spyOn(component.action, 'emit'); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); + await clickExpansionPanel(); - const resetButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="adf-filter-action-${PROCESS_FILTER_ACTION_RESTORE}"]`); + const resetButton = nativeElement.querySelector(`[data-automation-id="adf-filter-action-${PROCESS_FILTER_ACTION_RESTORE}"]`); fixture.detectChanges(); expect(resetButton.disabled).toBe(false); resetButton.click(); @@ -966,16 +924,11 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); + await clickExpansionPanel(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - - fixture.detectChanges(); - await fixture.whenStable(); - - const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const saveAsButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(component.processFilterActions).toBeDefined(); expect(component.processFilterActions.length).toEqual(3); expect(saveButton).toBeDefined(); @@ -990,20 +943,132 @@ describe('EditProcessFilterCloudComponent', () => { component.ngOnChanges({ id: processFilterIdChange }); fixture.detectChanges(); - const date = moment(); + const date = endOfDay(new Date()); component.filterChange.subscribe(() => { expect(component.processFilter.lastModifiedTo.toISOString()).toEqual(date.toISOString()); done(); }); - const lastModifiedToControl = component.editProcessFilterForm.get('lastModifiedTo'); - lastModifiedToControl.setValue(date); - date.set({ - hour: 23, - minute: 59, - second: 59 + component.lastModifiedTo.clearValidators(); + component.lastModifiedTo.setValue(new Date()); + + expect(component.lastModifiedTo.valid).toBe(true); + expect(component.editProcessFilterForm.valid).toBe(true); + }); + + it('should set the correct lastModifiedFrom date', (done) => { + component.appName = 'fake'; + component.filterProperties = ['appName', 'processInstanceId', 'priority', 'lastModified']; + const processFilterIdChange = new SimpleChange(undefined, 'mock-process-filter-id', true); + component.ngOnChanges({ id: processFilterIdChange }); + fixture.detectChanges(); + + const date = startOfDay(new Date()); + + component.filterChange.subscribe(() => { + expect(component.processFilter.lastModifiedFrom.toISOString()).toEqual(date.toISOString()); + done(); }); + + component.lastModifiedFrom.clearValidators(); + component.lastModifiedFrom.setValue(new Date()); + + expect(component.lastModifiedFrom.valid).toBe(true); + expect(component.editProcessFilterForm.valid).toBe(true); + }); + + it('should validate lastModifiedTo date input', async () => { + component.appName = 'fake'; + component.filterProperties = ['appName', 'processInstanceId', 'priority', 'lastModified']; + const processFilterIdChange = new SimpleChange(undefined, 'mock-process-filter-id', true); + component.ngOnChanges({ id: processFilterIdChange }); + + fixture.detectChanges(); + await fixture.whenStable(); + + component.onDateChanged('20/03/2023', { key: 'lastModifiedTo' } as any ); + expect(component.lastModifiedTo.value).toEqual(new Date('2023-03-20')); + expect(component.lastModifiedTo.valid).toBeTrue(); + + component.onDateChanged('invalid date', { key: 'lastModifiedTo' } as any); + expect(isValid(component.lastModifiedTo.value)).toBeFalse(); + expect(component.lastModifiedTo.valid).toBeFalse(); + }); + + it('should validate lastModifiedFrom date input', async () => { + component.appName = 'fake'; + component.filterProperties = ['appName', 'processInstanceId', 'priority', 'lastModified']; + const processFilterIdChange = new SimpleChange(undefined, 'mock-process-filter-id', true); + component.ngOnChanges({ id: processFilterIdChange }); + + fixture.detectChanges(); + await fixture.whenStable(); + + component.onDateChanged('20/03/2023', { key: 'lastModifiedFrom' } as any ); + expect(component.lastModifiedFrom.value).toEqual(new Date('2023-03-20')); + expect(component.lastModifiedFrom.valid).toBeTrue(); + + component.onDateChanged('invalid date', { key: 'lastModifiedFrom' } as any); + expect(isValid(component.lastModifiedFrom.value)).toBeFalse(); + expect(component.lastModifiedFrom.valid).toBeFalse(); + }); + + it('should update lastModifiedTo from input', async () => { + component.appName = 'fake'; + component.filterProperties = ['appName', 'processInstanceId', 'priority', 'lastModified']; + const processFilterIdChange = new SimpleChange(undefined, 'mock-process-filter-id', true); + component.ngOnChanges({ id: processFilterIdChange }); + + fixture.detectChanges(); + await fixture.whenStable(); + + const dateInput = nativeElement.querySelector(`[data-automation-id="adf-cloud-edit-process-property-lastModifiedTo"]`); + expect(dateInput).not.toBeNull(); + + dateInput.value = '20/03/2023'; + dateInput.dispatchEvent(new Event('keyup')); + + expect(component.lastModifiedTo.value).toEqual(new Date('2023-03-20')); + expect(component.lastModifiedTo.valid).toBeTrue(); + }); + + it('should update lastModifiedFrom from input', async () => { + component.appName = 'fake'; + component.filterProperties = ['appName', 'processInstanceId', 'priority', 'lastModified']; + const processFilterIdChange = new SimpleChange(undefined, 'mock-process-filter-id', true); + component.ngOnChanges({ id: processFilterIdChange }); + + fixture.detectChanges(); + await fixture.whenStable(); + + const dateInput = nativeElement.querySelector(`[data-automation-id="adf-cloud-edit-process-property-lastModifiedFrom"]`); + expect(dateInput).not.toBeNull(); + + dateInput.value = '20/03/2023'; + dateInput.dispatchEvent(new Event('keyup')); + + expect(component.lastModifiedFrom.value).toEqual(new Date('2023-03-20')); + expect(component.lastModifiedFrom.valid).toBeTrue(); + }); + + it('should fail validating lastModifiedFrom from input', async () => { + component.appName = 'fake'; + component.filterProperties = ['appName', 'processInstanceId', 'priority', 'lastModified']; + const processFilterIdChange = new SimpleChange(undefined, 'mock-process-filter-id', true); + component.ngOnChanges({ id: processFilterIdChange }); + + fixture.detectChanges(); + await fixture.whenStable(); + + const dateInput = nativeElement.querySelector(`[data-automation-id="adf-cloud-edit-process-property-lastModifiedFrom"]`); + expect(dateInput).not.toBeNull(); + + dateInput.value = 'invalid'; + dateInput.dispatchEvent(new Event('keyup')); + + expect(isValid(component.lastModifiedFrom.value)).toBeFalse(); + expect(component.lastModifiedFrom.valid).toBeFalse(); }); it('should set date range filter type when range is selected', (done) => { @@ -1014,8 +1079,7 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); component.filterChange.subscribe(() => { - const completedDateTypeControl = component.editProcessFilterForm.get('completedDateType'); - expect(completedDateTypeControl.value).toEqual(DateCloudFilterType.RANGE); + expect(component.completedDateType.value).toEqual(DateCloudFilterType.RANGE); done(); }); @@ -1023,8 +1087,8 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); const dateFilter = { - startDate: moment().startOf('day').toISOString(true), - endDate: moment().endOf('day').toISOString(true) + startDate: startOfDay(new Date()).toISOString(), + endDate: endOfDay(new Date()).toISOString() }; component.onDateRangeFilterChanged(dateFilter, { @@ -1057,8 +1121,7 @@ describe('EditProcessFilterCloudComponent', () => { done(); }); - const startedDateTypeControl = component.editProcessFilterForm.get('completedDateType'); - startedDateTypeControl.setValue(DateCloudFilterType.TODAY); + component.completedDateType.setValue(DateCloudFilterType.TODAY); }); it('should update form on date range value is updated', (done) => { @@ -1069,8 +1132,8 @@ describe('EditProcessFilterCloudComponent', () => { fixture.detectChanges(); const dateFilter = { - startDate: moment().startOf('day').toISOString(true), - endDate: moment().endOf('day').toISOString(true) + startDate: startOfDay(new Date()).toISOString(), + endDate: endOfDay(new Date()).toISOString() }; component.filterChange.subscribe(() => { @@ -1079,8 +1142,7 @@ describe('EditProcessFilterCloudComponent', () => { done(); }); - const startedDateTypeControl = component.editProcessFilterForm.get('completedDateType'); - startedDateTypeControl.setValue(DateCloudFilterType.RANGE); + component.completedDateType.setValue(DateCloudFilterType.RANGE); component.onDateRangeFilterChanged(dateFilter, { key: 'completedDateRange', @@ -1095,60 +1157,44 @@ describe('EditProcessFilterCloudComponent', () => { }); }); - it('should call restore default filters service on deletion of last filter', (done) => { + it('should call restore default filters service on deletion of last filter', async () => { component.toggleFilterActions = true; const deleteFilterSpy = spyOn(service, 'deleteFilter').and.returnValue(of([])); const restoreFiltersSpy = spyOn(component, 'restoreDefaultProcessFilters').and.returnValue(of([])); const deleteSpy: jasmine.Spy = spyOn(component.action, 'emit'); - fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); fixture.detectChanges(); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); - fixture.detectChanges(); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); - deleteButton.click(); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(deleteFilterSpy).toHaveBeenCalled(); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(deleteSpy).toHaveBeenCalled(); - expect(restoreFiltersSpy).toHaveBeenCalled(); - done(); - }); + await clickExpansionPanel(); + clickPropertyStatus(); + await clickDeleteButton(); - }); + expect(deleteFilterSpy).toHaveBeenCalled(); + + fixture.detectChanges(); + await fixture.whenStable(); + + expect(deleteSpy).toHaveBeenCalled(); + expect(restoreFiltersSpy).toHaveBeenCalled(); }); - it('should not call restore default filters service on deletion first filter', (done) => { + it('should not call restore default filters service on deletion first filter', async () => { component.toggleFilterActions = true; const deleteFilterSpy = spyOn(service, 'deleteFilter').and.returnValue(of([new ProcessFilterCloudModel({ name: 'mock-filter-name' })])); const restoreFiltersSpy = spyOn(component, 'restoreDefaultProcessFilters').and.returnValue(of([])); const deleteSpy: jasmine.Spy = spyOn(component.action, 'emit'); fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger'); - stateElement.click(); - fixture.detectChanges(); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); - deleteButton.click(); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(deleteFilterSpy).toHaveBeenCalled(); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(deleteSpy).toHaveBeenCalled(); - expect(restoreFiltersSpy).not.toHaveBeenCalled(); - done(); - }); + await clickExpansionPanel(); + clickPropertyStatus(); - }); + fixture.detectChanges(); + await clickDeleteButton(); + + expect(deleteFilterSpy).toHaveBeenCalled(); + fixture.detectChanges(); + await fixture.whenStable(); + expect(deleteSpy).toHaveBeenCalled(); + expect(restoreFiltersSpy).not.toHaveBeenCalled(); }); it('should build initiator as object array', () => { diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts index 0608f44265..31293de0fa 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/edit-process-filter-cloud.component.ts @@ -16,12 +16,11 @@ */ import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges, OnDestroy, ViewEncapsulation } from '@angular/core'; -import { UntypedFormGroup, UntypedFormBuilder, AbstractControl } from '@angular/forms'; +import { FormBuilder, AbstractControl, FormGroup, FormControl } from '@angular/forms'; import { DateAdapter } from '@angular/material/core'; import { MatDialog } from '@angular/material/dialog'; import { debounceTime, filter, takeUntil, finalize, switchMap, tap } from 'rxjs/operators'; import { Subject, Observable, Subscription } from 'rxjs'; -import moment, { Moment } from 'moment'; import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service'; import { ProcessFilterCloudModel, @@ -30,13 +29,14 @@ import { ProcessFilterOptions, ProcessSortFilterProperty } from '../models/process-filter-cloud.model'; -import { TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; +import { DateFnsUtils, TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core'; import { ProcessFilterCloudService } from '../services/process-filter-cloud.service'; import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud.component'; import { ProcessCloudService } from '../../services/process-cloud.service'; import { DateCloudFilterType, DateRangeFilter } from '../../../models/date-cloud-filter.model'; import { IdentityUserModel } from '../../../people/models/identity-user.model'; import { Environment } from '../../../common/interface/environment.interface'; +import { endOfDay, isValid, startOfDay } from 'date-fns'; export const PROCESS_FILTER_ACTION_SAVE = 'save'; export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs'; @@ -52,6 +52,19 @@ export interface DropdownOption { label: string; } +interface ProcessFilterFormProps { + appName?: FormControl; + appVersion?: FormControl; + processDefinitionName?: FormControl; + lastModifiedFrom?: FormControl; + lastModifiedTo?: FormControl; + status?: FormControl; + order?: FormControl; + sort?: FormControl; + completedDateType?: FormControl; + [x: string]: FormControl; +} + @Component({ selector: 'adf-cloud-edit-process-filter', templateUrl: './edit-process-filter-cloud.component.html', @@ -165,7 +178,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes value: '' }; processDefinitionNames: any[] = []; - editProcessFilterForm: UntypedFormGroup; + editProcessFilterForm: FormGroup; processFilterProperties: ProcessFilterProperties[] = []; processFilterActions: ProcessFilterAction[] = []; toggleFilterActions: boolean = false; @@ -177,9 +190,9 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes private filterChangeSub: Subscription; constructor( - private formBuilder: UntypedFormBuilder, + private formBuilder: FormBuilder, public dialog: MatDialog, - private dateAdapter: DateAdapter, + private dateAdapter: DateAdapter, private userPreferencesService: UserPreferencesService, private translateService: TranslationService, private processFilterCloudService: ProcessFilterCloudService, @@ -222,6 +235,18 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes return properties.reduce((result, current) => Object.assign(result, current), {}); } + get lastModifiedFrom(): AbstractControl { + return this.editProcessFilterForm.get('lastModifiedFrom'); + } + + get lastModifiedTo(): AbstractControl { + return this.editProcessFilterForm.get('lastModifiedTo'); + } + + get completedDateType(): AbstractControl { + return this.editProcessFilterForm.get('completedDateType'); + } + private getAttributesControlConfig(property: ProcessFilterProperties) { return Object.values(property.attributes).reduce((result, key) => { result[key] = property.value[key]; @@ -258,7 +283,8 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes filter(() => this.isFormValid()), takeUntil(this.onDestroy$) ) - .subscribe((formValues: ProcessFilterCloudModel) => { + .subscribe((formValues: Partial) => { + this.setLastModifiedFromFilter(formValues); this.setLastModifiedToFilter(formValues); const newValue = new ProcessFilterCloudModel(Object.assign({}, this.processFilter, formValues)); @@ -363,14 +389,21 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes return this.editProcessFilterForm.get(property.key); } - onDateChanged(newDateValue: Moment, dateProperty: ProcessFilterProperties) { + onDateChanged(newDateValue: Date | string, dateProperty: ProcessFilterProperties) { if (newDateValue) { const controller = this.getPropertyController(dateProperty); - if (newDateValue.isValid()) { - controller.setValue(newDateValue); + let date = newDateValue; + + if (typeof newDateValue === 'string') { + date = DateFnsUtils.parseDate(newDateValue, 'dd/MM/yyyy'); + } + + if (isValid(date)) { + controller.setValue(date); controller.setErrors(null); } else { + controller.setValue(date); controller.setErrors({ invalid: true }); } } @@ -484,7 +517,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes }); dialogRef.afterClosed().subscribe((result) => { if (result && result.action === ProcessFilterDialogCloudComponent.ACTION_SAVE) { - const filterId = Math.random().toString(36).substr(2, 9); + const filterId = Math.random().toString(36).substring(2, 9); const filterKey = this.getSanitizeFilterName(result.name); const newFilter = { name: result.name, @@ -561,15 +594,15 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes : false; } - private setLastModifiedToFilter(formValues: ProcessFilterCloudModel) { - if (formValues.lastModifiedTo && Date.parse(formValues.lastModifiedTo.toString())) { - const lastModifiedToFilterValue = moment(formValues.lastModifiedTo); - lastModifiedToFilterValue.set({ - hour: 23, - minute: 59, - second: 59 - }); - formValues.lastModifiedTo = lastModifiedToFilterValue.toDate(); + private setLastModifiedToFilter(formValues: Partial) { + if (isValid(formValues.lastModifiedTo)) { + formValues.lastModifiedTo = endOfDay(formValues.lastModifiedTo); + } + } + + private setLastModifiedFromFilter(formValues: Partial) { + if (isValid(formValues.lastModifiedFrom)) { + formValues.lastModifiedFrom = startOfDay(formValues.lastModifiedFrom); } } @@ -604,29 +637,18 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes } private createLastModifiedProperty(filterModel: ProcessFilterCloudModel): ProcessFilterProperties[] { - let lastModifiedFrom; - let lastModifiedTo; - - if (filterModel.lastModifiedFrom) { - lastModifiedFrom = moment(filterModel.lastModifiedFrom); - } - - if (filterModel.lastModifiedTo) { - lastModifiedTo = moment(filterModel.lastModifiedTo); - } - return [ { label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.LAST_MODIFIED_DATE_FORM', type: 'date', key: 'lastModifiedFrom', - value: lastModifiedFrom + value: filterModel.lastModifiedFrom }, { label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.LAST_MODIFIED_TO', type: 'date', key: 'lastModifiedTo', - value: lastModifiedTo + value: filterModel.lastModifiedTo } ]; } diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.html index deff273f45..586ee8a73a 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/base-edit-task-filter-cloud.component.html @@ -59,7 +59,7 @@ [attr.data-automation-id]="taskFilterProperty.key"> {{taskFilterProperty.label | translate}} implements OnInit, OnChanges, OnDestroy { - public static ACTION_SAVE = 'save'; - public static ACTION_SAVE_AS = 'saveAs'; - public static ACTION_DELETE = 'delete'; - public static APP_RUNNING_STATUS: string = 'RUNNING'; - public static APPLICATION_NAME: string = 'appName'; - public static PROCESS_DEFINITION_NAME: string = 'processDefinitionName'; - public static LAST_MODIFIED: string = 'lastModified'; - public static SORT: string = 'sort'; - public static ORDER: string = 'order'; - public static DEFAULT_ACTIONS = ['save', 'saveAs', 'delete']; - public static FORMAT_DATE: string = 'DD/MM/YYYY'; - public static ACTIONS_DISABLED_BY_DEFAULT = [BaseEditTaskFilterCloudComponent.ACTION_SAVE, BaseEditTaskFilterCloudComponent.ACTION_DELETE]; + public static ACTIONS_DISABLED_BY_DEFAULT = [ACTION_SAVE, ACTION_DELETE]; /** (required) Name of the app. */ @Input() @@ -94,7 +95,7 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC /** List of task filter actions. */ @Input() - actions: string[] = BaseEditTaskFilterCloudComponent.DEFAULT_ACTIONS; + actions: string[] = [...DEFAULT_ACTIONS]; /** List of sort properties to display. */ @Input() @@ -135,15 +136,13 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC protected onDestroy$ = new Subject(); isLoading: boolean = false; - constructor( - protected formBuilder: UntypedFormBuilder, - protected dateAdapter: DateAdapter, - protected userPreferencesService: UserPreferencesService, - protected appsProcessCloudService: AppsProcessCloudService, - protected taskCloudService: TaskCloudService, - protected dialog: MatDialog, - protected translateService: TranslationService - ) {} + protected translateService = inject(TranslationService); + protected taskCloudService = inject(TaskCloudService); + protected userPreferencesService = inject(UserPreferencesService); + protected appsProcessCloudService = inject(AppsProcessCloudService); + protected dialog = inject(MatDialog); + protected formBuilder = inject(UntypedFormBuilder); + protected dateAdapter = inject>(DateAdapter); ngOnInit() { this.userPreferencesService @@ -167,17 +166,17 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC createFilterActions(): TaskFilterAction[] { return [ { - actionType: BaseEditTaskFilterCloudComponent.ACTION_SAVE, + actionType: ACTION_SAVE, icon: 'adf:save', tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.SAVE' }, { - actionType: BaseEditTaskFilterCloudComponent.ACTION_SAVE_AS, + actionType: ACTION_SAVE_AS, icon: 'adf:save-as', tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.SAVE_AS' }, { - actionType: BaseEditTaskFilterCloudComponent.ACTION_DELETE, + actionType: ACTION_DELETE, icon: 'delete', tooltip: 'ADF_CLOUD_EDIT_TASK_FILTER.TOOL_TIP.DELETE' } @@ -185,13 +184,13 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC } hasFormChanged(action: TaskFilterAction): boolean { - if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_SAVE) { + if (action.actionType === ACTION_SAVE) { return !this.formHasBeenChanged; } - if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_SAVE_AS) { + if (action.actionType === ACTION_SAVE_AS) { return !this.formHasBeenChanged; } - if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_DELETE) { + if (action.actionType === ACTION_DELETE) { return false; } @@ -231,18 +230,18 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC } executeFilterActions(action: TaskFilterAction): void { - if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_SAVE) { + if (action.actionType === ACTION_SAVE) { this.save(action); - } else if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_SAVE_AS) { + } else if (action.actionType === ACTION_SAVE_AS) { this.saveAs(action); - } else if (action.actionType === BaseEditTaskFilterCloudComponent.ACTION_DELETE) { + } else if (action.actionType === ACTION_DELETE) { this.delete(action); } } getRunningApplications() { this.appsProcessCloudService - .getDeployedApplicationsByStatus(BaseEditTaskFilterCloudComponent.APP_RUNNING_STATUS, this.role) + .getDeployedApplicationsByStatus(APP_RUNNING_STATUS, this.role) .subscribe((applications) => { if (applications && applications.length > 0) { applications.map((application) => { @@ -268,7 +267,7 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC checkMandatoryActions(): void { if (this.actions === undefined || this.actions.length === 0) { - this.actions = BaseEditTaskFilterCloudComponent.DEFAULT_ACTIONS; + this.actions = [...DEFAULT_ACTIONS]; } } @@ -284,9 +283,9 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC return this.editTaskFilterForm.get(property.key); } - onDateChanged(newDateValue: any, dateProperty: TaskFilterProperties) { + onDateChanged(newDateValue: string | Moment, dateProperty: TaskFilterProperties) { if (newDateValue) { - const momentDate = moment(newDateValue, BaseEditTaskFilterCloudComponent.FORMAT_DATE, true); + const momentDate = moment(newDateValue, DATE_FORMAT, true); const controller = this.getPropertyController(dateProperty); if (momentDate.isValid()) { @@ -363,7 +362,7 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC } hasLastModifiedProperty(): boolean { - return this.filterProperties.indexOf(BaseEditTaskFilterCloudComponent.LAST_MODIFIED) >= 0; + return this.filterProperties.indexOf(LAST_MODIFIED_PROPERTY) >= 0; } get createSortProperties(): FilterOptions[] { @@ -386,12 +385,12 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC } hasSortProperty(): boolean { - return this.filterProperties.indexOf(BaseEditTaskFilterCloudComponent.SORT) >= 0; + return this.filterProperties.indexOf(SORT_PROPERTY) >= 0; } removeOrderProperty(filteredProperties: TaskFilterProperties[]): TaskFilterProperties[] { if (filteredProperties?.length > 0) { - return filteredProperties.filter((property) => property.key !== BaseEditTaskFilterCloudComponent.ORDER); + return filteredProperties.filter((property) => property.key !== ORDER_PROPERTY); } return []; } @@ -399,11 +398,11 @@ export abstract class BaseEditTaskFilterCloudComponent implements OnInit, OnC createAndFilterProperties() { this.checkMandatoryFilterProperties(); - if (this.checkForProperty(BaseEditTaskFilterCloudComponent.APPLICATION_NAME)) { + if (this.checkForProperty(APPLICATION_NAME)) { this.applicationNames = []; this.getRunningApplications(); } - if (this.checkForProperty(BaseEditTaskFilterCloudComponent.PROCESS_DEFINITION_NAME)) { + if (this.checkForProperty(PROCESS_DEFINITION_NAME)) { this.processDefinitionNames = []; this.getProcessDefinitions(); } diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.ts index a2f59ff564..9c098305e3 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-service-task-filter-cloud.component.ts @@ -15,18 +15,10 @@ * limitations under the License. */ -import { Component, ViewEncapsulation } from '@angular/core'; -import { UntypedFormBuilder } from '@angular/forms'; -import { DateAdapter } from '@angular/material/core'; -import { MatDialog } from '@angular/material/dialog'; +import { Component, ViewEncapsulation, inject } from '@angular/core'; import { takeUntil } from 'rxjs/operators'; import { Observable } from 'rxjs'; -import { Moment } from 'moment'; - import { TaskFilterProperties, TaskFilterAction, ServiceTaskFilterCloudModel } from '../../models/filter-cloud.model'; -import { TranslationService, UserPreferencesService } from '@alfresco/adf-core'; -import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service'; -import { TaskCloudService } from '../../../services/task-cloud.service'; import { ServiceTaskFilterCloudService } from '../../services/service-task-filter-cloud.service'; import { BaseEditTaskFilterCloudComponent, DropdownOption } from './base-edit-task-filter-cloud.component'; @@ -37,16 +29,10 @@ import { BaseEditTaskFilterCloudComponent, DropdownOption } from './base-edit-ta encapsulation: ViewEncapsulation.None }) export class EditServiceTaskFilterCloudComponent extends BaseEditTaskFilterCloudComponent { - constructor( - formBuilder: UntypedFormBuilder, - dialog: MatDialog, - translateService: TranslationService, - private serviceTaskFilterCloudService: ServiceTaskFilterCloudService, - dateAdapter: DateAdapter, - userPreferencesService: UserPreferencesService, - appsProcessCloudService: AppsProcessCloudService, - taskCloudService: TaskCloudService) { - super(formBuilder, dateAdapter, userPreferencesService, appsProcessCloudService, taskCloudService, dialog, translateService); + private serviceTaskFilterCloudService = inject(ServiceTaskFilterCloudService); + + constructor() { + super(); } assignNewFilter(model: ServiceTaskFilterCloudModel) { diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts index 92c68a520a..a9b6cb04b6 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.spec.ts @@ -32,7 +32,6 @@ import { TaskFilterCloudService } from '../../services/task-filter-cloud.service import { TaskCloudService } from '../../../services/task-cloud.service'; import { fakeFilter } from '../../mock/task-filters-cloud.mock'; import { AbstractControl } from '@angular/forms'; -import moment from 'moment'; import { TranslateModule } from '@ngx-translate/core'; import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model'; import { AssignmentType, TaskFilterCloudModel, TaskStatusFilter } from '../../models/filter-cloud.model'; @@ -55,12 +54,14 @@ import { mockFoodUsers } from '../../../../people/mock/people-cloud.mock'; import { mockFoodGroups } from '../../../../group/mock/group-cloud.mock'; import { SimpleChanges } from '@angular/core'; import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component'; +import { set } from 'date-fns'; describe('EditTaskFilterCloudComponent', () => { let component: EditTaskFilterCloudComponent; let service: TaskFilterCloudService; let appsService: AppsProcessCloudService; let fixture: ComponentFixture; + let nativeElement: HTMLElement; let dialog: MatDialog; let alfrescoApiService: AlfrescoApiService; let getTaskFilterSpy: jasmine.Spy; @@ -84,6 +85,7 @@ describe('EditTaskFilterCloudComponent', () => { }); fixture = TestBed.createComponent(EditTaskFilterCloudComponent); component = fixture.componentInstance; + nativeElement = fixture.debugElement.nativeElement; service = TestBed.inject(TaskFilterCloudService); appsService = TestBed.inject(AppsProcessCloudService); taskService = TestBed.inject(TaskCloudService); @@ -101,6 +103,14 @@ describe('EditTaskFilterCloudComponent', () => { afterEach(() => fixture.destroy()); + const clickExpansionPanel = async () => { + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); + expansionPanel.click(); + + fixture.detectChanges(); + await fixture.whenStable(); + }; + it('should fetch task filter by taskId', async () => { component.ngOnChanges({ id: mockTaskFilterIdChange }); fixture.detectChanges(); @@ -222,8 +232,8 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-title-id'); - const subTitle = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-sub-title-id'); + const title = nativeElement.querySelector('#adf-edit-task-filter-title-id'); + const subTitle = nativeElement.querySelector('#adf-edit-task-filter-sub-title-id'); expect(title.innerText).toEqual('FakeInvolvedTasks'); expect(subTitle.innerText.trim()).toEqual('ADF_CLOUD_EDIT_TASK_FILTER.TITLE'); }); @@ -235,7 +245,7 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-title-id'); + const title = nativeElement.querySelector('#adf-edit-task-filter-title-id'); expect(title).toBeNull(); }); @@ -245,9 +255,9 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-title-id'); - const subTitle = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-sub-title-id'); - const matSpinnerElement = fixture.debugElement.nativeElement.querySelector('.adf-cloud-edit-task-filter-loading-margin'); + const title = nativeElement.querySelector('#adf-edit-task-filter-title-id'); + const subTitle = nativeElement.querySelector('#adf-edit-task-filter-sub-title-id'); + const matSpinnerElement = nativeElement.querySelector('.adf-cloud-edit-task-filter-loading-margin'); expect(matSpinnerElement).toBeNull(); expect(title.innerText).toEqual('FakeInvolvedTasks'); @@ -261,7 +271,7 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const matSpinnerElement = fixture.debugElement.nativeElement.querySelector('.adf-cloud-edit-task-filter-loading-margin'); + const matSpinnerElement = nativeElement.querySelector('.adf-cloud-edit-task-filter-loading-margin'); expect(matSpinnerElement).toBeDefined(); }); @@ -296,15 +306,11 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); expect(saveButton.disabled).toBe(true); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(deleteButton.disabled).toBe(true); }); @@ -313,15 +319,11 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); expect(saveButton.disabled).toBe(true); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(deleteButton.disabled).toBe(false); }); @@ -330,20 +332,20 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); component.editTaskFilterForm.valueChanges .pipe(debounceTime(500)) .subscribe(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); fixture.detectChanges(); expect(saveButton.disabled).toBe(false); done(); }); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); const sortOptions = fixture.debugElement.queryAll(By.css('.mat-option-text')); @@ -353,13 +355,9 @@ describe('EditTaskFilterCloudComponent', () => { it('should disable save button if the filter is not changed for custom filter', async () => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); expect(saveButton.disabled).toBe(true); }); }); @@ -371,25 +369,17 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); expect(saveButton.disabled).toEqual(true); }); it('should disable saveAs button if the process filter is not changed for custom filter', async () => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); expect(saveButton.disabled).toEqual(true); }); @@ -400,20 +390,20 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); component.editTaskFilterForm.valueChanges .pipe(debounceTime(500)) .subscribe(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); fixture.detectChanges(); expect(saveButton.disabled).toEqual(false); done(); }); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); @@ -424,20 +414,20 @@ describe('EditTaskFilterCloudComponent', () => { it('should enable saveAs button if the filter values are changed for custom filter', (done) => { component.toggleFilterActions = true; - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); component.editTaskFilterForm.valueChanges .pipe(debounceTime(500)) .subscribe(() => { - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); fixture.detectChanges(); expect(saveButton.disabled).toEqual(false); done(); }); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); @@ -451,31 +441,25 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-status"]'); - const assigneeElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-assignee"]'); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); - const orderElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-order"]'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-status"]'); + const assigneeElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-assignee"]'); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); + const orderElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-order"]'); expect(assigneeElement).toBeDefined(); expect(stateElement.textContent.trim()).toBe('ADF_CLOUD_TASK_FILTERS.STATUS.CREATED'); expect(sortElement.textContent.trim()).toBe('id'); expect(orderElement.textContent.trim()).toBe('ADF_CLOUD_TASK_FILTERS.DIRECTION.ASCENDING'); }); - it('should display all the statuses that are defined in the task filter', () => { + it('should display all the statuses that are defined in the task filter', async () => { + await clickExpansionPanel(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-status"]'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-status"]'); stateElement.click(); fixture.detectChanges(); + await fixture.whenStable(); const statusOptions = fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-edit-task-property-options-status"]')); @@ -489,10 +473,9 @@ describe('EditTaskFilterCloudComponent', () => { it('should display sort drop down', async () => { fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); + await clickExpansionPanel(); + + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); sortElement.click(); fixture.detectChanges(); @@ -504,10 +487,9 @@ describe('EditTaskFilterCloudComponent', () => { it('should display order drop down', async () => { fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const orderElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-order"]'); + await clickExpansionPanel(); + + const orderElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-order"]'); orderElement.click(); fixture.detectChanges(); @@ -572,7 +554,7 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const peopleCloudComponent = fixture.debugElement.nativeElement.querySelector('adf-cloud-people'); + const peopleCloudComponent = nativeElement.querySelector('adf-cloud-people'); expect(peopleCloudComponent).toBeTruthy(); }); @@ -624,7 +606,7 @@ describe('EditTaskFilterCloudComponent', () => { component.ngOnChanges({ id: mockTaskFilterIdChange }); fixture.detectChanges(); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-dueDateRange"] .mat-select-trigger'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-dueDateRange"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); @@ -717,7 +699,7 @@ describe('EditTaskFilterCloudComponent', () => { component.filterProperties = ['assignment']; component.ngOnChanges({ id: mockTaskFilterIdChange }); fixture.detectChanges(); - const assignmentComponent = fixture.debugElement.nativeElement.querySelector('adf-cloud-task-assignment-filter'); + const assignmentComponent = nativeElement.querySelector('adf-cloud-task-assignment-filter'); expect(assignmentComponent).toBeTruthy(); }); @@ -822,10 +804,8 @@ describe('EditTaskFilterCloudComponent', () => { it('should display default sort properties', async () => { component.ngOnChanges({ id: mockTaskFilterIdChange }); fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); + await clickExpansionPanel(); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); sortElement.click(); fixture.detectChanges(); @@ -849,10 +829,8 @@ describe('EditTaskFilterCloudComponent', () => { component.ngOnChanges({ id: mockTaskFilterIdChange }); fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); + await clickExpansionPanel(); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); sortElement.click(); fixture.detectChanges(); @@ -870,10 +848,8 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); component.sortProperties = []; fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const sortElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); + await clickExpansionPanel(); + const sortElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"]'); sortElement.click(); fixture.detectChanges(); @@ -892,15 +868,11 @@ describe('EditTaskFilterCloudComponent', () => { component.toggleFilterActions = true; component.ngOnChanges({ id: mockTaskFilterIdChange }); fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const saveAsButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(component.taskFilterActions.map(action => action.actionType)).toEqual(['save', 'saveAs', 'delete']); expect(component.taskFilterActions.length).toBe(3); expect(saveButton.disabled).toBe(true); @@ -917,18 +889,14 @@ describe('EditTaskFilterCloudComponent', () => { component.toggleFilterActions = true; fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); + await clickExpansionPanel(); - fixture.detectChanges(); - await fixture.whenStable(); - - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); expect(component.taskFilterActions.map(action => action.actionType)).toEqual(['save']); expect(component.taskFilterActions.length).toBe(1); expect(saveButton.disabled).toBeTruthy(); - const saveAsButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const saveAsButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(saveAsButton).toBeFalsy(); expect(deleteButton).toBeFalsy(); }); @@ -939,18 +907,19 @@ describe('EditTaskFilterCloudComponent', () => { component.ngOnChanges({ id: mockTaskFilterIdChange }); fixture.detectChanges(); - const lastModifiedToControl: AbstractControl = component.editTaskFilterForm.get('lastModifiedTo'); - lastModifiedToControl.setValue(new Date().toISOString()); - const lastModifiedToFilter = moment(lastModifiedToControl.value); - lastModifiedToFilter.set({ - hour: 23, - minute: 59, - second: 59 - }); + const date = new Date(); + const lastModifiedToControl = component.editTaskFilterForm.get('lastModifiedTo'); + lastModifiedToControl.setValue(date.toISOString()); + + const lastModifiedToFilter = set(date, { + hours: 23, + minutes: 59, + seconds: 59 + }).toISOString(); component.filterChange.subscribe(() => { if (component.changedTaskFilter instanceof TaskFilterCloudModel) { - expect(component.changedTaskFilter.lastModifiedTo).toEqual(lastModifiedToFilter.toISOString(true)); + expect(component.changedTaskFilter.lastModifiedTo).toEqual(lastModifiedToFilter); } done(); }); @@ -971,11 +940,11 @@ describe('EditTaskFilterCloudComponent', () => { spyOn(service, 'updateFilter').and.returnValue(of([new TaskFilterCloudModel({ name: 'mock-filter-name' })])); fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); + const expansionPanel = nativeElement.querySelector('mat-expansion-panel-header'); expansionPanel.click(); fixture.detectChanges(); tick(); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); tick(); @@ -984,7 +953,7 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); tick(550); - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-save"]'); saveButton.dispatchEvent(new Event('click')); fixture.detectChanges(); tick(); @@ -996,16 +965,14 @@ describe('EditTaskFilterCloudComponent', () => { it('should emit delete event and delete the filter on click of delete button', async () => { spyOn(service, 'deleteFilter').and.returnValue(of(null)); fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + await clickExpansionPanel(); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); await fixture.whenStable(); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(deleteButton.getAttribute('disabled')).toBeNull(); deleteButton.click(); expect(service.deleteFilter).toHaveBeenCalled(); @@ -1015,12 +982,9 @@ describe('EditTaskFilterCloudComponent', () => { it('should emit saveAs event and add filter on click saveAs button', async () => { spyOn(service, 'addFilter').and.returnValue(of(null)); fixture.detectChanges(); + await clickExpansionPanel(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); @@ -1029,7 +993,7 @@ describe('EditTaskFilterCloudComponent', () => { fixture.detectChanges(); await fixture.whenStable(); - const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); + const saveButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]'); saveButton.dispatchEvent(new Event('click')); fixture.detectChanges(); afterClosedSubject.next({ @@ -1048,16 +1012,14 @@ describe('EditTaskFilterCloudComponent', () => { spyOn(service, 'deleteFilter').and.returnValue(of([])); const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([])); fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + await clickExpansionPanel(); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); await fixture.whenStable(); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(deleteButton.disabled).toBe(false); deleteButton.click(); expect(service.deleteFilter).toHaveBeenCalled(); @@ -1069,16 +1031,14 @@ describe('EditTaskFilterCloudComponent', () => { spyOn(service, 'deleteFilter').and.returnValue(of([new TaskFilterCloudModel({ name: 'mock-filter-name' })])); const restoreDefaultFiltersSpy = spyOn(component, 'restoreDefaultTaskFilters').and.returnValue(of([])); fixture.detectChanges(); - const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header'); - expansionPanel.click(); - fixture.detectChanges(); - const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); + await clickExpansionPanel(); + const stateElement = nativeElement.querySelector('[data-automation-id="adf-cloud-edit-task-property-sort"] .mat-select-trigger'); stateElement.click(); fixture.detectChanges(); await fixture.whenStable(); - const deleteButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); + const deleteButton = nativeElement.querySelector('[data-automation-id="adf-filter-action-delete"]'); expect(deleteButton.disabled).toBe(false); deleteButton.click(); expect(service.deleteFilter).toHaveBeenCalled(); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts index 608419d161..1eb06663e7 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/edit-task-filters/edit-task-filter-cloud.component.ts @@ -15,21 +15,14 @@ * limitations under the License. */ -import { Component, ViewEncapsulation } from '@angular/core'; -import { UntypedFormBuilder } from '@angular/forms'; -import { DateAdapter } from '@angular/material/core'; -import { MatDialog } from '@angular/material/dialog'; +import { Component, ViewEncapsulation, inject } from '@angular/core'; import { takeUntil, map } from 'rxjs/operators'; import { Observable } from 'rxjs'; -import moment, { Moment } from 'moment'; - import { TaskFilterCloudModel, TaskFilterProperties, TaskFilterAction, TaskStatusFilter } from '../../models/filter-cloud.model'; import { TaskFilterCloudService } from '../../services/task-filter-cloud.service'; -import { TranslationService, UserPreferencesService } from '@alfresco/adf-core'; -import { AppsProcessCloudService } from '../../../../app/services/apps-process-cloud.service'; import { DateCloudFilterType } from '../../../../models/date-cloud-filter.model'; -import { TaskCloudService } from '../../../services/task-cloud.service'; import { BaseEditTaskFilterCloudComponent, DropdownOption } from './base-edit-task-filter-cloud.component'; +import { set } from 'date-fns'; @Component({ selector: 'adf-cloud-edit-task-filter', @@ -38,16 +31,10 @@ import { BaseEditTaskFilterCloudComponent, DropdownOption } from './base-edit-ta encapsulation: ViewEncapsulation.None }) export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudComponent { - constructor( - formBuilder: UntypedFormBuilder, - dialog: MatDialog, - translateService: TranslationService, - private taskFilterCloudService: TaskFilterCloudService, - dateAdapter: DateAdapter, - userPreferencesService: UserPreferencesService, - appsProcessCloudService: AppsProcessCloudService, - taskCloudService: TaskCloudService) { - super(formBuilder, dateAdapter, userPreferencesService, appsProcessCloudService, taskCloudService, dialog, translateService); + private taskFilterCloudService = inject(TaskFilterCloudService); + + constructor() { + super(); } assignNewFilter(model: TaskFilterCloudModel) { @@ -88,13 +75,15 @@ export class EditTaskFilterCloudComponent extends BaseEditTaskFilterCloudCompone private setLastModifiedToFilter(formValues: TaskFilterCloudModel) { if (formValues.lastModifiedTo && Date.parse(formValues.lastModifiedTo.toString())) { - const lastModifiedToFilterValue = moment(formValues.lastModifiedTo); - lastModifiedToFilterValue.set({ - hour: 23, - minute: 59, - second: 59 - }); - formValues.lastModifiedTo = lastModifiedToFilterValue.toISOString(true); + const lastModifiedToFilterValue = set( + new Date(formValues.lastModifiedTo), + { + hours: 23, + minutes: 59, + seconds: 59 + } + ); + formValues.lastModifiedTo = lastModifiedToFilterValue.toISOString(); } } diff --git a/lib/process-services/src/lib/task-list/components/start-task.component.ts b/lib/process-services/src/lib/task-list/components/start-task.component.ts index 5d001fcbcb..054b5b0ff7 100644 --- a/lib/process-services/src/lib/task-list/components/start-task.component.ts +++ b/lib/process-services/src/lib/task-list/components/start-task.component.ts @@ -15,9 +15,7 @@ * limitations under the License. */ -import { - LogService, UserPreferencesService, UserPreferenceValues, FormFieldModel, FormModel, DateFnsUtils -} from '@alfresco/adf-core'; +import { LogService, FormFieldModel, FormModel, DateFnsUtils, AdfDateFnsAdapter, ADF_DATE_FORMATS } from '@alfresco/adf-core'; import { Component, EventEmitter, Input, OnInit, Output, ViewEncapsulation, OnDestroy } from '@angular/core'; import { DateAdapter, MAT_DATE_FORMATS } from '@angular/material/core'; import { EMPTY, Observable, Subject } from 'rxjs'; @@ -28,7 +26,6 @@ import { switchMap, defaultIfEmpty, takeUntil } from 'rxjs/operators'; import { UntypedFormBuilder, AbstractControl, Validators, UntypedFormGroup, UntypedFormControl } from '@angular/forms'; import { UserProcessModel } from '../../common/models/user-process.model'; import { isValid } from 'date-fns'; -import { DateFnsAdapter, MAT_DATE_FNS_FORMATS } from '@angular/material-date-fns-adapter'; const FORMAT_DATE = 'DD/MM/YYYY'; const MAX_LENGTH = 255; @@ -38,8 +35,8 @@ const MAX_LENGTH = 255; templateUrl: './start-task.component.html', styleUrls: ['./start-task.component.scss'], providers: [ - { provide: DateAdapter, useClass: DateFnsAdapter }, - { provide: MAT_DATE_FORMATS, useValue: MAT_DATE_FNS_FORMATS }], + { provide: DateAdapter, useClass: AdfDateFnsAdapter }, + { provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS }], encapsulation: ViewEncapsulation.None }) export class StartTaskComponent implements OnInit, OnDestroy { @@ -75,8 +72,6 @@ export class StartTaskComponent implements OnInit, OnDestroy { private onDestroy$ = new Subject(); constructor(private taskService: TaskListService, - private dateAdapter: DateAdapter, - private userPreferencesService: UserPreferencesService, private formBuilder: UntypedFormBuilder, private logService: LogService) { } @@ -90,11 +85,6 @@ export class StartTaskComponent implements OnInit, OnDestroy { this.field = new FormFieldModel(new FormModel(), { id: this.assigneeId, value: this.assigneeId, placeholder: 'Assignee' }); - this.userPreferencesService - .select(UserPreferenceValues.Locale) - .pipe(takeUntil(this.onDestroy$)) - .subscribe(locale => this.dateAdapter.setLocale(DateFnsUtils.getLocaleFromString(locale))); - this.loadFormsTask(); this.buildForm(); } @@ -142,10 +132,10 @@ export class StartTaskComponent implements OnInit, OnDestroy { } this.taskService.createNewTask(this.taskDetailsModel) .pipe( - switchMap((createRes: any) => + switchMap((createRes) => this.attachForm(createRes.id, this.taskDetailsModel.formKey).pipe( defaultIfEmpty(createRes), - switchMap((attachRes: any) => + switchMap((attachRes) => this.assignTaskByUserId(createRes.id, this.assigneeId).pipe( defaultIfEmpty(attachRes ? attachRes : createRes) ) @@ -183,7 +173,7 @@ export class StartTaskComponent implements OnInit, OnDestroy { return firstName + delimiter + lastName; } - onDateChanged(newDateValue: any) { + onDateChanged(newDateValue: Date | string) { this.dateError = false; if (newDateValue) { @@ -233,12 +223,11 @@ export class StartTaskComponent implements OnInit, OnDestroy { return response; } - private assignTaskByUserId(taskId: string, userId: any): Observable { - let response: any = EMPTY; + private assignTaskByUserId(taskId: string, userId: any): Observable { if (taskId && userId) { - response = this.taskService.assignTaskByUserId(taskId, userId); + return this.taskService.assignTaskByUserId(taskId, userId); } - return response; + return EMPTY; } private loadFormsTask(): void {