Skip to content

Commit

Permalink
feat(ngrid/testing): add test harnesses for ngrid, columns and data r…
Browse files Browse the repository at this point in the history
…ows/cells

This commit add support for common actions when testing nGrid, getting columns and data.
In addition, a restructure was made in the testing infrastructure and project configuration
to start testing the code with these helpers.
  • Loading branch information
shlomiassaf committed Dec 3, 2020
1 parent e064725 commit 19bbba6
Show file tree
Hide file tree
Showing 54 changed files with 1,262 additions and 68 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/gh-pages-v3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ jobs:
- uses: actions/setup-node@v1
with:
node-version: 12
- uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm install
- run: mkdir /home/runner/work/ngrid/ngrid/.cache
- run: node --max-old-space-size=8192 ./node_modules/.bin/ng build --prod --base-href /ngrid/v3/ --deploy-url /ngrid/v3/
Expand Down
89 changes: 80 additions & 9 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@
"apps/ngrid-demo-app/src/google1a0455c97ead32c8.html",
"apps/ngrid-demo-app/src/assets"
],
"styles": ["apps/ngrid-demo-app/src/styles/main.scss"],
"styles": [
"apps/ngrid-demo-app/src/styles/main.scss"
],
"scripts": [
{
"input": "node_modules/document-register-element/build/document-register-element.js"
Expand Down Expand Up @@ -83,6 +85,27 @@
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "apps/ngrid-demo-app/src/__test-runners/karma-test.ts",
"tsConfig": "apps/ngrid-demo-app/tsconfig.spec.karma.json",
"karmaConfig": "apps/ngrid-demo-app/karma.conf.js",
"polyfills": "apps/ngrid-demo-app/src/polyfills.ts",
"styles": [
"apps/ngrid-demo-app/src/__test-runners/main-test.scss"
],
"scripts": [],
"assets": []
},
"configurations": {
"ci": {
"watch": false,
"progress": false,
"browsers": "ChromeHeadlessCI"
}
}
},
"testJest": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "apps/ngrid-demo-app/jest.config.js",
Expand All @@ -95,9 +118,14 @@
"tsConfig": [
"apps/ngrid-demo-app/tsconfig.app.json",
"apps/ngrid-demo-app/tsconfig.spec.json",
"apps/ngrid-demo-app/tsconfig.worker.json"
"apps/ngrid-demo-app/tsconfig.worker.json",
"apps/ngrid-demo-app/tsconfig.spec.json",
"apps/ngrid-demo-app/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!apps/ngrid-demo-app/**"]
"exclude": [
"**/node_modules/**",
"!apps/ngrid-demo-app/**"
]
}
},
"server": {
Expand Down Expand Up @@ -147,8 +175,13 @@
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"options": {
"tsConfig": ["apps/ngrid-demo-app-e2e/tsconfig.e2e.json"],
"exclude": ["**/node_modules/**", "!apps/ngrid-demo-app-e2e/**/*"]
"tsConfig": [
"apps/ngrid-demo-app-e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**",
"!apps/ngrid-demo-app-e2e/**/*"
]
}
}
}
Expand Down Expand Up @@ -216,6 +249,21 @@
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"main": "libs/ngrid/src/__test-runners/karma-test.ts",
"tsConfig": "libs/ngrid/tsconfig.spec.karma.json",
"karmaConfig": "libs/ngrid/karma.conf.js"
},
"configurations": {
"ci": {
"watch": false,
"progress": false,
"browsers": "ChromeHeadlessCI"
}
}
},
"testJest": {
"builder": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/ngrid/jest.config.js",
Expand All @@ -227,9 +275,13 @@
"options": {
"tsConfig": [
"libs/ngrid/tsconfig.lib.json",
"libs/ngrid/tsconfig.spec.json",
"libs/ngrid/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/ngrid/**/*"]
"exclude": [
"**/node_modules/**",
"!libs/ngrid/**/*"
]
}
}
}
Expand Down Expand Up @@ -296,6 +348,13 @@
"options": {
"jestConfig": "libs/ngrid-material/jest.config.js",
"passWithNoTests": true
},
"configurations": {
"ci": {
"watch": false,
"progress": false,
"browsers": "ChromeHeadlessCI"
}
}
},
"lint": {
Expand All @@ -305,7 +364,10 @@
"libs/ngrid-material/tsconfig.lib.json",
"libs/ngrid-material/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/ngrid-material/**/*"]
"exclude": [
"**/node_modules/**",
"!libs/ngrid-material/**/*"
]
}
}
}
Expand Down Expand Up @@ -358,7 +420,6 @@
}
}
}

},
"configurations": {
"production": {
Expand All @@ -372,6 +433,13 @@
"options": {
"jestConfig": "libs/ngrid-cypress/jest.config.js",
"passWithNoTests": true
},
"configurations": {
"ci": {
"watch": false,
"progress": false,
"browsers": "ChromeHeadlessCI"
}
}
},
"lint": {
Expand All @@ -381,7 +449,10 @@
"libs/ngrid-cypress/tsconfig.lib.json",
"libs/ngrid-cypress/tsconfig.spec.json"
],
"exclude": ["**/node_modules/**", "!libs/ngrid-cypress/**/*"]
"exclude": [
"**/node_modules/**",
"!libs/ngrid-cypress/**/*"
]
}
}
}
Expand Down
16 changes: 10 additions & 6 deletions apps/libs/shared-data/lib/datasource.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,7 @@ export class DemoDataSource {
private adapter: WorkerStoreAdapter | WindowStoreAdapter;

constructor() {
if (typeof Worker !== 'undefined') {
this.adapter = new WorkerStoreAdapter();
} else {
this.adapter = new WindowStoreAdapter();
}
this.ready = this.adapter.ready;
this.createAdapter();
}

reset(...collections: Array<DATA_TYPES>): void { this.adapter.reset(...collections); }
Expand All @@ -133,6 +128,15 @@ export class DemoDataSource {
}

dispose(): void { this.adapter.dispose(); }

protected createAdapter() {
if (typeof Worker !== 'undefined') {
this.adapter = new WorkerStoreAdapter();
} else {
this.adapter = new WindowStoreAdapter();
}
this.ready = this.adapter.ready;
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<pbl-ngrid style="height: 110px" [dataSource]="ds" [columns]="columns"></pbl-ngrid>
<pbl-ngrid style="height: 500px" [dataSource]="ds" [columns]="columns"></pbl-ngrid>
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PblNgridModule } from '@pebula/ngrid';
import { PblNgridHarness } from '@pebula/ngrid/testing';
import { DatasourceIntroductionSimpleModelExample } from './simple-model.component';

describe('demo-app/datasource/simple-model', () => {
let fixture: ComponentFixture<DatasourceIntroductionSimpleModelExample>;
let loader: HarnessLoader;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PblNgridModule.forRoot({}, [])],
declarations: [DatasourceIntroductionSimpleModelExample],
}).compileComponents();

fixture = TestBed.createComponent(DatasourceIntroductionSimpleModelExample);
fixture.detectChanges();
loader = TestbedHarnessEnvironment.loader(fixture);
});

it('should have the columns provided', async () => {
const columnIds = await (await loader.getHarness(PblNgridHarness)).getColumnIds();
expect(columnIds).toEqual(['id', 'name', 'email']);
});

it('should show the data provided', async () => {
const data = await (await loader.getHarness(PblNgridHarness)).getViewPortData();

expect(data).toEqual([
['10', 'John Doe', '[email protected]']
]);
});
});

Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core';
import { Example } from '@pebula/apps/shared';
import { createDS } from '@pebula/ngrid';
import { of, timer } from 'rxjs';
import { mapTo } from 'rxjs/operators';

@Component({
selector: 'pbl-datasource-introduction-simple-model-example',
Expand All @@ -21,5 +24,5 @@ export class DatasourceIntroductionSimpleModelExample {
},
};

ds = [ { id: 10, name: 'John Doe', email: '[email protected]' }];
ds = timer(1000).pipe(mapTo([ { id: 10, name: 'John Doe', email: '[email protected]' }]));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { PblNgridModule } from '@pebula/ngrid';
import { PblNgridHarness, ScrollToLocation } from '@pebula/ngrid/testing';
import { WorkingWithPblDataSourceExample } from './working-with-pbl-datasource.component';
import { getDataSourceProvider, getDataSource } from '../../../../../src/__test-runners/test-datasource';

describe('demo-app/datasource/working-with-pbl-datasource', () => {
let fixture: ComponentFixture<WorkingWithPblDataSourceExample>;
let loader: HarnessLoader;
let nGridHarness: PblNgridHarness;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PblNgridModule.forRoot({}, [])],
declarations: [WorkingWithPblDataSourceExample],
providers: [
await getDataSourceProvider(),
]
}).compileComponents();

fixture = TestBed.createComponent(WorkingWithPblDataSourceExample);
fixture.detectChanges();
loader = TestbedHarnessEnvironment.loader(fixture);
nGridHarness = await loader.getHarness(PblNgridHarness);
});

it('should show the data provided', async () => {
const data = await nGridHarness.getViewPortData();
expect(data.length).toBeGreaterThanOrEqual(1);
const dsData = await getDataSource().getPeople(0, data.length);
expect(data).toEqual(dsData.map( d => [d.id.toString(), d.name, d.email]));
});

it('should show the data provided scrolling to the end (vScroll)', async () => {
await nGridHarness.waitForRenderChanged(() => nGridHarness.scrollToLocation(ScrollToLocation.VerticalEnd))
const data = await nGridHarness.getViewPortData();
expect(data.length).toBeGreaterThanOrEqual(1);
const dsData = await getDataSource().getPeople(0, 500);
dsData.splice(0, dsData.length - data.length);
expect(data).toEqual(dsData.map( d => [d.id.toString(), d.name, d.email]));
});
});

6 changes: 6 additions & 0 deletions apps/ngrid-demo-app/content/concepts/testing/e2e-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: E2E Testing
path: concepts/testing/e2e-testing
parent: concepts/testing
ordinal: 1
---
7 changes: 7 additions & 0 deletions apps/ngrid-demo-app/content/concepts/testing/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
title: Testing
path: concepts/testing
parent: concepts
ordinal: 4
empty: true
---
55 changes: 55 additions & 0 deletions apps/ngrid-demo-app/content/concepts/testing/unit-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
title: Unit Testing
path: concepts/testing/unit-testing
parent: concepts/testing
ordinal: 0
---
# Unit Testing

**nGrid** comes with built in helpers to support unit testing of the grid.

The helpers are actually classes that encapsulate internal logic done by the grid and allow the tester to focus on testing
what's required for the task at hand.

These classes are called [Component test harnesses](https://material.angular.io/cdk/test-harnesses/overview) and they are part
of the test harness suite provided by the angular cdk, you can read more about it [here](https://material.angular.io/cdk/test-harnesses/overview).

The helpers are not coupled with any testing framework or runners. You can use them with karma or with protractor to execute E2E tests.

I> Using the harness is optional, you can use them to test most of the scenarios however, if you need to use plain old unit testing
techniques you are free to do so.

## NGrid Component Harness

The main harness used to interact with **nGrid** is `PblNgridHarness` located in `@pebula/ngrid/testing`.

From `PblNgridHarness` you can query for other harness components that wrap cells, columns, rows and other building blocks.

For example, `PblNgridHarness.getViewPortData()` will return a `string[][]` which represents the currently rendered row matrix
with cell data as it is rendered in the DOM.

`getViewPortData()` is helper function which uses the row harness component `PblNgridDataRowHarness` to get all rows
and in each row, the `PblNgridColumnHeaderCellHarness` component to get each cell.

I> The entire documentation site is using nGrid's test harness components.

## Testing nGrid with Jest using JSDom

The component harness helpers will work with any framework / runner, including Jest, however using Jest with JSDOM is not
recommended.

Wether you use the component harness or not, using Jest exposes a limited set of UI functionality when testing.
This is true in general, but has a deep impact on big UI components like **nGrid**.

Jest uses JSDOM to allow fest unit testing but it comes with a cost, it is not a browser.
There is no layout rendering, no CSS calculation, no scroll API support and more.

This does not allow proper UI testing for a component such as the grid.
For example, it is nearly impossible to deep test the grid with virtual scroll enabled since it is not possible to scroll.
This will also be true when working with grid's that have a lot of columns which exceed the viewport size, you will not be able
to bring them into the view, no scrolling.

That being said, if you're already using Jest you might not have any other option but to use it.
The component harness helpers will work but some functionality will not.

You will be able to test columns, data and other functionality but not all.
2 changes: 1 addition & 1 deletion apps/ngrid-demo-app/content/concepts/theming/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@
title: Theming
path: concepts/theming
parent: concepts
ordinal: 4
ordinal: 5
empty: true
---
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,3 @@ tags: internalPlugin
empty: true
---
# Drag & Drop Plugin

Loading

0 comments on commit 19bbba6

Please sign in to comment.