Skip to content

Commit

Permalink
Create lifecycle tests for clay-charts
Browse files Browse the repository at this point in the history
Fixes #759
  • Loading branch information
Julien Castelain committed Mar 27, 2018
1 parent c8e7dc7 commit 565341d
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 28 deletions.
55 changes: 36 additions & 19 deletions packages/clay-charts/src/ChartBase.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {bb} from 'billboard.js';
import {Config} from 'metal-state';
import {isServerSide} from 'metal';
import {isDefAndNotNull, isServerSide} from 'metal';
import types from './utils/types';
import * as d3 from 'd3';

Expand Down Expand Up @@ -78,32 +78,49 @@ const ChartBase = {
return;
}

this._resolveData(this.data).then(data => {
this._resolvedData = data;
const config = this._constructChartConfig();
this.bbChart = bb.generate(config);

this.on('dataChanged', this._handleDataChanged.bind(this));
this.on('groupsChanged', this._handleGroupsChanged.bind(this));
this.on('_loadingChanged', this._handleLoadingChanged.bind(this));
this.on('regionsChanged', this._handleRegionsChanged.bind(this));
this.on('sizeChanged', this._handleSizeChanged.bind(this));
this.on('typeChanged', this._handleTypeChanged.bind(this));
this.on('xChanged', this._handleXChanged.bind(this));

this.emit('chartReady');
this._resolveData(this.data)
.then(data => {
if (!isDefAndNotNull(data)) {
this.emit(
'chartError',
new Error('unable to load Chart data.')
);
return;
}

this._loading = false;
});
this._resolvedData = data;

const config = this._constructChartConfig();
this.bbChart = bb.generate(config);

this.on('dataChanged', this._handleDataChanged.bind(this));
this.on('groupsChanged', this._handleGroupsChanged.bind(this));
this.on(
'_loadingChanged',
this._handleLoadingChanged.bind(this)
);
this.on(
'regionsChanged',
this._handleRegionsChanged.bind(this)
);
this.on('sizeChanged', this._handleSizeChanged.bind(this));
this.on('typeChanged', this._handleTypeChanged.bind(this));
this.on('xChanged', this._handleXChanged.bind(this));

this.emit('chartReady');

this._loading = false;
})
.catch(err => {
this.emit('chartError', err);
});
},

/**
* @inheritDoc
* @memberof ChartBase
*/
disposed() {
super.disposed();

if (isServerSide()) {
return;
}
Expand Down
22 changes: 13 additions & 9 deletions packages/clay-charts/src/DataComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,19 @@ class DataComponent extends Component {
_resolveData(data) {
this._setupPolling();

if (Array.isArray(data) || (isObject(data) && !isFunction(data))) {
return Promise.resolve(data);
} else if (isFunction(data)) {
return data().then(val => val);
} else if (isString(data)) {
return fetch(data, {cors: 'cors'})
.then(res => res.json())
.then(res => res.data);
}
return new Promise((resolve, reject) => {
if (Array.isArray(data) || (isObject(data) && !isFunction(data))) {
resolve(data);
} else if (isFunction(data)) {
data().then(val => resolve(val));
} else if (isString(data)) {
fetch(data, {cors: 'cors'})
.then(res => res.json())
.then(res => resolve(res.data));
} else {
reject(`Could not resolve data: ${data}`);
}
});
}

/**
Expand Down
243 changes: 243 additions & 0 deletions packages/clay-charts/src/__tests__/Chart.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,247 @@ describe('Chart', () => {
done();
});
});

describe('lifecycle', () => {
let chart;

beforeEach(() => {
jest.spyOn(Chart.prototype, 'attached');
jest.spyOn(Chart.prototype, 'detached');
jest.spyOn(Chart.prototype, 'disposed');

jest.spyOn(Chart.RENDERER, 'render');
jest.spyOn(Chart.RENDERER, 'update');
});

afterEach(() => {
Chart.prototype.attached.mockRestore();
Chart.prototype.detached.mockRestore();
Chart.prototype.disposed.mockRestore();

Chart.RENDERER.render.mockRestore();
Chart.RENDERER.update.mockRestore();
});

it('should run component render lifecycle', () => {
const renderListener = jest.fn();

/* eslint-disable */
class TestChart extends Chart {
created() {
this.on('render', renderListener);
}
}
/* eslint-enable */

chart = new TestChart({data: []});

expect(Chart.RENDERER.render).toHaveBeenCalledTimes(1);
expect(renderListener).toHaveBeenCalledTimes(1);
expect(Chart.prototype.attached).toHaveBeenCalledTimes(1);
expect(Chart.prototype.detached).not.toHaveBeenCalled();
});

it('should not run component render lifecycle if "false" is passed as second param', () => {
const renderListener = jest.fn();

/* eslint-disable */
class TestChart extends Chart {
created() {
this.on('render', renderListener);
}
}
/* eslint-enable */

chart = new TestChart({data: []}, false);

expect(Chart.RENDERER.render).not.toHaveBeenCalled();
expect(renderListener).not.toHaveBeenCalled();
expect(Chart.prototype.attached).not.toHaveBeenCalled();
expect(Chart.prototype.detached).not.toHaveBeenCalled();
});

it('should be able to manually invoke detach/attach lifecycle', () => {
chart = new Chart({data: []});
expect(Chart.prototype.attached).toHaveBeenCalledTimes(1);

chart.detach();
chart.detach(); // Allow multiple
expect(chart.element.parentNode).toBeNull();
expect(chart.inDocument).toBeFalsy();
expect(Chart.prototype.detached).toHaveBeenCalledTimes(1);

chart.attach();
chart.attach(); // Allow multiple
expect(chart.element.parentNode).not.toBeNull();
expect(chart.inDocument).toBeTruthy();
expect(Chart.prototype.attached).toHaveBeenCalledTimes(2);
});

it('should not throw error if attach() is called before component is rendered', () => {
chart = new Chart(null, false);

const fn = () => chart.attach();

expect(fn).not.toThrow();
expect(chart.inDocument).toBeTruthy();
});

it('should run "willAttach" lifecycle method when the component is about to attach', () => {
/* eslint-disable */
class TestChart extends Chart {}
/* eslint-enable */

jest.spyOn(TestChart.prototype, 'willAttach');
chart = new TestChart();

expect(chart.willAttach).toHaveBeenCalled();
expect(Chart.prototype.attached).toHaveBeenCalled();
expect(chart.willAttach).toHaveBeenCalledTimes(1);
});

it('should emit "willAttach" lifecycle event when the component is about to attach', () => {
const listener = jest.fn();

/* eslint-disable */
class TestChart extends Chart {
created() {
this.on('willAttach', listener);
}
}
/* eslint-enable */
chart = new TestChart();

expect(listener).toHaveBeenCalled();
expect(Chart.prototype.attached).toHaveBeenCalled();
expect(listener).toHaveBeenCalledTimes(1);
});

it('should emit "attached" event when component is attached', () => {
chart = new Chart(null, false);

const listener = jest.fn();

chart.on('attached', listener);
chart.attach('.parent', '.sibling');

expect(listener).toHaveBeenCalledTimes(1);
});

it('should return attach data via the `getAttachData` function', () => {
chart = new Chart(null, false);
chart.attach('.parent', '.sibling');

const attachData = chart.getAttachData();

expect(attachData).not.toBeNull();
expect(attachData.parent).toEqual('.parent');
expect(attachData.sibling).toEqual('.sibling');
});

it('should run "rendered" lifecycle method when the component is rendered', () => {
/* eslint-disable */
class TestChart extends Chart {}
/* eslint-enable */

jest.spyOn(TestChart.prototype, 'rendered');
chart = new TestChart();

expect(chart.rendered).toHaveBeenCalledTimes(1);
});

it('should emit "rendered" event when the component is rendered', () => {
const listener = jest.fn();

/* eslint-disable */
class TestChart extends Chart {
created() {
this.on('rendered', listener);
}
}
/* eslint-enable */
chart = new TestChart();

expect(listener).toHaveBeenCalledTimes(1);
});

it('should return component instance from lifecycle triggering methods', () => {
chart = new Chart();

expect(chart).toEqual(chart.detach());
expect(chart).toEqual(chart.attach());
});

it('should run "willDetach" lifecycle method when the component is about to detach', () => {
/* eslint-disable */
class TestChart extends Chart {}
/* eslint-enable */

jest.spyOn(TestChart.prototype, 'willDetach');
chart = new TestChart();

expect(chart.willDetach).toHaveBeenCalledTimes(0);

chart.detach();

expect(chart.willDetach).toHaveBeenCalledTimes(1);
expect(Chart.prototype.detached).toHaveBeenCalledTimes(1);
});

it('should emit "willDetach" lifecycle event when the component is about to detach', () => {
const listener = jest.fn();

/* eslint-disable */
class TestChart extends Chart {
created() {
this.on('willDetach', listener);
}
}
/* eslint-enable */

chart = new TestChart();

expect(listener).toHaveBeenCalledTimes(0);

chart.detach();

expect(listener).toHaveBeenCalledTimes(1);
expect(Chart.prototype.detached).toHaveBeenCalledTimes(1);
});

it('should dispose component', () => {
chart = new Chart();

expect(chart.element.parentNode).not.toBeNull();
const element = chart.element;

chart.dispose();
expect(element.parentNode).toBeNull();

expect(Chart.prototype.detached).toHaveBeenCalledTimes(1);
});

it('should call "disposed" lifecycle function when component is disposed', () => {
chart = new Chart();
expect(chart.disposed).not.toHaveBeenCalled();

chart.dispose();
expect(chart.disposed).toHaveBeenCalledTimes(1);
});

it('should emit "disposed" event when component is disposed', () => {
const listener = jest.fn();

chart = new Chart({
events: {
disposed: listener,
},
});

expect(listener).not.toHaveBeenCalled();

chart.dispose();
expect(listener).toHaveBeenCalledTimes(1);
});
});
});

0 comments on commit 565341d

Please sign in to comment.