From 2005bfa372960e99507122cc36a7eab5ea130535 Mon Sep 17 00:00:00 2001 From: Alfredo Li Date: Wed, 26 Jan 2022 15:06:51 -0600 Subject: [PATCH] dotCMS/core#21507 add state management to Dot Palette --- ...dot-palette-content-type.component.spec.ts | 39 +-- .../dot-palette-contentlets.component.spec.ts | 249 ++++-------------- .../dot-palette-contentlets.component.ts | 8 +- .../dot-palette-contentlets.module.ts | 6 +- .../dot-palette/dot-palette.component.spec.ts | 124 +++++++-- .../dot-palette/dot-palette.component.ts | 4 +- .../store/dot-palette.store.spec.ts | 165 ++++++++++++ .../dot-palette/store/dot-palette.store.ts | 2 - .../dot-edit-content.component.spec.ts | 57 ++-- .../content/dot-edit-content.module.ts | 2 + 10 files changed, 391 insertions(+), 265 deletions(-) create mode 100644 apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/store/dot-palette.store.spec.ts diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-content-type/dot-palette-content-type.component.spec.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-content-type/dot-palette-content-type.component.spec.ts index 4ec18fad72..c0bd93d645 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-content-type/dot-palette-content-type.component.spec.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-content-type/dot-palette-content-type.component.spec.ts @@ -12,26 +12,38 @@ import { DotPaletteInputFilterModule } from '../dot-palette-input-filter/dot-pal import { HttpClientTestingModule } from '@angular/common/http/testing'; import { CoreWebService, CoreWebServiceMock } from '@dotcms/dotcms-js'; -const data = [ +export const contentTypeDataMock = [ { + baseType: 'Product', + clazz: '', + defaultType: false, icon: 'cloud', id: 'a1661fbc-9e84-4c00-bd62-76d633170da3', name: 'Product', variable: 'Product' }, { + baseType: 'Blog', + clazz: '', + defaultType: false, icon: 'alt_route', id: '799f176a-d32e-4844-a07c-1b5fcd107578', name: 'Blog', variable: 'Blog' }, { + baseType: 'Form', + clazz: '', + defaultType: false, icon: 'cloud', id: '897cf4a9-171a-4204-accb-c1b498c813fe', name: 'Contact', variable: 'Form' }, { + baseType: 'Text', + clazz: '', + defaultType: false, icon: 'person', id: '6044a806-f462-4977-a353-57539eac2a2c', name: 'Long name Blog Comment', @@ -61,10 +73,7 @@ describe('DotPaletteContentTypeComponent', () => { beforeEach(() => { TestBed.configureTestingModule({ - declarations: [ - TestHostComponent, - DotPaletteContentTypeComponent - ], + declarations: [TestHostComponent, DotPaletteContentTypeComponent], imports: [ DotPipesModule, DotIconModule, @@ -75,7 +84,7 @@ describe('DotPaletteContentTypeComponent', () => { ], providers: [ { provide: DotContentletEditorService, useClass: MockDotContentletEditorService }, - { provide: CoreWebService, useClass: CoreWebServiceMock }, + { provide: CoreWebService, useClass: CoreWebServiceMock } ] }); @@ -89,7 +98,7 @@ describe('DotPaletteContentTypeComponent', () => { }); it('should list items correctly', () => { - componentHost.items = data; + componentHost.items = contentTypeDataMock; fixtureHost.detectChanges(); const contents = fixtureHost.debugElement.queryAll(By.css('[data-testId="paletteItem"]')); expect(contents.length).toEqual(4); @@ -104,7 +113,7 @@ describe('DotPaletteContentTypeComponent', () => { }); it('should filter items on search', () => { - componentHost.items = data; + componentHost.items = contentTypeDataMock; fixtureHost.detectChanges(); const input = fixtureHost.debugElement.query(By.css('dot-palette-input-filter')); input.componentInstance.filter.emit('Product'); @@ -114,24 +123,22 @@ describe('DotPaletteContentTypeComponent', () => { }); it('should set Dragged ContentType on dragStart', () => { - componentHost.items = data; + componentHost.items = contentTypeDataMock; fixtureHost.detectChanges(); const content = fixtureHost.debugElement.query(By.css('[data-testId="paletteItem"]')); - content.triggerEventHandler('dragstart', data[0]); + content.triggerEventHandler('dragstart', contentTypeDataMock[0]); expect(dotContentletEditorService.setDraggedContentType).toHaveBeenCalledOnceWith( - data[0] as DotCMSContentType + contentTypeDataMock[0] as DotCMSContentType ); }); it('should emit event to show a specific contentlet', () => { - componentHost.items = data; + componentHost.items = contentTypeDataMock; spyOn(de.componentInstance.selected, 'emit').and.callThrough(); fixtureHost.detectChanges(); - const buttons = fixtureHost.debugElement.queryAll( - By.css('[data-testId="paletteItem"]') - ); + const buttons = fixtureHost.debugElement.queryAll(By.css('[data-testId="paletteItem"]')); buttons[3].nativeElement.click(); - expect(de.componentInstance.itemsFiltered).toEqual(data); + expect(de.componentInstance.itemsFiltered).toEqual(contentTypeDataMock); expect(de.componentInstance.selected.emit).toHaveBeenCalledWith('Text'); }); }); diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component.spec.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component.spec.ts index 5d3686ae34..60acd7cfe4 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component.spec.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component.spec.ts @@ -10,13 +10,11 @@ import { DotPaletteInputFilterModule } from '../dot-palette-input-filter/dot-pal import { HttpClientTestingModule } from '@angular/common/http/testing'; import { CoreWebService, CoreWebServiceMock } from '@dotcms/dotcms-js'; import { DotPaletteContentletsComponent } from './dot-palette-contentlets.component'; -import { Observable, of } from 'rxjs'; -import { PaginatorService } from '@services/paginator'; -import { DotESContentService } from '@services/dot-es-content/dot-es-content.service'; import { PaginatorModule } from 'primeng/paginator'; import { DotCMSContentlet } from '@dotcms/dotcms-models'; +import { LazyLoadEvent } from 'primeng/api'; -const formData = { +export const contentletFormDataMock = { baseType: 'FORM', clazz: 'com.dotcms.contenttype.model.type.ImmutableFormContentType', defaultType: false, @@ -39,7 +37,7 @@ const formData = { workflows: [] }; -const productData = { +export const contentletProductDataMock = { baseType: 'CONTENT', contentType: 'Product', contentTypeIcon: 'inventory', @@ -59,15 +57,20 @@ const productData = { selector: 'dot-test-host-component', template: ` ` }) class TestHostComponent { - @Input() languageId: string; - @Input() contentTypeVariable: string; - @Output() filter = new EventEmitter(); + @Input() items: DotCMSContentlet[]; + @Input() loading: boolean; + @Input() totalRecords: number; + + @Output() back = new EventEmitter(); + @Output() filter = new EventEmitter(); + @Output() paginate = new EventEmitter(); } @Injectable() @@ -75,32 +78,6 @@ class MockDotContentletEditorService { setDraggedContentType = jasmine.createSpy('setDraggedContentType'); } -@Injectable() -class MockPaginatorService { - url: string; - paginationPerPage = 10; - maxLinksPage = 5; - sortField: string; - sortOrder: string; - totalRecords = 40; - - setExtraParams(): void {} - - public getWithOffset(): Observable { - return null; - } -} - -@Injectable() -class MockESPaginatorService { - paginationPerPage = 15; - totalRecords = 20; - - public get(): Observable { - return null; - } -} - @Component({ selector: 'dot-contentlet-icon', template: '' @@ -113,9 +90,8 @@ export class DotContentletIconMockComponent { describe('DotPaletteContentletsComponent', () => { let fixtureHost: ComponentFixture; let componentHost: TestHostComponent; + let component: DotPaletteContentletsComponent; let dotContentletEditorService: DotContentletEditorService; - let paginatorService: PaginatorService; - let paginatorESService: DotESContentService; let de: DebugElement; beforeEach(() => { @@ -137,9 +113,6 @@ describe('DotPaletteContentletsComponent', () => { ], providers: [ { provide: DotContentletEditorService, useClass: MockDotContentletEditorService }, - { provide: PaginatorService, useClass: MockPaginatorService }, - { provide: DotESContentService, useClass: MockESPaginatorService }, - { provide: CoreWebService, useClass: CoreWebServiceMock } ] }); @@ -149,46 +122,15 @@ describe('DotPaletteContentletsComponent', () => { de = fixtureHost.debugElement.query(By.css('dot-palette-contentlets')); dotContentletEditorService = de.injector.get(DotContentletEditorService); - paginatorService = de.injector.get(PaginatorService); - paginatorESService = de.injector.get(DotESContentService); - spyOn(paginatorService, 'setExtraParams').and.callThrough(); + component = de.componentInstance; fixtureHost.detectChanges(); }); - it('should load initial params correctly and loading Forms via PaginatorService', async () => { - spyOn(paginatorService, 'getWithOffset').and.returnValue(of([formData])); - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'forms'; - - fixtureHost.detectChanges(); - await fixtureHost.whenStable(); - - const contentletIcon = fixtureHost.debugElement.query(By.css('dot-contentlet-icon')); - - expect(paginatorService.url).toBe('v1/contenttype'); - expect(paginatorService.paginationPerPage).toBe(25); - expect(paginatorService.sortField).toBe('modDate'); - expect(paginatorService.sortOrder).toBe(1); - expect(paginatorService.setExtraParams).toHaveBeenCalledWith('type', 'Form'); - expect(paginatorService.getWithOffset).toHaveBeenCalledWith(0); - expect(de.componentInstance.items.length).toBe(1); - expect(de.componentInstance.hideNoResults).toBe(true); - expect(contentletIcon.componentInstance.icon).toBe(formData.icon); - expect(contentletIcon.componentInstance.size).toBe('45px'); - }); - - it('should load intital Product data via DotESContent', async () => { - spyOn(paginatorESService, 'get').and.returnValue( - of({ - contentTook: 0, - jsonObjectView: { contentlets: [productData] }, - queryTook: 1, - resultsSize: 20 - }) - ); - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'Product'; + it('should load initial params correctly with contentlets', async () => { + componentHost.items = [contentletProductDataMock] as unknown as DotCMSContentlet[]; + componentHost.loading = false; + componentHost.totalRecords = 10; fixtureHost.detectChanges(); await fixtureHost.whenStable(); @@ -196,32 +138,16 @@ describe('DotPaletteContentletsComponent', () => { const contentletImg = fixtureHost.debugElement.query( By.css('[data-testId="paletteItem"] img') ); - - expect(paginatorESService.get).toHaveBeenCalledWith({ - itemsPerPage: 25, - lang: '1', - filter: '', - offset: '0', - query: `+contentType: ${productData.contentType}` - }); expect(de.componentInstance.items.length).toBe(1); - expect(de.componentInstance.hideNoResults).toBe(true); expect(contentletImg.nativeElement.src).toContain( - `/dA/${productData.inode}/titleImage/48w` + `/dA/${contentletProductDataMock.inode}/titleImage/48w` ); }); it('should load with No Results data', async () => { - spyOn(paginatorESService, 'get').and.returnValue( - of({ - contentTook: 0, - jsonObjectView: { contentlets: [] }, - queryTook: 1, - resultsSize: 0 - }) - ); - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'Product'; + componentHost.items = [] as unknown as DotCMSContentlet[]; + componentHost.loading = false; + componentHost.totalRecords = 0; fixtureHost.detectChanges(); await fixtureHost.whenStable(); @@ -232,21 +158,15 @@ describe('DotPaletteContentletsComponent', () => { expect(noResultsContainer).toBeTruthy(); }); - it('should paginate products data via DotESContent', async () => { + it('should emit paginate event', async () => { + spyOn(component.paginate, 'emit').and.callThrough(); let productsArray = []; - for (let index = 0; index < 20; index++) { - productsArray.push(productData); + for (let index = 0; index < 30; index++) { + productsArray.push(contentletProductDataMock); } - spyOn(paginatorESService, 'get').and.returnValue( - of({ - contentTook: 0, - jsonObjectView: { contentlets: productsArray }, - queryTook: 1, - resultsSize: 20 - }) - ); - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'Product'; + componentHost.items = [productsArray] as unknown as DotCMSContentlet[]; + componentHost.loading = false; + componentHost.totalRecords = 30; fixtureHost.detectChanges(); await fixtureHost.whenStable(); @@ -255,121 +175,50 @@ describe('DotPaletteContentletsComponent', () => { expect(paginatorContainer).toBeTruthy(); expect(paginatorContainer.componentInstance.rows).toBe(25); - expect(paginatorContainer.componentInstance.totalRecords).toBe(20); + expect(paginatorContainer.componentInstance.totalRecords).toBe(30); expect(paginatorContainer.componentInstance.showFirstLastIcon).toBe(false); expect(paginatorContainer.componentInstance.pageLinkSize).toBe('2'); - paginatorContainer.componentInstance.onPageChange.emit({ first: 15 }); + paginatorContainer.componentInstance.onPageChange.emit({ first: 25 }); fixtureHost.detectChanges(); await fixtureHost.whenStable(); - expect(paginatorESService.get).toHaveBeenCalledWith({ - itemsPerPage: 25, - lang: '1', - filter: '', - offset: '15', - query: `+contentType: ${productData.contentType}` + expect(component.paginate.emit).toHaveBeenCalledWith({ + first: 25 }); }); - it('should paginate forms data via PaginatorService', async () => { - let formsArray = []; - for (let index = 0; index < 20; index++) { - formsArray.push(formData); - } - spyOn(paginatorService, 'getWithOffset').and.returnValue(of(formsArray)); - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'forms'; + it('should emit go back', async () => { + spyOn(component.back, 'emit').and.callThrough(); fixtureHost.detectChanges(); await fixtureHost.whenStable(); - const paginatorContainer = fixtureHost.debugElement.query(By.css('p-paginator')); - expect(paginatorContainer).toBeTruthy(); - paginatorContainer.componentInstance.onPageChange.emit({ first: 15 }); - - fixtureHost.detectChanges(); - await fixtureHost.whenStable(); + const filterComp = fixtureHost.debugElement.query(By.css('dot-palette-input-filter')); + filterComp.componentInstance.goBack.emit(); - expect(paginatorService.getWithOffset).toHaveBeenCalledWith(15); + expect(filterComp.componentInstance.goBackBtn).toBe(true); + expect(component.back.emit).toHaveBeenCalled(); }); it('should set Dragged ContentType on dragStart', async () => { - spyOn(paginatorService, 'getWithOffset').and.returnValue(of([formData])); - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'forms'; - + componentHost.items = [contentletProductDataMock] as unknown as DotCMSContentlet[]; + componentHost.loading = false; + componentHost.totalRecords = 10; fixtureHost.detectChanges(); await fixtureHost.whenStable(); const content = fixtureHost.debugElement.query(By.css('[data-testId="paletteItem"]')); - content.triggerEventHandler('dragstart', productData); + content.triggerEventHandler('dragstart', contentletProductDataMock); expect(dotContentletEditorService.setDraggedContentType).toHaveBeenCalledOnceWith( - (formData) as DotCMSContentlet - ); - }); - - it('should go back to show Content Type components', async () => { - spyOn(paginatorESService, 'get').and.returnValue( - of({ - contentTook: 0, - jsonObjectView: { contentlets: [productData] }, - queryTook: 1, - resultsSize: 2 - }) - ); - spyOn(de.componentInstance.back, 'emit').and.callThrough(); - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'Product'; - - fixtureHost.detectChanges(); - await fixtureHost.whenStable(); - - const filterComp = fixtureHost.debugElement.query(By.css('dot-palette-input-filter')); - filterComp.componentInstance.goBack.emit(); - - expect(filterComp.componentInstance.goBackBtn).toBe(true); - expect(de.componentInstance.items).toEqual(null); - expect(de.componentInstance.filter).toEqual(''); - expect(de.componentInstance.back.emit).toHaveBeenCalled(); - }); - - it('should filter Product items on search via DotESContent', async () => { - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'Product'; - spyOn(paginatorESService, 'get').and.returnValue( - of({ - contentTook: 0, - jsonObjectView: { contentlets: [] }, - queryTook: 1, - resultsSize: 2 - }) + (contentletProductDataMock) as DotCMSContentlet ); - - fixtureHost.detectChanges(); - await fixtureHost.whenStable(); - - const filterComp = fixtureHost.debugElement.query(By.css('dot-palette-input-filter')); - filterComp.componentInstance.filter.emit('test'); - - fixtureHost.detectChanges(); - - expect(paginatorESService.get).toHaveBeenCalledWith({ - itemsPerPage: 25, - lang: '1', - filter: 'test', - offset: '0', - query: `+contentType: ${productData.contentType}` - }); }); - it('should filter Forms items on search via PaginatorService', async () => { - componentHost.languageId = '1'; - componentHost.contentTypeVariable = 'forms'; - spyOn(paginatorService, 'getWithOffset').and.returnValue(of([])); - + it('should filter Product item', async () => { + spyOn(component.filter, 'emit').and.callThrough(); fixtureHost.detectChanges(); await fixtureHost.whenStable(); @@ -378,8 +227,6 @@ describe('DotPaletteContentletsComponent', () => { fixtureHost.detectChanges(); - expect(paginatorService.searchParam).toBe('variable'); - expect(paginatorService.filter).toBe('test'); - expect(paginatorService.getWithOffset).toHaveBeenCalledWith(0); + expect(component.filter.emit).toHaveBeenCalledWith('test'); }); }); diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component.ts index 6ce3161db7..52df7009a9 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component.ts @@ -1,8 +1,6 @@ import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core'; import { DotContentletEditorService } from '@dotcms/app/view/components/dot-contentlet-editor/services/dot-contentlet-editor.service'; import { DotCMSContentlet } from '@dotcms/dotcms-models'; -import { DotESContentService } from '@services/dot-es-content/dot-es-content.service'; -import { PaginatorService } from '@services/paginator'; import { LazyLoadEvent } from 'primeng/api'; import { DotPaletteInputFilterComponent } from '../dot-palette-input-filter/dot-palette-input-filter.component'; @@ -24,11 +22,7 @@ export class DotPaletteContentletsComponent { @ViewChild('inputFilter') inputFilter: DotPaletteInputFilterComponent; - constructor( - public paginatorESService: DotESContentService, - public paginationService: PaginatorService, - private dotContentletEditorService: DotContentletEditorService - ) {} + constructor(private dotContentletEditorService: DotContentletEditorService) {} /** * Loads data with a specific page diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.module.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.module.ts index 4b0ebba746..354c2c92f7 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.module.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.module.ts @@ -2,10 +2,9 @@ import { NgModule } from '@angular/core'; import { DotPaletteContentletsComponent } from '@dotcms/app/portlets/dot-edit-page/components/dot-palette/dot-palette-contentlets/dot-palette-contentlets.component'; import { CommonModule } from '@angular/common'; import { DotPipesModule } from '@pipes/dot-pipes.module'; -import {PaginatorModule} from 'primeng/paginator'; +import { PaginatorModule } from 'primeng/paginator'; import { DotSpinnerModule } from '@dotcms/ui'; import { DotPaletteInputFilterModule } from '../dot-palette-input-filter/dot-palette-input-filter.module'; -import { DotESContentService } from '@services/dot-es-content/dot-es-content.service'; @NgModule({ imports: [ @@ -16,7 +15,6 @@ import { DotESContentService } from '@services/dot-es-content/dot-es-content.ser DotSpinnerModule ], declarations: [DotPaletteContentletsComponent], - exports: [DotPaletteContentletsComponent], - providers: [DotESContentService] + exports: [DotPaletteContentletsComponent] }) export class DotPaletteContentletsModule {} diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette.component.spec.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette.component.spec.ts index 007256c9a4..726d5d07bb 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette.component.spec.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette.component.spec.ts @@ -7,6 +7,12 @@ import { dotcmsContentTypeBasicMock } from '@dotcms/app/test/dot-content-types.m import { HttpClientTestingModule } from '@angular/common/http/testing'; import { CoreWebService, CoreWebServiceMock } from '@dotcms/dotcms-js'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { Observable, of } from 'rxjs'; +import { DotESContentService } from '@dotcms/app/api/services/dot-es-content/dot-es-content.service'; +import { PaginatorService } from '@dotcms/app/api/services/paginator'; +import { DotPaletteStore, LoadingState } from './store/dot-palette.store'; +import { take } from 'rxjs/operators'; +import { contentletProductDataMock } from './dot-palette-contentlets/dot-palette-contentlets.component.spec'; @Component({ selector: 'dot-palette-content-type', @@ -24,10 +30,12 @@ export class DotPaletteContentTypeMockComponent { template: '' }) export class DotPaletteContentletsMockComponent { - @Input() contentTypeVariable: string; - @Input() languageId: string; + @Input() items: string; + @Input() loading: boolean; + @Input() totalRecords: number; @Output() back = new EventEmitter(); - + @Output() filter = new EventEmitter(); + @Output() paginate = new EventEmitter(); focusInputFilter() {} } @@ -46,10 +54,63 @@ const itemMock = { system: false }; +@Injectable() +class MockESPaginatorService { + paginationPerPage = 15; + totalRecords = 20; + + public get(): Observable { + return null; + } +} + +@Injectable() +class MockPaginatorService { + url: string; + paginationPerPage = 10; + maxLinksPage = 5; + sortField: string; + sortOrder: string; + totalRecords = 40; + + setExtraParams(): void {} + + public getWithOffset(): Observable { + return null; + } +} + +const storeMock = jasmine.createSpyObj( + 'DotPaletteStore', + [ + 'getContentletsData', + 'filter', + 'languageId', + 'viewContentlet', + 'setLoading', + 'setLoaded', + 'loadContentTypes', + 'filterContentlets', + 'loadContentlets' + ], + { + vm$: of({ + contentlets: [contentletProductDataMock], + contentTypes: [itemMock], + filter: '', + languageId: '1', + totalRecords: 20, + viewContentlet: 'contentlet:out', + callState: LoadingState.LOADED + }) + } +); + describe('DotPaletteComponent', () => { let comp: DotPaletteComponent; let fixture: ComponentFixture; let de: DebugElement; + let store: DotPaletteStore; beforeEach(() => { TestBed.configureTestingModule({ @@ -59,48 +120,79 @@ describe('DotPaletteComponent', () => { DotPaletteContentTypeMockComponent ], imports: [HttpClientTestingModule, NoopAnimationsModule], - providers: [{ provide: CoreWebService, useClass: CoreWebServiceMock }] + providers: [ + { provide: CoreWebService, useClass: CoreWebServiceMock }, + { provide: PaginatorService, useClass: MockPaginatorService }, + { provide: DotESContentService, useClass: MockESPaginatorService } + ] }); + TestBed.overrideProvider(DotPaletteStore, { useValue: storeMock }); + store = TestBed.inject(DotPaletteStore); fixture = TestBed.createComponent(DotPaletteComponent); de = fixture.debugElement; comp = fixture.componentInstance; + comp.items = [itemMock]; + fixture.detectChanges(); }); it('should dot-palette-content-type have items assigned', () => { - comp.items = [itemMock]; - fixture.detectChanges(); const contentTypeComp = fixture.debugElement.query(By.css('dot-palette-content-type')); expect(contentTypeComp.componentInstance.items).toEqual([itemMock]); }); - it('should change view to contentlets and set Content Type Variable on contentlets palette view', async () => { + it('should change view to contentlets and set viewContentlet Variable on contentlets palette view', async () => { + const contentContentletsComp = fixture.debugElement.query( + By.css('dot-palette-contentlets') + ); const contentTypeComp = fixture.debugElement.query(By.css('dot-palette-content-type')); contentTypeComp.triggerEventHandler('selected', 'Blog'); + fixture.detectChanges(); await fixture.whenStable(); + + const wrapper = fixture.debugElement.query(By.css('[data-testid="wrapper"]')); + expect(wrapper.nativeElement.style.transform).toEqual('translateX(0%)'); + expect(store.viewContentlet).toHaveBeenCalledWith('contentlet:in'); + expect(store.filter).toHaveBeenCalledWith(''); + expect(store.loadContentlets).toHaveBeenCalledWith('Blog'); + expect(contentContentletsComp.componentInstance.totalRecords).toBe(20); + expect(contentContentletsComp.componentInstance.items).toEqual([contentletProductDataMock]); + }); + + it('should change view to content type and unset viewContentlet Variable on contentlets palette view', async () => { const contentContentletsComp = fixture.debugElement.query( By.css('dot-palette-contentlets') ); + contentContentletsComp.triggerEventHandler('back', ''); - const wrapper = fixture.debugElement.query(By.css('[data-testid="wrapper"]')); - expect(wrapper.nativeElement.style.transform).toEqual('translateX(-100%)'); + fixture.detectChanges(); + await fixture.whenStable(); - expect(contentContentletsComp.componentInstance.contentTypeVariable).toEqual('Blog'); + expect(store.viewContentlet).toHaveBeenCalledWith('contentlet:out'); }); - it('should change view to content type and unset Content Type Variable on contentlets palette view', async () => { + it('should set value on store on filtering event', async () => { const contentContentletsComp = fixture.debugElement.query( By.css('dot-palette-contentlets') ); - contentContentletsComp.triggerEventHandler('back', ''); - comp.languageId = '2'; + contentContentletsComp.triggerEventHandler('filter', 'test'); + fixture.detectChanges(); await fixture.whenStable(); - const wrapper = fixture.debugElement.query(By.css('[data-testid="wrapper"]')); - expect(wrapper.nativeElement.style.transform).toEqual('translateX(0%)'); + expect(store.filterContentlets).toHaveBeenCalledWith('test'); + }); + + it('should set value on store on paginate event', async () => { + const contentContentletsComp = fixture.debugElement.query( + By.css('dot-palette-contentlets') + ); + contentContentletsComp.triggerEventHandler('paginate', { first: 20 }); + + fixture.detectChanges(); + await fixture.whenStable(); - expect(contentContentletsComp.componentInstance.languageId).toEqual('2'); + expect(store.getContentletsData).toHaveBeenCalledWith({ first: 20 }); }); }); diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette.component.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette.component.ts index 41993982a9..db5ad021d7 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette.component.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/dot-palette.component.ts @@ -37,7 +37,6 @@ export class DotPaletteComponent implements OnInit { } } @Input() languageId: string; - contentTypeVariable = ''; vm$: Observable = this.store.vm$; @ViewChild('contentlets') contentlets: DotPaletteContentletsComponent; @@ -50,13 +49,12 @@ export class DotPaletteComponent implements OnInit { } /** - * Sets value on contentTypeVariable variable to show/hide components on the UI + * Sets value on store variables to show/hide components on the UI * * @param string [variableName] * @memberof DotPaletteContentletsComponent */ switchView(variableName?: string): void { - this.contentTypeVariable = variableName ? variableName : ''; const viewContentlet = variableName ? 'contentlet:in' : 'contentlet:out'; this.store.viewContentlet(viewContentlet); diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/store/dot-palette.store.spec.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/store/dot-palette.store.spec.ts new file mode 100644 index 0000000000..703f94b7ae --- /dev/null +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/store/dot-palette.store.spec.ts @@ -0,0 +1,165 @@ +import { TestBed } from '@angular/core/testing'; +import { Observable, of } from 'rxjs'; +import { DotPaletteStore, LoadingState } from './dot-palette.store'; +import { Injectable } from '@angular/core'; +import { DotESContentService } from '@dotcms/app/api/services/dot-es-content/dot-es-content.service'; +import { PaginatorService } from '@dotcms/app/api/services/paginator'; +import { contentTypeDataMock } from '../dot-palette-content-type/dot-palette-content-type.component.spec'; +import { DotCMSContentlet, DotCMSContentType } from '@dotcms/dotcms-models'; +import { + contentletFormDataMock, + contentletProductDataMock +} from '../dot-palette-contentlets/dot-palette-contentlets.component.spec'; + +@Injectable() +class MockPaginatorService { + url: string; + paginationPerPage = 10; + maxLinksPage = 5; + sortField: string; + sortOrder: string; + totalRecords = 40; + + setExtraParams(): void {} + + public getWithOffset(): Observable { + return null; + } +} + +@Injectable() +class MockESPaginatorService { + paginationPerPage = 15; + totalRecords = 20; + + public get(): Observable { + return null; + } +} + +describe('DotPaletteStore', () => { + let dotPaletteStore: DotPaletteStore; + let paginatorService: PaginatorService; + let dotESContentService: DotESContentService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + DotPaletteStore, + { provide: PaginatorService, useClass: MockPaginatorService }, + { provide: DotESContentService, useClass: MockESPaginatorService } + ] + }); + dotPaletteStore = TestBed.inject(DotPaletteStore); + paginatorService = TestBed.inject(PaginatorService); + dotESContentService = TestBed.inject(DotESContentService); + }); + + it('should update filter', () => { + dotPaletteStore.filter('test'); + dotPaletteStore.state$.subscribe((data) => { + expect(data.filter).toEqual('test'); + }); + }); + + it('should update languageId', () => { + dotPaletteStore.languageId('1'); + dotPaletteStore.state$.subscribe((data) => { + expect(data.languageId).toEqual('1'); + }); + }); + + it('should update viewContentlet', () => { + dotPaletteStore.viewContentlet('in'); + dotPaletteStore.state$.subscribe((data) => { + expect(data.viewContentlet).toEqual('in'); + }); + }); + + it('should update setLoading', () => { + dotPaletteStore.setLoading(); + dotPaletteStore.state$.subscribe((data) => { + expect(data.callState).toEqual(LoadingState.LOADING); + }); + }); + + it('should update setLoaded', () => { + dotPaletteStore.setLoaded(); + dotPaletteStore.state$.subscribe((data) => { + expect(data.callState).toEqual(LoadingState.LOADED); + }); + }); + + it('should load contentTypes to store', (done) => { + dotPaletteStore.loadContentTypes(contentTypeDataMock as DotCMSContentType[]); + dotPaletteStore.vm$.subscribe((data) => { + expect(data.contentTypes).toEqual(contentTypeDataMock as DotCMSContentType[]); + done(); + }); + }); + + it('should load Forms contentlets to store', (done) => { + spyOn(paginatorService, 'getWithOffset').and.returnValue(of([contentletFormDataMock])); + dotPaletteStore.loadContentlets('forms'); + + expect(paginatorService.url).toBe('v1/contenttype'); + expect(paginatorService.paginationPerPage).toBe(25); + expect(paginatorService.sortField).toBe('modDate'); + expect(paginatorService.sortOrder).toBe(1); + + dotPaletteStore.vm$.subscribe((data) => { + expect(data.contentlets).toEqual([ + contentletFormDataMock + ] as unknown as DotCMSContentType[]); + expect(data.filter).toEqual(''); + expect(data.loading).toEqual(false); + expect(data.totalRecords).toEqual(paginatorService.totalRecords); + done(); + }); + }); + + it('should load Product contentlets to store', (done) => { + spyOn(dotESContentService, 'get').and.returnValue( + of({ + contentTook: 0, + jsonObjectView: { contentlets: [contentletProductDataMock] }, + queryTook: 1, + resultsSize: 20 + }) + ); + dotPaletteStore.loadContentlets('product'); + + dotPaletteStore.vm$.subscribe((data) => { + expect(dotESContentService.get).toHaveBeenCalledWith({ + itemsPerPage: 25, + lang: '1', + filter: '', + offset: '0', + query: '+contentType: product' + }); + expect(data.contentlets).toEqual([ + contentletProductDataMock + ] as unknown as DotCMSContentlet[]); + expect(data.filter).toEqual(''); + expect(data.loading).toEqual(false); + expect(data.totalRecords).toEqual(20); + done(); + }); + }); + + it('should set filter value in store', (done) => { + spyOn(dotESContentService, 'get').and.returnValue( + of({ + contentTook: 0, + jsonObjectView: { contentlets: [contentletProductDataMock] }, + queryTook: 1, + resultsSize: 20 + }) + ); + dotPaletteStore.filterContentlets('Prod'); + dotPaletteStore.vm$.subscribe((data) => { + expect(data.filter).toEqual('Prod'); + done(); + }); + }); +}); diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/store/dot-palette.store.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/store/dot-palette.store.ts index f4ee94fa18..069e632674 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/store/dot-palette.store.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/components/dot-palette/store/dot-palette.store.ts @@ -204,7 +204,6 @@ export class DotPaletteStore extends ComponentStore { this.paginationService.searchParam = 'variable'; this.paginationService.filter = value; } - this.getContentletsData({ first: 0 }); }) ); @@ -215,7 +214,6 @@ export class DotPaletteStore extends ComponentStore { map((contentTypeVariable: string) => { this.contentTypeVarName = contentTypeVariable; this.isFormContentType = contentTypeVariable === 'forms'; - if (this.isFormContentType) { this.paginationService.url = `v1/contenttype`; this.paginationService.paginationPerPage = this.itemsPerPage; diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.component.spec.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.component.spec.ts index 7a347697b6..173d924eed 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.component.spec.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.component.spec.ts @@ -3,7 +3,14 @@ import { of as observableOf, of, throwError } from 'rxjs'; import { ActivatedRoute } from '@angular/router'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { By } from '@angular/platform-browser'; -import { ComponentFixture, tick, fakeAsync, TestBed, discardPeriodicTasks, flush } from '@angular/core/testing'; +import { + ComponentFixture, + tick, + fakeAsync, + TestBed, + discardPeriodicTasks, + flush +} from '@angular/core/testing'; import { Component, DebugElement, @@ -87,6 +94,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { DotGenerateSecurePasswordService } from '@services/dot-generate-secure-password/dot-generate-secure-password.service'; import { DotPropertiesService } from '@services/dot-properties/dot-properties.service'; import { PageModelChangeEventType } from './services/dot-edit-content-html/models'; +import { DotESContentService } from '@dotcms/app/api/services/dot-es-content/dot-es-content.service'; const responseData: DotCMSContentType[] = [ { @@ -173,6 +181,15 @@ export class MockDotFormSelectorComponent { @Output() close = new EventEmitter(); } +@Component({ + selector: 'dot-palette', + template: '' +}) +export class MockDotPaletteComponent { + @Input() languageId = '1'; + @Input() items: any[]; +} + @Injectable() class MockDotContentTypeService { getContentTypes = jasmine @@ -235,6 +252,7 @@ describe('DotEditContentComponent', () => { MockDotWhatsChangedComponent, MockDotFormSelectorComponent, MockDotIconComponent, + MockDotPaletteComponent, HostTestComponent, MockGlobalMessageComponent ], @@ -250,7 +268,6 @@ describe('DotEditContentComponent', () => { DotEditPageWorkflowsActionsModule, DotOverlayMaskModule, DotWizardModule, - DotPaletteModule, RouterTestingModule.withRoutes([ { component: DotEditContentComponent, @@ -273,6 +290,7 @@ describe('DotEditContentComponent', () => { DotGenerateSecurePasswordService, DotCustomEventHandlerService, DotPropertiesService, + DotESContentService, { provide: DotContentTypeService, useClass: MockDotContentTypeService }, { provide: LoginService, @@ -658,7 +676,9 @@ describe('DotEditContentComponent', () => { jasmine.any(ElementRef) ); expect(dotEditContentHtmlService.initEditMode).not.toHaveBeenCalled(); - expect(dotEditContentHtmlService.setCurrentPage).toHaveBeenCalledWith(mockRenderedPageState.page); + expect(dotEditContentHtmlService.setCurrentPage).toHaveBeenCalledWith( + mockRenderedPageState.page + ); })); it('should render in edit mode', fakeAsync(() => { @@ -685,7 +705,9 @@ describe('DotEditContentComponent', () => { jasmine.any(ElementRef) ); expect(dotEditContentHtmlService.renderPage).not.toHaveBeenCalled(); - expect(dotEditContentHtmlService.setCurrentPage).toHaveBeenCalledWith(state.page); + expect(dotEditContentHtmlService.setCurrentPage).toHaveBeenCalledWith( + state.page + ); })); it('should show/hide content palette in edit mode with correct content', fakeAsync(() => { @@ -718,13 +740,17 @@ describe('DotEditContentComponent', () => { ); const classList = contentPaletteWrapper.nativeElement.classList; expect(contentPalette.items).toEqual(responseData.slice(0, 3)); - expect(parseInt(contentPalette.languageId)).toEqual(mockDotRenderedPage().page.languageId); + expect(parseInt(contentPalette.languageId)).toEqual( + mockDotRenderedPage().page.languageId + ); expect(classList.contains('editMode')).toEqual(true); paletteController.triggerEventHandler('click', ''); fixture.detectChanges(); expect(classList.contains('collapsed')).toEqual(true); - expect(dotEditContentHtmlService.setCurrentPage).toHaveBeenCalledWith(state.page); + expect(dotEditContentHtmlService.setCurrentPage).toHaveBeenCalledWith( + state.page + ); })); it('should not display palette when is not enterprise', fakeAsync(() => { @@ -750,7 +776,9 @@ describe('DotEditContentComponent', () => { fixture.detectChanges(); const contentPaletteWrapper = de.query(By.css('.dot-edit-content__palette')); expect(contentPaletteWrapper).toBeNull(); - expect(dotEditContentHtmlService.setCurrentPage).toHaveBeenCalledWith(state.page); + expect(dotEditContentHtmlService.setCurrentPage).toHaveBeenCalledWith( + state.page + ); })); it('should reload the page because of EMA', fakeAsync(() => { @@ -784,7 +812,7 @@ describe('DotEditContentComponent', () => { expect(dotPageStateService.reload).toHaveBeenCalledTimes(1); flush(); - })) + })); it('should NOT reload the page', fakeAsync(() => { spyOn(dotLicenseService, 'isEnterprise').and.returnValue(of(false)); @@ -795,7 +823,7 @@ describe('DotEditContentComponent', () => { ...mockDotRenderedPage(), page: { ...mockDotRenderedPage().page, - lockedBy: null, + lockedBy: null }, viewAs: { mode: DotPageMode.EDIT, @@ -820,7 +848,7 @@ describe('DotEditContentComponent', () => { expect(dotPageStateService.reload).toHaveBeenCalledTimes(0); flush(); - })) + })); }); describe('events', () => { @@ -847,8 +875,6 @@ describe('DotEditContentComponent', () => { expect(dotUiColorsService.setColors).toHaveBeenCalled(); })); - - describe('custom', () => { it('should handle remote-render-edit', fakeAsync(() => { detectChangesForIframeRender(fixture); @@ -909,10 +935,9 @@ describe('DotEditContentComponent', () => { })); it('should handle load-edit-mode-page to internal navigation', fakeAsync(() => { - spyOn( - dotPageStateService, - 'setInternalNavigationState' - ).and.callFake(() => {}); + spyOn(dotPageStateService, 'setInternalNavigationState').and.callFake( + () => {} + ); detectChangesForIframeRender(fixture); diff --git a/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.module.ts b/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.module.ts index dca4098e7a..87211d0dce 100644 --- a/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.module.ts +++ b/apps/dotcms-ui/src/app/portlets/dot-edit-page/content/dot-edit-content.module.ts @@ -32,6 +32,7 @@ import { DotWorkflowActionsFireService } from '@services/dot-workflow-actions-fi import { DotLicenseService } from '@services/dot-license/dot-license.service'; import { DotPaletteModule } from '@dotcms/app/portlets/dot-edit-page/components/dot-palette/dot-palette.module'; import { DotIconModule } from '@dotcms/ui'; +import { DotESContentService } from '@dotcms/app/api/services/dot-es-content/dot-es-content.service'; const routes: Routes = [ { @@ -71,6 +72,7 @@ const routes: Routes = [ DotEditContentHtmlService, DotEditContentToolbarHtmlService, DotEditPageService, + DotESContentService, DotPageRenderService, DotWorkflowService, IframeOverlayService,