diff --git a/README.md b/README.md index 49d46e7e9..eb1ed3067 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,7 @@ You should also add `Angular-Slickgrid` as an allowed CommonJS dependency to you ``` ### Fully Tested with [Jest](https://jestjs.io/) (Unit Tests) - [Cypress](https://www.cypress.io/) (E2E Tests) -Angular-Slickgrid reached **100%** Unit Test Coverage, we are talking about +10,000 lines of code (+2,700 unit tests) that are now fully tested with [Jest](https://jestjs.io/). There are also +350 Cypress E2E tests to cover most UI functionalities on nearly all Examples of Angular-Slickgrid. +Angular-Slickgrid reached **100%** Unit Test Coverage, we are talking about +10,000 lines of code (+2,800 unit tests) that are now fully tested with [Jest](https://jestjs.io/). On the UI side, all Angular-Slickgrid Examples are tested with [Cypress](https://www.cypress.io/), there are over 400+ Cypress E2E tests. ## Installation Refer to the **[Wiki - HOWTO Step by Step](https://github.com/ghiscoding/angular-slickgrid/wiki/HOWTO---Step-by-Step)** and/or clone the [Angular-Slickgrid Demos](https://github.com/ghiscoding/angular-slickgrid-demos) repository. Please don't open any issue unless you have followed these steps (from the Wiki), and if any of the steps are incorrect or confusing, then please let me know. diff --git a/src/app/examples/grid-tabs.component.html b/src/app/examples/grid-tabs.component.html index cb1af39ae..4cb4499f9 100644 --- a/src/app/examples/grid-tabs.component.html +++ b/src/app/examples/grid-tabs.component.html @@ -4,21 +4,21 @@

{{title}}

- +

Grid 1 - Load Local Data

+ [columnDefinitions]="columnDefinitions1" + [gridOptions]="gridOptions1" + [dataset]="dataset1">
- +

Grid 2 - Load a JSON dataset through Http-Client

+ [columnDefinitions]="columnDefinitions2" + [gridOptions]="gridOptions2" + [dataset]="dataset2" + (onAngularGridCreated)="angularGrid2Ready($event)">
diff --git a/src/app/examples/swt-common-grid-test.component.html b/src/app/examples/swt-common-grid-test.component.html index dcd7310c5..6aee42912 100644 --- a/src/app/examples/swt-common-grid-test.component.html +++ b/src/app/examples/swt-common-grid-test.component.html @@ -1,13 +1,16 @@ -
-
-
- Custom Pagination URL: -
-
-
- - +
+

+
+ +
+
+ Custom Pagination URL: +
+
+
+ +
diff --git a/src/app/examples/swt-common-grid-test.component.ts b/src/app/examples/swt-common-grid-test.component.ts index 64ddf6116..a66d1111c 100644 --- a/src/app/examples/swt-common-grid-test.component.ts +++ b/src/app/examples/swt-common-grid-test.component.ts @@ -1,10 +1,16 @@ -import { Component, OnInit, ViewChild, ModuleWithProviders, NgModule, ViewContainerRef, ComponentFactoryResolver, OnChanges, AfterContentInit, AfterViewChecked, ElementRef, Renderer, EventEmitter, - Output, AfterViewInit, Injectable} from '@angular/core'; +import { + AfterViewInit, + Component, + ComponentFactoryResolver, + Injectable, + OnInit, + ViewChild, + ViewContainerRef, +} from '@angular/core'; import { HttpClient } from '@angular/common/http'; -import { Routes, RouterModule } from '@angular/router'; -import {SwtCommonGridComponent} from './swt-common-grid.component'; -import {SwtCommonGridPaginationComponent} from './swt-common-grid-pagination.component'; +import { SwtCommonGridComponent } from './swt-common-grid.component'; +import { SwtCommonGridPaginationComponent } from './swt-common-grid-pagination.component'; import { FilterChangedArgs, PaginationChangedArgs, SortChangedArgs } from '../modules/angular-slickgrid'; import { Logger } from './swt-logger.service'; @@ -19,195 +25,197 @@ import { Logger } from './swt-logger.service'; }) @Injectable() export class SwtCommonGridTestComponent implements OnInit, AfterViewInit { - componentFactory: any; - testurl = 'http://127.0.0.1:8080/grid!display.do?'; - currentUrl = this.testurl; + title = 'Example 13: Custom Backend Server Pagination'; + subTitle = `A simple component to show that it is possible to create a custom Backend Service for any other backend querying, the example below is for Oracle.`; + componentFactory: any; + testurl = 'http://127.0.0.1:8080/grid!display.do?'; + currentUrl = this.testurl; - @ViewChild('commonGrid1', { static: true }) commonGrid: SwtCommonGridComponent; - @ViewChild('commonGridPag1', { static: true }) commonGridPag: SwtCommonGridPaginationComponent; + @ViewChild('commonGrid1', { static: true }) commonGrid: SwtCommonGridComponent; + @ViewChild('commonGridPag1', { static: true }) commonGridPag: SwtCommonGridPaginationComponent; - private logger: Logger = null; + private logger: Logger = null; - constructor(private httpClient: HttpClient, - private viewContainerRef: ViewContainerRef, - private componentFactoryResolver: ComponentFactoryResolver) { - this.logger = new Logger('test', null); + constructor(private httpClient: HttpClient, + private viewContainerRef: ViewContainerRef, + private componentFactoryResolver: ComponentFactoryResolver) { + this.logger = new Logger('test', null); - } - - ngOnInit() { - // Link pagination component into the current Grid - if (this.commonGridPag) { - this.commonGrid.paginationComponent = this.commonGridPag; - } - - } - - ngAfterViewInit() { - this.logger.info('method [ngAfterViewInit] - START'); - - setTimeout(() => { - // Init datagrid example: - this.commonGridPag.processing = true; - - // Real HTTP call - this.currentUrl = this.testurl + '¤tPage=1'; - /* - this.httpClient.get(this.currentUrl).subscribe( - (data: any) => { - this.commonGrid.CustomGrid(data.suspectManagement.grid.metadata); - this.commonGrid.gridData = data.suspectManagement.grid.rows; - this.commonGridPag.pageCount = data.suspectManagement.singletons.maxpage; - this.commonGridPag.processing = false; - } - ); - */ - this.commonGrid.CustomGrid(data_sample.pagination_samples.grid.metadata); - this.commonGrid.gridData = data_sample.pagination_samples.grid.rows; - this.commonGridPag.pageCount = data_sample.pagination_samples.grid.rows.maxpage; - - this.commonGridPag.processing = false; - }, 0); - this.logger.info('method [ngAfterViewInit] - END'); - } + } - filterChanged(event: FilterChangedArgs) { - this.commonGridPag.processing = true; - this.updateGridData(); + ngOnInit() { + // Link pagination component into the current Grid + if (this.commonGridPag) { + this.commonGrid.paginationComponent = this.commonGridPag; } - paginationChanged(event: PaginationChangedArgs) { - this.commonGridPag.processing = true; - this.updateGridData(); - } - - sortChanged(event: SortChangedArgs) { - this.commonGridPag.processing = true; - this.updateGridData(); - } - - - updateGridData() { - this.currentUrl = this.testurl + '¤tPage=' + this.commonGrid.currentPage + '&selectedSort=' + this.commonGrid.sortedGridColumn + '&selectedFilter=' + this.commonGrid.filteredGridColumns; - // Real HTTP call - /*this.httpClient.get(this.currentUrl).subscribe( - (data: any) => { - this.commonGrid.gridData = data.suspectManagement?data.suspectManagement.grid.rows:[]; - this.commonGridPag.pageCount = data.suspectManagement?data.suspectManagement.singletons.maxpage:1; - this.commonGridPag.processing = false; - } - );*/ - setTimeout(() => { - this.commonGrid.gridData = data_sample.pagination_samples.grid.rows; - this.commonGridPag.pageCount = data_sample.pagination_samples.grid.rows.maxpage; - }, 750); - } + } + + ngAfterViewInit() { + this.logger.info('method [ngAfterViewInit] - START'); + + setTimeout(() => { + // Init datagrid example: + this.commonGridPag.processing = true; + + // Real HTTP call + this.currentUrl = this.testurl + '¤tPage=1'; + /* + this.httpClient.get(this.currentUrl).subscribe( + (data: any) => { + this.commonGrid.CustomGrid(data.suspectManagement.grid.metadata); + this.commonGrid.gridData = data.suspectManagement.grid.rows; + this.commonGridPag.pageCount = data.suspectManagement.singletons.maxpage; + this.commonGridPag.processing = false; + } + ); + */ + this.commonGrid.CustomGrid(data_sample.pagination_samples.grid.metadata); + this.commonGrid.gridData = data_sample.pagination_samples.grid.rows; + this.commonGridPag.pageCount = data_sample.pagination_samples.grid.rows.maxpage; + + this.commonGridPag.processing = false; + }, 0); + this.logger.info('method [ngAfterViewInit] - END'); + } + + filterChanged(event: FilterChangedArgs) { + this.commonGridPag.processing = true; + this.updateGridData(); + } + + paginationChanged(event: PaginationChangedArgs) { + this.commonGridPag.processing = true; + this.updateGridData(); + } + + sortChanged(event: SortChangedArgs) { + this.commonGridPag.processing = true; + this.updateGridData(); + } + + + updateGridData() { + this.currentUrl = this.testurl + '¤tPage=' + this.commonGrid.currentPage + '&selectedSort=' + this.commonGrid.sortedGridColumn + '&selectedFilter=' + this.commonGrid.filteredGridColumns; + // Real HTTP call + /*this.httpClient.get(this.currentUrl).subscribe( + (data: any) => { + this.commonGrid.gridData = data.suspectManagement?data.suspectManagement.grid.rows:[]; + this.commonGridPag.pageCount = data.suspectManagement?data.suspectManagement.singletons.maxpage:1; + this.commonGridPag.processing = false; + } + );*/ + setTimeout(() => { + this.commonGrid.gridData = data_sample.pagination_samples.grid.rows; + this.commonGridPag.pageCount = data_sample.pagination_samples.grid.rows.maxpage; + }, 750); + } } export const data_sample = { - 'pagination_samples': { - 'grid': { - 'metadata': { - 'columns': { - 'column': [{ - 'sort': true, - 'filterable': false, - 'width': 60, - 'dataelement': 'hasNote', - 'heading': 'Note' - }, - { - 'sort': true, - 'filterable': true, - 'width': 125, - 'dataelement': 'status', - 'heading': 'Status' - }, - { - 'sort': true, - 'visible': true, - 'filterable': true, - 'width': 125, - 'dataelement': 'currency', - 'heading': 'Currency' - }, - { - 'sort': true, - 'visible': true, - 'filterable': true, - 'width': 125, - 'dataelement': 'amount', - 'heading': 'Amount' - }, - { - 'sort': true, - 'visible': true, - 'filterable': true, - 'width': 125, - 'dataelement': 'inputDate', - 'heading': 'Input Date' - }, - { - 'sort': true, - 'visible': true, - 'filterable': true, - 'width': 125, - 'dataelement': 'inputTime', - 'heading': 'Input Time' - }] - } - }, - 'rows': { - 'row': [{ - 'currency': { - 'content': 'EUR' - }, - 'amount': { - 'content': '2 203 677,000' - }, - 'startTime': { - 'content': '06/19/2017 11:52:51' - }, - 'inputDate': { - 'content': '06/19/2017' - }, - 'status': { - 'content': 'New' - }, - 'inputTime': { - 'content': '11:52:51' - }, - 'hasNote': { - 'content': 'False' - } - }, - { - 'currency': { - 'content': 'USD' - }, - 'amount': { - 'content': '6 203 677,000' - }, - 'startTime': { - 'content': '06/28/2017 10:42:00' - }, - 'inputDate': { - 'content': '06/28/2017' - }, - 'status': { - 'content': 'New' - }, - 'inputTime': { - 'content': '10:40:12' - }, - 'hasNote': { - 'content': 'True' - } - } - ], - 'maxpage': 5 - } + 'pagination_samples': { + 'grid': { + 'metadata': { + 'columns': { + 'column': [{ + 'sort': true, + 'filterable': false, + 'width': 60, + 'dataelement': 'hasNote', + 'heading': 'Note' + }, + { + 'sort': true, + 'filterable': true, + 'width': 125, + 'dataelement': 'status', + 'heading': 'Status' + }, + { + 'sort': true, + 'visible': true, + 'filterable': true, + 'width': 125, + 'dataelement': 'currency', + 'heading': 'Currency' + }, + { + 'sort': true, + 'visible': true, + 'filterable': true, + 'width': 125, + 'dataelement': 'amount', + 'heading': 'Amount' + }, + { + 'sort': true, + 'visible': true, + 'filterable': true, + 'width': 125, + 'dataelement': 'inputDate', + 'heading': 'Input Date' + }, + { + 'sort': true, + 'visible': true, + 'filterable': true, + 'width': 125, + 'dataelement': 'inputTime', + 'heading': 'Input Time' + }] + } + }, + 'rows': { + 'row': [{ + 'currency': { + 'content': 'EUR' + }, + 'amount': { + 'content': '2 203 677,000' + }, + 'startTime': { + 'content': '06/19/2017 11:52:51' + }, + 'inputDate': { + 'content': '06/19/2017' + }, + 'status': { + 'content': 'New' + }, + 'inputTime': { + 'content': '11:52:51' + }, + 'hasNote': { + 'content': 'False' + } + }, + { + 'currency': { + 'content': 'USD' + }, + 'amount': { + 'content': '6 203 677,000' + }, + 'startTime': { + 'content': '06/28/2017 10:42:00' + }, + 'inputDate': { + 'content': '06/28/2017' + }, + 'status': { + 'content': 'New' + }, + 'inputTime': { + 'content': '10:40:12' + }, + 'hasNote': { + 'content': 'True' + } } + ], + 'maxpage': 5 + } } + } }; diff --git a/src/app/examples/swt-common-grid.component.ts b/src/app/examples/swt-common-grid.component.ts index b9c775908..19ba20685 100644 --- a/src/app/examples/swt-common-grid.component.ts +++ b/src/app/examples/swt-common-grid.component.ts @@ -25,13 +25,13 @@ const DEFAULT_FILTER_TYPING_DEBOUNCE = 750; selector: 'swt-common-grid', template: ` - `, + [dataset]="dataset"> + `, styles: [` :host ::ng-deep .gridPane{ width: 100%!important; diff --git a/test/cypress/integration/example13.spec.js b/test/cypress/integration/example13.spec.js new file mode 100644 index 000000000..0c23c31da --- /dev/null +++ b/test/cypress/integration/example13.spec.js @@ -0,0 +1,78 @@ +/// + +describe('Example 13 - Custom Backend Server Pagination', () => { + const fullTitles = ['Note', 'Status', 'Currency', 'Amount', 'Input Date', 'Input Time']; + + it('should display Example title', () => { + cy.visit(`${Cypress.config('baseExampleUrl')}/swt`); + cy.get('h2').should('contain', 'Example 13: Custom Backend Server Pagination'); + }); + + it('should have exact column titles in grid', () => { + cy.get('#slickGridContainer-common-grid') + .find('.slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(fullTitles[index])); + }); + + it('should be on 1st page and have default query string', () => { + cy.get('.slick-pagination-nav input') + .invoke('val') + .then(pageNumber => expect(pageNumber).to.eq('1')); + + cy.get('[data-test="query-string"]') + .contains('http://127.0.0.1:8080/grid!display.do?¤tPage=1'); + }); + + it('should go on next page and expect query to be updated accordingly', () => { + cy.get('.icon-seek-next').click(); + + cy.get('.fa-refresh.fa-spin') + .should('be.visible', true); + + cy.get('.slick-pagination-nav input') + .invoke('val') + .then(pageNumber => expect(pageNumber).to.eq('2')); + + cy.get('[data-test="query-string"]') + .contains('http://127.0.0.1:8080/grid!display.do?¤tPage=2&selectedSort=&selectedFilter='); + + cy.get('.fa-refresh.fa-spin') + .should('be.visible', false); + }); + + it('should filter with "New" Status and expect query string to contains the search and the page to be back to Page 1', () => { + cy.get('input.search-filter.filter-status') + .type('New'); + + cy.get('.fa-refresh.fa-spin') + .should('be.visible', true); + + cy.get('[data-test="query-string"]') + .contains('http://127.0.0.1:8080/grid!display.do?¤tPage=1&selectedSort=&selectedFilter=All|New|All|All|All|All|'); + + cy.get('.slick-pagination-nav input') + .invoke('val') + .then(pageNumber => expect(pageNumber).to.eq('1')); + + cy.get('.fa-refresh.fa-spin') + .should('be.visible', false); + }); + + it('should go to last page 5 and expect query to be updated accordingly', () => { + cy.get('.icon-seek-end').click(); + + cy.get('.fa-refresh.fa-spin') + .should('be.visible', true); + + cy.get('.slick-pagination-nav input') + .invoke('val') + .then(pageNumber => expect(pageNumber).to.eq('5')); + + cy.get('[data-test="query-string"]') + .contains('http://127.0.0.1:8080/grid!display.do?¤tPage=5&selectedSort=&selectedFilter=All|New|All|All|All|All|'); + + cy.get('.fa-refresh.fa-spin') + .should('be.visible', false); + }); +}); diff --git a/test/cypress/integration/example24.spec.js b/test/cypress/integration/example24.spec.js new file mode 100644 index 000000000..393ca3298 --- /dev/null +++ b/test/cypress/integration/example24.spec.js @@ -0,0 +1,52 @@ +/// + +describe('Example 24 - Grids in Bootstrap Tabs', () => { + const GRID_ROW_HEIGHT = 35; + const grid1FullTitles = ['Title', 'Duration (days)', '% Complete', 'Start', 'Finish', 'Effort Driven']; + const grid2FullTitles = ['Name', 'Gender', 'Company']; + + it('should display Example title', () => { + cy.visit(`${Cypress.config('baseExampleUrl')}/gridtabs`); + cy.get('h2').should('contain', 'Example 24: Grids in Bootstrap Tabs'); + }); + + it('should have exact column titles in grid', () => { + cy.get('#slickGridContainer-grid1') + .find('.slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(grid1FullTitles[index])); + }); + + it('should have "Task 0" incremented by 1 after each row', () => { + cy.get(`[data-test="javascript-tab"] [style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(0)`).should('contain', 'Task 0'); + cy.get(`[data-test="javascript-tab"] [style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(0)`).should('contain', 'Task 1'); + cy.get(`[data-test="javascript-tab"] [style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(0)`).should('contain', 'Task 2'); + cy.get(`[data-test="javascript-tab"] [style="top:${GRID_ROW_HEIGHT * 3}px"] > .slick-cell:nth(0)`).should('contain', 'Task 3'); + cy.get(`[data-test="javascript-tab"] [style="top:${GRID_ROW_HEIGHT * 4}px"] > .slick-cell:nth(0)`).should('contain', 'Task 4'); + cy.get(`[data-test="javascript-tab"] [style="top:${GRID_ROW_HEIGHT * 5}px"] > .slick-cell:nth(0)`).should('contain', 'Task 5'); + }); + + it('should change open next Tab "Http-Client" and expect a grid with 3 columns', () => { + cy.get('.tab-container') + .contains('Http-Client').click(); + + cy.get('#slickGridContainer-grid2:visible') + .find('.slick-header-columns') + .children() + .each(($child, index) => expect($child.text()).to.eq(grid2FullTitles[index])); + }); + + it('should expect first 3 rows to be an exact match of data provided by the external JSON file', () => { + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(0)`).should('contain', 'Ethel Price'); + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(1)`).should('contain', 'female'); + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 0}px"] > .slick-cell:nth(2)`).should('contain', 'Enersol'); + + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(0)`).should('contain', 'Claudine Neal'); + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(1)`).should('contain', 'female'); + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 1}px"] > .slick-cell:nth(2)`).should('contain', 'Sealoud'); + + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(0)`).should('contain', 'Beryl Rice'); + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(1)`).should('contain', 'female'); + cy.get(`[data-test="http-tab"] [style="top:${GRID_ROW_HEIGHT * 2}px"] > .slick-cell:nth(2)`).should('contain', 'Velity'); + }); +});