npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

plumbline

v10.0.9

Published

Angular component testing utility for simplistic shallow mounted unit testing

Downloads

17

Readme

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:

| Plumbline | Angular | Installation | | ------------ | ------- | ----------------------------- | | 5.x.x | 5.x | npm i --save-dev plumbline@5 | | 6.x.x | 6.x | npm i --save-dev plumbline@6 | | TBD | 7.x | TBD | | TBD | 8.x | TBD | | TBD | 9.x | TBD | | 10.x.x | 10.x | npm 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');
    });
});