10.0.9 • Published 3 years ago

plumbline v10.0.9

Weekly downloads
5
License
MIT
Repository
github
Last release
3 years ago

Plumbline

Plumbline is a Javascript testing utility that simplifies shallow mounting components in Angular and makes it easier to assert and traverse DOM elements. Inspired by the Enzyme API for React.

Plumbline also assists in the TestBed module compilation process by:

  • Consolidating duplicate component appearances (between different modules) into a single higher-level module.
  • Allowing the tester to specify whether individual components are to be mounted or mocked.
  • Mocking entire root modules for quick error free mounting.

Prerequisites

The current version of Plumbline is only tested to work with Angular 5, Jasmine and Karma.

Installation

To include Plumbline in your Angular application, install with npm

npm i --save-dev plumbline

Angular version support is as follows:

PlumblineAngularInstallation
5.x.x5.xnpm i --save-dev plumbline@5
6.x.x6.xnpm i --save-dev plumbline@6
TBD7.xTBD
TBD8.xTBD
TBD9.xTBD
10.x.x10.xnpm i --save-dev plumbline@10

Basic Usage

mount

An Angular component is mounted as follows and returns a Promise containing PlumblineWrapper.

mount<T>(
    markup: any,
    testComponent: any,
    testModule?: any,
    options?: any): Promise<PlumblineWrapper<T>>

markup: DOM markup as it would appear in your application. testComponent: Reference to the main component that will be rendered. testModule: Mount and mock scheme describing how directives, modules and providers should be rendered. options: Additional options for render (e.g. bindings).

The testModule use "mountModule" and "mockModule" parameters to optimize the render process. "mountModule" will shallow render processes associated with that component. "mockModule" will mock a component with empty methods and variables. This allows the tester to save on computation and render time for non-essential functionality during testing.

let complexComp = await mount<ComplexComponent>(
	  `<complex-component></complex-component>`,
	  ComplexComponent, {
	      mockModule: {
	          declarations: [ComplexComponent]
	      },
	      mountModule: {
	          imports: [ShallowModule1]
	      }
	  });

PlumblineWrapper Methods

.find(selector)

@input selector takes CSS @return PlumblineWrapper[] Gets a list of descendants from the current PlumblineWrapper reference filtered by a selector.

.element()

@return NativeNode of element Gets the native element of the current PlumblineWrapper reference.

.parent()

@return PlumblineWrapper Gets the parent node of the current PlumblineWrapper reference.

.instance(component?)

@input selector takes component class reference @return instance of component Gets the injected instance of the current PlumblineWrapper reference.

.update()

@return Promise of current PlumblineWrapper Runs ngOnChanges, detectChanges and whenStable for changes in Angular. You may also use await on this method to keep your process stack in sync.

Specific Examples

For many more examples of mount and testing schemes, you can review the specs of the github project. There I have written out many unit tests that actually showcase the capabilities of the package.

An Angular component can be mounted simply by providing raw markup and its class reference.

import {
    Component,
    Input,
    OnChanges,
    OnInit
} from '@angular/core';
import {mount} from './mount';

describe('Mount', () => {
    @Component({
        selector: 'simple-component',
        template: '<h1>This is Simple</h1>'
    })
    class SimpleComponent {}

    describe('Simple Component', () => {
        it('Simple Render', async () => {
            let simpleComp = await mount<SimpleComponent>(
                `<simple-component></simple-component>`,
                SimpleComponent, {}, {}
            );
            expect(simpleComp.element()).not.toEqual(null);
            expect(simpleComp.element().innerHTML).toEqual('<h1>This is Simple</h1>');
            expect(simpleComp.find('h1')[0].element().innerHTML).toEqual('This is Simple');
        });
    });
});

In addition, raw markup can include dynamic bindings passed in via the 4th parameter of mount.

describe('Mount', () => {
    @Component({
        selector: 'title-component',
        template: `
            <h1>{{titleOut}}</h1>
            <p>{{subtitleOut}}</p>
        `
    })
    class TitleComponent implements OnInit {
        @Input() titleIn: string;
        @Input() subtitleIn: string;
        titleOut: string;
        subtitleOut: string;

        constructor() {
            this.titleIn = '';
            this.titleOut = '';
            this.subtitleIn = '';
            this.subtitleOut = '';
        }

        ngOnInit() {
            this.titleIn = this.titleIn ? this.titleIn : '';
            this.titleOut = this.titleIn;
            this.subtitleIn = this.subtitleIn ? this.subtitleIn : '';
            this.subtitleOut = this.subtitleIn;
        }

        ngOnChanges() {
            this.titleIn = this.titleIn ? this.titleIn : '';
            this.titleOut = this.titleIn;
            this.subtitleIn = this.subtitleIn ? this.subtitleIn : '';
            this.subtitleOut = this.subtitleIn;
        }
    }

    describe('Simple Component', () => {
        it('Single Binding', async () => {
            let titleComp = await mount<TitleComponent>(
                `<title-component [titleIn]="currentTitle"></title-component>`,
                TitleComponent, {}, {
                    bind: {
                        currentTitle: 'Title 1'
                    }
                }
            );
            expect(titleComp.element()).not.toEqual(null);
            expect(titleComp.find('h1')[0].element().innerHTML).toEqual('Title 1');
        });
    });
});

An example of mixed mocking and mounting.

describe('Mixed Mount and Mock', () => {
		@Component({
        selector: `complex-component`,
        template: `
            <simple-component></simple-component>
            <title-component [titleIn]="'Title 1'" [subtitleIn]="'Counter: ' + counter">
            </title-component>
            <span>{{counter}}</span>
        `
    })
    class ComplexComponent implements OnInit, OnChanges {
        counter = 1;

        ngOnInit() {}
        ngOnChanges() {}

        setCounter(count: number) {
            this.counter = count;
        }
        getCounter() {
            return this.counter;
        }
    }

		it('Simple Mount Render - Mixed Mounting', async () => {
        let complexComp = await mount<ComplexComponent>(
            `<complex-component></complex-component>`,
            ComplexComponent, {
                mockModule: {
                    declarations: [SimpleComponent]
                },
                mountModule: {
                    declarations: [TitleComponent]
                }
            });
        expect(complexComp.element()).not.toEqual(null);
        expect(complexComp.element().innerHTML).not.toContain('<h1>This is Simple</h1>');
        expect(complexComp.element().innerHTML).toContain('<h1>Title 1</h1>');
        expect(complexComp.element().innerHTML).toContain('<p>Counter: 1</p>');
        expect(complexComp.find('h1').length).toEqual(1);
        expect(complexComp.find('h1')[0].element().innerHTML).toEqual('Title 1');
        expect(complexComp.find('p')[0].element().innerHTML).toEqual('Counter: 1');
    });
});
10.0.9

3 years ago

10.0.8

4 years ago

10.0.7

4 years ago

10.0.6

4 years ago

10.0.5

4 years ago

10.0.4

4 years ago

10.0.3

4 years ago

10.0.1

4 years ago

10.0.2

4 years ago

5.0.8

4 years ago

6.0.2

4 years ago

5.0.7

4 years ago

5.0.6

4 years ago

6.0.1

4 years ago

6.0.0

4 years ago

5.0.5

4 years ago

5.0.4

4 years ago

5.0.3

4 years ago

5.0.2

4 years ago

5.0.1

4 years ago

5.0.0

4 years ago

0.1.18

4 years ago

0.1.19

4 years ago

0.1.17

5 years ago

0.1.16

5 years ago

0.1.15

5 years ago

0.1.14

5 years ago

0.1.13

5 years ago

0.1.12

5 years ago

0.1.11

5 years ago

0.1.10

5 years ago

0.1.9

5 years ago

0.1.8

5 years ago

0.1.7

5 years ago

0.1.6

5 years ago

0.1.5

5 years ago

0.1.4

5 years ago

0.1.3

5 years ago

0.1.2

5 years ago

0.1.1

6 years ago

0.1.0

6 years ago

0.0.4

6 years ago

0.0.3

6 years ago

0.0.2

6 years ago

0.0.1

6 years ago