Skip to content

Commit

Permalink
feat(library): chartist component simplification and unit test refact…
Browse files Browse the repository at this point in the history
…oring.
  • Loading branch information
hakimio committed Jan 16, 2019
1 parent e5a3221 commit 5357a66
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 105 deletions.
109 changes: 48 additions & 61 deletions projects/ng-chartist/src/lib/chartist.component.spec.ts
Original file line number Diff line number Diff line change
@@ -1,153 +1,140 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';

import * as Chartist from 'chartist';

import { ChartistComponent, ChartType } from './chartist.component';

const data: any = require('./testdata.json');

describe('chartist component', function(): void {
describe('chartist component', () => {
let instance: ChartistComponent;
let fixture: ComponentFixture<ChartistComponent>;

beforeEach(async(() => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ChartistComponent]
}).compileComponents();
}));
});

beforeEach(function(): void {
beforeEach(() => {
fixture = TestBed.createComponent(ChartistComponent);
instance = fixture.debugElement.componentInstance;
instance = fixture.componentInstance;
});

it(`should be initialized`, () => {
expect(fixture).toBeDefined();
expect(instance).toBeDefined();
});

it('should initialize the correct chart only once', async function() {
it('should initialize the correct chart only once', () => {
const chartType: ChartType = 'Bar';

spyOn(Chartist, chartType).and.callThrough();

instance.data = data[chartType];
instance.type = chartType;

await instance['renderChart']();
instance.ngOnInit();

expect(Chartist.Bar).toHaveBeenCalledTimes(1);
});

it('should return the correct chart instance', async function() {
it('should return the correct chart instance', () => {
const chartType: ChartType = 'Bar';

instance.data = data[chartType];
instance.type = chartType;

const chart = await instance['renderChart']();
instance.ngOnInit();

expect(chart instanceof Chartist.Bar).toBe(true);
expect(instance.chart instanceof Chartist.Bar).toBe(true);
});

it('should bind events if there are events', async function() {
it('should bind events if there are events', () => {
const chartType: ChartType = 'Bar';

spyOn(<any>instance, 'bindEvents').and.callThrough();

instance.data = data[chartType];
instance.type = chartType;
instance.events = {
draw(): boolean {
return false;
}
draw: () => {}
};

await instance.ngOnInit();
instance.ngOnInit();

expect(instance['bindEvents']).toHaveBeenCalled();
});

it('should re-render the chart if the chart type changes', function(): void {
const changes: any = {
type: 'Bar'
};

instance.type = 'Line';

it('should re-render the chart if the chart type changes', () => {
spyOn(<any>instance, 'renderChart').and.callThrough();

instance['update'](changes);
instance.type = 'Bar';
instance.data = data.Bar;

instance.ngOnChanges({
type: <any>{
currentValue: instance.type
}
});

expect(instance['renderChart']).toHaveBeenCalled();
});

it('should update the chart if the data changes', async function() {
const changes: any = {
data: {
labels: [],
series: []
}
};

it('should update the chart if the data changes', () => {
instance.data = data.Bar;
instance.type = 'Bar';

fixture.detectChanges();

await instance['renderChart']();

instance.data = data.Line;
instance.type = 'Line';
instance.ngOnInit();

spyOn(instance.chart, 'update').and.callThrough();
spyOn(<any>instance, 'renderChart').and.callThrough();

fixture.detectChanges();

instance['update'](changes);
instance.ngOnChanges({
data: <any>{
currentValue: instance.data
}
});

expect(instance['renderChart']).not.toHaveBeenCalled();
expect(instance.chart.update).toHaveBeenCalled();
});

it('should update the chart if the options change', async function() {
const changes: any = {
options: {
reverseData: true
}
};

it('should update the chart if the options change', () => {
instance.data = data.Bar;
instance.type = 'Bar';

fixture.detectChanges();

await instance['renderChart']();

instance.data = data.Bar;
instance.type = 'Bar';
instance.ngOnInit();

spyOn(instance.chart, 'update').and.callThrough();
spyOn(<any>instance, 'renderChart').and.callThrough();

fixture.detectChanges();

instance['update'](changes);
instance.options = {
reverseData: true
};
instance.ngOnChanges({
options: <any>{
currentValue: instance.options
}
});

expect(instance['renderChart']).not.toHaveBeenCalled();
expect(instance.chart.update).toHaveBeenCalled();
});

it('should throw an error when missing type', function(): void {
it('should not initialize chart when type is missing', () => {
instance.data = data.Bar;

expect(instance.ngOnInit).toThrow();
instance.ngOnInit();

expect(instance.chart).toBeUndefined();
});

it('should throw an error when missing data', function(): void {
it('should not initialize chart when data is missing', () => {
instance.type = 'Bar';

expect(instance.ngOnInit).toThrow();
instance.ngOnInit();

expect(instance.chart).toBeUndefined();
});
});
76 changes: 32 additions & 44 deletions projects/ng-chartist/src/lib/chartist.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
} from '@angular/core';

import * as Chartist from 'chartist';
import { IChartistBase, IChartOptions } from 'chartist';

/**
* Possible chart types
Expand Down Expand Up @@ -42,16 +43,16 @@ export interface ChartEvent {
})
export class ChartistComponent implements OnInit, OnChanges, OnDestroy {
@Input()
data: Promise<Chartist.IChartistData> | Chartist.IChartistData;
data: Chartist.IChartistData;

@Input()
type: Promise<ChartType> | ChartType;
type: ChartType;

@Input()
options: Promise<Chartist.IChartOptions> | Chartist.IChartOptions;
options: Chartist.IChartOptions;

@Input()
responsiveOptions: Promise<ResponsiveOptions> | ResponsiveOptions;
responsiveOptions: ResponsiveOptions;

@Input()
events: ChartEvent;
Expand All @@ -60,18 +61,13 @@ export class ChartistComponent implements OnInit, OnChanges, OnDestroy {

constructor(private elementRef: ElementRef) {}

ngOnInit(): Promise<ChartInterfaces> {
if (!this.type || !this.data) {
Promise.reject('Expected at least type and data.');
}

return this.renderChart().then((chart) => {
if (this.events !== undefined) {
this.bindEvents(chart);
ngOnInit(): void {
if (this.type && this.data) {
this.renderChart();
if (this.events) {
this.bindEvents();
}

return chart;
});
}
}

ngOnChanges(changes: SimpleChanges): void {
Expand All @@ -84,47 +80,39 @@ export class ChartistComponent implements OnInit, OnChanges, OnDestroy {
}
}

private renderChart(): Promise<ChartInterfaces> {
const promises: any[] = [
this.type,
this.elementRef.nativeElement,
private renderChart() {
const nativeElement = this.elementRef.nativeElement;

if (!(this.type in Chartist)) {
throw new Error(`${this.type} is not a valid chart type`);
}

this.chart = (<any>Chartist)[this.type](
nativeElement,
this.data,
this.options,
this.responsiveOptions
];

return Promise.all(promises).then((values) => {
const [type, ...args]: any = values;

if (!(type in Chartist)) {
throw new Error(`${type} is not a valid chart type`);
}

this.chart = (Chartist as any)[type](...args);

return this.chart;
});
);
}

private update(changes: SimpleChanges): void {
if (!this.type || !this.data) {
return;
}

if (!this.chart || 'type' in changes) {
this.renderChart();
} else {
if (changes.data) {
this.data = changes.data.currentValue;
}

if (changes.options) {
this.options = changes.options.currentValue;
}

(this.chart as any).update(this.data, this.options);
} else if (changes.data || changes.options) {
(<IChartistBase<IChartOptions>>this.chart).update(
this.data,
this.options
);
}
}

private bindEvents(chart: any): void {
private bindEvents(): void {
for (const event of Object.keys(this.events)) {
chart.on(event, this.events[event]);
this.chart.on(event, this.events[event]);
}
}
}

0 comments on commit 5357a66

Please sign in to comment.