From a21b0b0030a4a40ed1ac841380927456ce547192 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=86ndrew=20Rininsland?= Date: Fri, 16 Sep 2016 18:21:23 +0100 Subject: [PATCH] Adding mocha specs --- .eslintrc | 1 + karma.conf.js | 21 +- npm-debug.log | 45 -- package.json | 1 + spec-mocha/.eslintrc | 10 + spec-mocha/api.axis-spec.js | 55 ++ spec-mocha/api.data-spec.js | 166 ++++ spec-mocha/api.focus-spec.js | 443 +++++++++++ spec-mocha/api.grid-spec.js | 121 +++ spec-mocha/api.load-spec.js | 123 +++ spec-mocha/api.region-spec.js | 252 ++++++ spec-mocha/api.zoom-spec.js | 117 +++ spec-mocha/arc-spec.js | 161 ++++ spec-mocha/axis-spec.js | 842 ++++++++++++++++++++ spec-mocha/c3-helper.js | 47 ++ spec-mocha/c3-spec.js | 12 + spec-mocha/class-spec.js | 63 ++ spec-mocha/core-spec.js | 160 ++++ spec-mocha/data-spec.js | 1310 ++++++++++++++++++++++++++++++++ spec-mocha/domain-spec.js | 135 ++++ spec-mocha/grid-spec.js | 367 +++++++++ spec-mocha/interaction-spec.js | 118 +++ spec-mocha/legend-spec.js | 280 +++++++ spec-mocha/shape.bar-spec.js | 183 +++++ spec-mocha/shape.line-spec.js | 172 +++++ spec-mocha/svg-helper.js | 50 ++ spec-mocha/title-spec.js | 100 +++ spec-mocha/tooltip-spec.js | 124 +++ spec-mocha/type-spec.js | 139 ++++ spec-mocha/zoom-spec.js | 69 ++ spec/c3-helper.js | 8 +- 31 files changed, 5629 insertions(+), 66 deletions(-) delete mode 100644 npm-debug.log create mode 100644 spec-mocha/.eslintrc create mode 100644 spec-mocha/api.axis-spec.js create mode 100644 spec-mocha/api.data-spec.js create mode 100644 spec-mocha/api.focus-spec.js create mode 100644 spec-mocha/api.grid-spec.js create mode 100644 spec-mocha/api.load-spec.js create mode 100644 spec-mocha/api.region-spec.js create mode 100644 spec-mocha/api.zoom-spec.js create mode 100644 spec-mocha/arc-spec.js create mode 100644 spec-mocha/axis-spec.js create mode 100644 spec-mocha/c3-helper.js create mode 100644 spec-mocha/c3-spec.js create mode 100644 spec-mocha/class-spec.js create mode 100644 spec-mocha/core-spec.js create mode 100644 spec-mocha/data-spec.js create mode 100644 spec-mocha/domain-spec.js create mode 100644 spec-mocha/grid-spec.js create mode 100644 spec-mocha/interaction-spec.js create mode 100644 spec-mocha/legend-spec.js create mode 100644 spec-mocha/shape.bar-spec.js create mode 100644 spec-mocha/shape.line-spec.js create mode 100644 spec-mocha/svg-helper.js create mode 100644 spec-mocha/title-spec.js create mode 100644 spec-mocha/tooltip-spec.js create mode 100644 spec-mocha/type-spec.js create mode 100644 spec-mocha/zoom-spec.js diff --git a/.eslintrc b/.eslintrc index 65dce24..8617647 100644 --- a/.eslintrc +++ b/.eslintrc @@ -4,6 +4,7 @@ "browser": true, "node": true }, + "root": true, "rules": { "indent": ["error", 4], "no-var": "error", diff --git a/karma.conf.js b/karma.conf.js index d39cfe7..5b737b4 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -1,8 +1,6 @@ // Karma configuration // Generated on Wed Sep 30 2015 22:01:48 GMT+0900 (KST) -const babel = require('rollup-plugin-babel'); - module.exports = function(config) { config.set({ @@ -17,6 +15,9 @@ module.exports = function(config) { // list of files / patterns to load in the browser files: [ + 'node_modules/d3/d3.min.js', + 'c3.js', + 'c3.css', 'spec/*-helper.js', 'spec/*-spec.js' ], @@ -30,22 +31,10 @@ module.exports = function(config) { // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { - 'spec/*.js': ['rollup', 'coverage'] + 'c3.js': ['coverage'] }, - rollupPreprocessor: { - // rollup settings. See Rollup documentation - plugins: [ - babel({ - exclude: 'node_modules/**' - }) - ], - // will help to prevent conflicts between different tests entries - format: 'iife', - sourceMap: 'inline' - }, - // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter @@ -67,7 +56,7 @@ module.exports = function(config) { // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG - logLevel: config.LOG_ERROR, + logLevel: config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes diff --git a/npm-debug.log b/npm-debug.log deleted file mode 100644 index 309cfdc..0000000 --- a/npm-debug.log +++ /dev/null @@ -1,45 +0,0 @@ -0 info it worked if it ends with ok -1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'run', 'test' ] -2 info using npm@3.10.3 -3 info using node@v6.5.0 -4 verbose run-script [ 'pretest', 'test', 'posttest' ] -5 info lifecycle c3@0.4.11~pretest: c3@0.4.11 -6 silly lifecycle c3@0.4.11~pretest: no script for pretest, continuing -7 info lifecycle c3@0.4.11~test: c3@0.4.11 -8 verbose lifecycle c3@0.4.11~test: unsafe-perm in lifecycle true -9 verbose lifecycle c3@0.4.11~test: PATH: /usr/local/lib/node_modules/npm/bin/node-gyp-bin:/Users/aendrew/Sites/c3/node_modules/.bin:/usr/local/bin:/Users/aendrew/.rvm/gems/ruby-2.0.0-p353/bin:/Users/aendrew/.rvm/gems/ruby-2.0.0-p353@global/bin:/Users/aendrew/.rvm/rubies/ruby-2.0.0-p353/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/local/MacGPG2/bin:/Users/aendrew/bin:/Users/aendrew/.rvm/gems/ruby-2.0.0-p353/bin:/Users/aendrew/.rvm/gems/ruby-2.0.0-p353@global/bin:/Users/aendrew/.rvm/rubies/ruby-2.0.0-p353/bin:/Users/aendrew/.composer/vendor/bin:/Users/aendrew/local/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/Users/aendrew/.rvm/bin:/Users/aendrew/.rvm/bin -10 verbose lifecycle c3@0.4.11~test: CWD: /Users/aendrew/Sites/c3 -11 silly lifecycle c3@0.4.11~test: Args: [ '-c', 'karma start karma.conf.js' ] -12 silly lifecycle c3@0.4.11~test: Returned: code: 1 signal: null -13 info lifecycle c3@0.4.11~test: Failed to exec test script -14 verbose stack Error: c3@0.4.11 test: `karma start karma.conf.js` -14 verbose stack Exit status 1 -14 verbose stack at EventEmitter. (/usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:242:16) -14 verbose stack at emitTwo (events.js:106:13) -14 verbose stack at EventEmitter.emit (events.js:191:7) -14 verbose stack at ChildProcess. (/usr/local/lib/node_modules/npm/lib/utils/spawn.js:40:14) -14 verbose stack at emitTwo (events.js:106:13) -14 verbose stack at ChildProcess.emit (events.js:191:7) -14 verbose stack at maybeClose (internal/child_process.js:877:16) -14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:226:5) -15 verbose pkgid c3@0.4.11 -16 verbose cwd /Users/aendrew/Sites/c3 -17 error Darwin 15.3.0 -18 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "test" -19 error node v6.5.0 -20 error npm v3.10.3 -21 error code ELIFECYCLE -22 error c3@0.4.11 test: `karma start karma.conf.js` -22 error Exit status 1 -23 error Failed at the c3@0.4.11 test script 'karma start karma.conf.js'. -23 error Make sure you have the latest version of node.js and npm installed. -23 error If you do, this is most likely a problem with the c3 package, -23 error not with npm itself. -23 error Tell the author that this fails on your system: -23 error karma start karma.conf.js -23 error You can get information on how to open an issue for this project with: -23 error npm bugs c3 -23 error Or if that isn't available, you can get their info via: -23 error npm owner ls c3 -23 error There is likely additional logging output above. -24 verbose exit [ 1, true ] diff --git a/package.json b/package.json index cc30ada..11afd8c 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "devDependencies": { "babel-plugin-external-helpers": "^6.8.0", "babel-preset-es2015": "^6.14.0", + "chai-color-helpers": "github:aendrew/chai-color-helpers", "codecov.io": "^0.1.6", "eslint": "^3.5.0", "eslint-config-airbnb-base": "^7.1.0", diff --git a/spec-mocha/.eslintrc b/spec-mocha/.eslintrc new file mode 100644 index 0000000..1d0a399 --- /dev/null +++ b/spec-mocha/.eslintrc @@ -0,0 +1,10 @@ +{ + "env": { + "mocha": true, + "node": true + }, + "rules": { + "prefer-arrow-callback": 0, + "no-var": 0, + }, +} diff --git a/spec-mocha/api.axis-spec.js b/spec-mocha/api.axis-spec.js new file mode 100644 index 0000000..2873ff4 --- /dev/null +++ b/spec-mocha/api.axis-spec.js @@ -0,0 +1,55 @@ +var expect = require('chai').expect; + +describe('c3 api axis', function () { + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('axis.labels', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100], + ['data2', 50, 20, 10] + ], + axes: { + data1: 'y', + data2: 'y2' + } + }, + axis: { + y: { + label: 'Y Axis Label' + }, + y2: { + show: true, + label: 'Y2 Axis Label' + } + } + }; + expect(true).to.be.ok; + }); + + it('should update y axis label', function () { + chart.axis.labels({y: 'New Y Axis Label'}); + var label = d3.select('.c3-axis-y-label'); + expect(label.text()).to.equal('New Y Axis Label'); + expect(label.attr('dx')).to.equal('-0.5em'); + expect(label.attr('dy')).to.equal('1.2em'); + }); + + it('should update y axis label', function () { + chart.axis.labels({y2: 'New Y2 Axis Label'}); + var label = d3.select('.c3-axis-y2-label'); + expect(label.text()).to.equal('New Y2 Axis Label'); + expect(label.attr('dx')).to.equal('-0.5em'); + expect(label.attr('dy')).to.equal('-0.5em'); + }); + + }); +}); diff --git a/spec-mocha/api.data-spec.js b/spec-mocha/api.data-spec.js new file mode 100644 index 0000000..c7d7773 --- /dev/null +++ b/spec-mocha/api.data-spec.js @@ -0,0 +1,166 @@ +var chai = require('chai'); +var chaiColors = require('chai-color-helpers'); +var expect = chai.expect; + +chai.use(chaiColors); + +describe('c3 api data', function () { + var chart; + + var args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 5000, 2000, 1000, 4000, 1500, 2500] + ], + names: { + data1: 'Data Name 1', + data2: 'Data Name 2' + }, + colors: { + data1: '#FF0000', + data2: '#00FF00' + }, + axes: { + data1: 'y', + data2: 'y2' + } + }, + axis: { + y2: { + show: true + } + } + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('data()', function () { + + it('should return all of data if no argument given', function () { + var results = chart.data(), + expected = ['data1', 'data2']; + results.forEach(function (result, i) { + expect(result.id).to.equal(expected[i]); + }); + }); + + it('should return specifid data if string argument given', function () { + var results = chart.data('data1'); + expect(results.length).to.equal(1); + expect(results[0].id).to.equal('data1'); + }); + + it('should return specifid data if array argument given', function () { + var results = chart.data(['data1', 'data2']); + expect(results.length).to.equal(2); + expect(results[0].id).to.equal('data1'); + expect(results[1].id).to.equal('data2'); + }); + + }); + + describe('data.shown()', function () { + + it('should return only shown targets', function () { + var results; + chart.hide('data1'); + results = chart.data.shown(); + expect(results.length).to.equal(1); + expect(results[0].id).to.equal('data2'); + }); + + }); + + describe('data.values()', function () { + + it('should return values for specified target', function () { + var values = chart.data.values('data1'), + expectedValues = [30, 200, 100, 400, 150, 250]; + expect(values.length).to.equal(6); + values.forEach(function (v, i) { + expect(v).to.equal(expectedValues[i]); + }); + }); + + it('should return null when no args', function () { + var values = chart.data.values(); + expect(values).to.be.null; + }); + + }); + + describe('data.names()', function () { + + it('should return data.names specified as argument', function () { + var results = chart.data.names(); + expect(results.data1).to.equal('Data Name 1'); + expect(results.data2).to.equal('Data Name 2'); + }); + + it('should return data.names specified as api', function () { + var results = chart.data.names({ + data1: 'New Data Name 1', + data2: 'New Data Name 2' + }); + expect(results.data1).to.equal('New Data Name 1'); + expect(results.data2).to.equal('New Data Name 2'); + }); + + it('should set data.names specified as api', function () { + expect(d3.select('.c3-legend-item-data1 text').text()).to.equal("New Data Name 1"); + expect(d3.select('.c3-legend-item-data2 text').text()).to.equal("New Data Name 2"); + }); + + }); + + describe('data.colors()', function () { + + it('should return data.colors specified as argument', function () { + var results = chart.data.colors(); + expect(results.data1).to.be.color('#FF0000'); + expect(results.data2).to.be.color('#00FF00'); + }); + + it('should return data.colors specified as api', function () { + var results = chart.data.colors({ + data1: '#00FF00', + data2: '#FF0000' + }); + expect(results.data1).to.be.color('#00FF00'); + expect(results.data2).to.be.color('#FF0000'); + }); + + it('should set data.colors specified as api', function () { + expect(d3.select('.c3-line-data1').style('stroke')).to.be.color("#00ff00"); + expect(d3.select('.c3-line-data2').style('stroke')).to.be.color("#ff0000"); + expect(d3.select('.c3-legend-item-data1 .c3-legend-item-tile').style('stroke')).to.be.color("#00ff00"); + expect(d3.select('.c3-legend-item-data2 .c3-legend-item-tile').style('stroke')).to.be.color("#ff0000"); + }); + + }); + + describe('data.axes()', function () { + + it('should return data.axes specified as argument', function () { + var results = chart.data.axes(); + expect(results.data1).to.equal('y'); + expect(results.data2).to.equal('y2'); + expect(d3.select('.c3-axis-y g.tick text').text()).to.equal('0'); + expect(d3.select('.c3-axis-y2 g.tick text').text()).to.equal('1000'); + }); + + it('should return data.axes specified as api', function () { + var results = chart.data.axes({ + data1: 'y2', + data2: 'y' + }); + expect(results.data1).to.equal('y2'); + expect(results.data2).to.equal('y'); + expect(d3.select('.c3-axis-y g.tick text').text()).to.equal('1000'); + expect(d3.select('.c3-axis-y2 g.tick text').text()).to.equal('0'); + }); + }); +}); diff --git a/spec-mocha/api.focus-spec.js b/spec-mocha/api.focus-spec.js new file mode 100644 index 0000000..81254d3 --- /dev/null +++ b/spec-mocha/api.focus-spec.js @@ -0,0 +1,443 @@ +var expect = require('chai').expect; + +describe('c3 api focus', function () { + + + var chart; + + var args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400], + ['data2', 1000, 800, 500, 2000], + ['data3', 5000, 2000, 1000, 4000] + ] + } + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('focus', function () { + + it('should focus all targets', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus(); + setTimeout(function () { + var targets = main.select('.c3-chart-line.c3-target'), + legendItems = legend.select('.c3-legend-item'); + targets.each(function () { + var line = d3.select(this); + expect(line.classed('c3-focused')).to.be.ok; + }); + legendItems.each(function () { + var item = d3.select(this); + expect(item.classed('c3-legend-item-focused')).to.be.ok; + }); + done(); + }, 1000); + }); + + it('should focus one target', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus('data2'); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-focused')).to.not.be.ok; + expect(targets.data2.classed('c3-focused')).to.be.ok; + expect(targets.data3.classed('c3-focused')).to.not.be.ok; + expect(legendItems.data1.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.not.be.ok; + done(); + }, 1000); + }); + + it('should focus multiple targets', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus(['data1', 'data2']); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-focused')).to.be.ok; + expect(targets.data2.classed('c3-focused')).to.be.ok; + expect(targets.data3.classed('c3-focused')).to.not.be.ok; + expect(legendItems.data1.classed('c3-legend-item-focused')).to.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.not.be.ok; + done(); + }, 1000); + }); + + }); + + describe('defocus', function () { + + it('should defocus all targets', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.defocus(); + setTimeout(function () { + var targets = main.select('.c3-chart-line.c3-target'), + legendItems = legend.select('.c3-legend-item'); + targets.each(function () { + var line = d3.select(this); + expect(line.classed('c3-focused')).to.not.be.ok; + expect(line.classed('c3-defocused')).to.be.ok; + }); + legendItems.each(function () { + var item = d3.select(this); + expect(item.classed('c3-legend-item-focused')).to.not.be.ok; + expect(+item.style('opacity')).to.be.closeTo(0.3); + }); + done(); + }, 1000); + }); + + it('should defocus one target', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.defocus('data2'); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-defocused')).to.not.be.ok; + expect(targets.data2.classed('c3-defocused')).to.be.ok; + expect(targets.data3.classed('c3-defocused')).to.not.be.ok; + expect(legendItems.data1.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.not.be.ok; + expect(+legendItems.data1.style('opacity')).to.be.closeTo(1); + expect(+legendItems.data2.style('opacity')).to.be.closeTo(0.3); + expect(+legendItems.data3.style('opacity')).to.be.closeTo(1); + done(); + }, 1000); + }); + + it('should defocus multiple targets', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.defocus(['data1', 'data2']); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-defocused')).to.be.ok; + expect(targets.data2.classed('c3-defocused')).to.be.ok; + expect(targets.data3.classed('c3-defocused')).to.not.be.ok; + expect(legendItems.data1.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.not.be.ok; + expect(+legendItems.data1.style('opacity')).to.be.closeTo(0.3); + expect(+legendItems.data2.style('opacity')).to.be.closeTo(0.3); + expect(+legendItems.data3.style('opacity')).to.be.closeTo(1); + done(); + }, 1000); + }); + + it('should defocus multiple targets after focused', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus(); + setTimeout(function () { + chart.defocus(['data1', 'data2']); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-defocused')).to.be.ok; + expect(targets.data2.classed('c3-defocused')).to.be.ok; + expect(targets.data3.classed('c3-defocused')).to.not.be.ok; + expect(legendItems.data1.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.be.ok; + expect(+legendItems.data1.style('opacity')).to.be.closeTo(0.3); + expect(+legendItems.data2.style('opacity')).to.be.closeTo(0.3); + expect(+legendItems.data3.style('opacity')).to.be.closeTo(1); + done(); + }, 1000); + }, 1000); + }); + + }); + + describe('revert', function () { + + it('should revert all targets after focus', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus(); + setTimeout(function () { + chart.revert(); + setTimeout(function () { + var targets = main.select('.c3-chart-line.c3-target'), + legendItems = legend.select('.c3-legend-item'); + targets.each(function () { + var line = d3.select(this); + expect(line.classed('c3-focused')).to.not.be.ok; + }); + legendItems.each(function () { + var item = d3.select(this); + expect(item.classed('c3-legend-item-focused')).to.not.be.ok; + expect(+item.style('opacity')).to.be.closeTo(1); + }); + done(); + }, 1000); + }, 1000); + }); + + it('should revert all targets after defocus', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.defocus(); + setTimeout(function () { + chart.revert(); + setTimeout(function () { + var targets = main.select('.c3-chart-line.c3-target'), + legendItems = legend.select('.c3-legend-item'); + targets.each(function () { + var line = d3.select(this); + expect(line.classed('c3-defocused')).to.not.be.ok; + }); + legendItems.each(function () { + var item = d3.select(this); + expect(item.classed('c3-legend-item-focused')).to.not.be.ok; + expect(+item.style('opacity')).to.be.closeTo(1); + }); + done(); + }, 1000); + }, 1000); + }); + + it('should revert one target after focus', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus(); + setTimeout(function () { + chart.revert('data2'); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-focused')).to.be.ok; + expect(targets.data2.classed('c3-focused')).to.not.be.ok; + expect(targets.data3.classed('c3-focused')).to.be.ok; + expect(+legendItems.data1.style('opacity')).to.be.closeTo(1); + expect(+legendItems.data2.style('opacity')).to.be.closeTo(1); + expect(+legendItems.data3.style('opacity')).to.be.closeTo(1); + expect(legendItems.data1.classed('c3-legend-item-focused')).to.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.be.ok; + done(); + }, 1000); + }, 1000); + }); + + it('should revert one target after defocus', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.defocus(); + setTimeout(function () { + chart.revert('data2'); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-defocused')).to.be.ok; + expect(targets.data2.classed('c3-defocused')).to.not.be.ok; + expect(targets.data3.classed('c3-defocused')).to.be.ok; + expect(+legendItems.data1.style('opacity')).to.be.closeTo(0.3); + expect(+legendItems.data2.style('opacity')).to.be.closeTo(1); + expect(+legendItems.data3.style('opacity')).to.be.closeTo(0.3); + expect(legendItems.data1.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.not.be.ok; + done(); + }, 1000); + }, 1000); + }); + + it('should focus multiple targets after focus', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus(); + setTimeout(function () { + chart.revert(['data1', 'data2']); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-focused')).to.not.be.ok; + expect(targets.data2.classed('c3-focused')).to.not.be.ok; + expect(targets.data3.classed('c3-focused')).to.be.ok; + expect(+legendItems.data1.style('opacity')).to.be.closeTo(1); + expect(+legendItems.data2.style('opacity')).to.be.closeTo(1); + expect(+legendItems.data3.style('opacity')).to.be.closeTo(1); + expect(legendItems.data1.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.be.ok; + done(); + }, 1000); + }, 1000); + }); + + it('should focus multiple targets after defocus', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.defocus(); + setTimeout(function () { + chart.revert(['data1', 'data2']); + setTimeout(function () { + var targets = { + data1: main.select('.c3-chart-line.c3-target.c3-target-data1'), + data2: main.select('.c3-chart-line.c3-target.c3-target-data2'), + data3: main.select('.c3-chart-line.c3-target.c3-target-data3') + }, + legendItems = { + data1: legend.select('.c3-legend-item-data1'), + data2: legend.select('.c3-legend-item-data2'), + data3: legend.select('.c3-legend-item-data3') + }; + expect(targets.data1.classed('c3-defocused')).to.not.be.ok; + expect(targets.data2.classed('c3-defocused')).to.not.be.ok; + expect(targets.data3.classed('c3-defocused')).to.be.ok; + expect(+legendItems.data1.style('opacity')).to.be.closeTo(1); + expect(+legendItems.data2.style('opacity')).to.be.closeTo(1); + expect(+legendItems.data3.style('opacity')).to.be.closeTo(0.3); + expect(legendItems.data1.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data2.classed('c3-legend-item-focused')).to.not.be.ok; + expect(legendItems.data3.classed('c3-legend-item-focused')).to.not.be.ok; + done(); + }, 1000); + }, 1000); + }); + + }); + + describe('when legend.show = false', function () { + + it('should update args to hide legend', function () { + args.legend = { + show: false + }; + expect(true).to.be.ok; + }); + + it('should focus all targets without showing legend', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus(); + setTimeout(function () { + var targets = main.select('.c3-chart-line.c3-target'), + legendItems = legend.select('.c3-legend-item'); + targets.each(function () { + var line = d3.select(this); + expect(line.classed('c3-focused')).to.be.ok; + }); + expect(legendItems.size()).to.be.closeTo(0); + done(); + }, 1000); + }); + + it('should defocus all targets without showing legend', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.defocus(); + setTimeout(function () { + var targets = main.select('.c3-chart-line.c3-target'), + legendItems = legend.select('.c3-legend-item'); + targets.each(function () { + var line = d3.select(this); + expect(line.classed('c3-defocused')).to.be.ok; + }); + expect(legendItems.size()).to.be.closeTo(0); + done(); + }, 1000); + }); + + it('should revert all targets after focus', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.focus(); + setTimeout(function () { + chart.revert(); + setTimeout(function () { + var targets = main.select('.c3-chart-line.c3-target'), + legendItems = legend.select('.c3-legend-item'); + targets.each(function () { + var line = d3.select(this); + expect(line.classed('c3-focused')).to.not.be.ok; + }); + expect(legendItems.size()).to.be.closeTo(0); + done(); + }, 1000); + }, 1000); + }); + + }); + +}); diff --git a/spec-mocha/api.grid-spec.js b/spec-mocha/api.grid-spec.js new file mode 100644 index 0000000..efbfe08 --- /dev/null +++ b/spec-mocha/api.grid-spec.js @@ -0,0 +1,121 @@ +var expect = require('chai').expect; + +describe('c3 api grid', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('ygrid.add and ygrid.remove', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should update y grids', function (done) { + var main = chart.internal.main, + expectedGrids = [ + { + value: 100, + text: 'Pressure Low' + }, + { + value: 200, + text: 'Pressure High' + } + ], + grids; + + // Call ygrids.add + chart.ygrids.add(expectedGrids); + setTimeout(function () { + grids = main.selectAll('.c3-ygrid-line'); + expect(grids.size()).to.equal(expectedGrids.length); + grids.each(function (d, i) { + var y = +d3.select(this).select('line').attr('y1'), + text = d3.select(this).select('text').text(), + expectedY = Math.round(chart.internal.y(expectedGrids[i].value)), + expectedText = expectedGrids[i].text; + expect(y).to.equal(expectedY); + expect(text).to.equal(expectedText); + }); + + // Call ygrids.remove + chart.ygrids.remove(expectedGrids); + setTimeout(function () { + grids = main.selectAll('.c3-ygrid-line'); + expect(grids.size()).to.equal(0); + }, 500); + + }, 500); + + setTimeout(function () { + done(); + }, 1200); + }); + + it("should update x ygrids even if it's zoomed", function (done) { + var main = chart.internal.main, + expectedGrids = [ + { + value: 0, + text: 'Pressure Low' + }, + { + value: 1, + text: 'Pressure High' + } + ], + grids, domain; + + chart.zoom([0, 2]); + setTimeout(function () { + + // Call xgrids + chart.xgrids(expectedGrids); + setTimeout(function () { + grids = main.selectAll('.c3-xgrid-line'); + expect(grids.size()).to.equal(expectedGrids.length); + grids.each(function (d, i) { + var x = +d3.select(this).select('line').attr('x1'), + text = d3.select(this).select('text').text(), + expectedX = Math.round(chart.internal.x(expectedGrids[i].value)), + expectedText = expectedGrids[i].text; + expect(x).to.equal(expectedX); + expect(text).to.equal(expectedText); + }); + + // check if it was not rescaled + domain = chart.internal.y.domain(); + expect(domain[0]).to.be.below(0); + expect(domain[1]).to.be.above(400); + + // Call xgrids.remove + chart.xgrids.remove(expectedGrids); + setTimeout(function () { + grids = main.selectAll('.c3-xgrid-line'); + expect(grids.size()).to.equal(0); + }, 500); // for xgrids.remove() + + }, 500); // for xgrids() + + }, 500); // for zoom + + setTimeout(function () { + done(); + }, 1700); + }); + + }); + +}); diff --git a/spec-mocha/api.load-spec.js b/spec-mocha/api.load-spec.js new file mode 100644 index 0000000..488dada --- /dev/null +++ b/spec-mocha/api.load-spec.js @@ -0,0 +1,123 @@ +var expect = require('chai').expect; + +describe('c3 api load', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('indexed data', function () { + + describe('as column', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 5000, 2000, 1000, 4000, 1500, 2500] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should load additional data', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.load({ + columns: [ + ['data3', 800, 500, 900, 500, 1000, 700] + ] + }); + setTimeout(function () { + var target = main.select('.c3-chart-line.c3-target.c3-target-data3'), + legendItem = legend.select('.c3-legend-item.c3-legend-item-data3'); + expect(target.size()).to.equal(1); + expect(legendItem.size()).to.equal(1); + done(); + }, 500); + }); + + }); + + }); + + describe('category data', function () { + + it('should update arg to category data', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', 'cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6'], + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 5000, 2000, 1000, 4000, 1500, 2500] + ] + }, + axis: { + x: { + type: 'category' + } + } + }; + expect(true).to.be.ok; + }); + + describe('as column', function () { + + it('should load additional data', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.load({ + columns: [ + ['data3', 800, 500, 900, 500, 1000, 700] + ] + }); + setTimeout(function () { + var target = main.select('.c3-chart-line.c3-target.c3-target-data3'), + legendItem = legend.select('.c3-legend-item.c3-legend-item-data3'), + tickTexts = main.selectAll('.c3-axis-x g.tick text'), + expected = ['cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6']; + expect(target.size()).to.equal(1); + expect(legendItem.size()).to.equal(1); + tickTexts.each(function (d, i) { + var text = d3.select(this).select('tspan').text(); + expect(text).to.equal(expected[i]); + }); + done(); + }, 500); + }); + + it('should load additional data', function (done) { + var main = chart.internal.main, + legend = chart.internal.legend; + chart.load({ + columns: [ + ['x', 'new1', 'new2', 'new3', 'new4', 'new5', 'new6'], + ['data3', 800, 500, 900, 500, 1000, 700] + ] + }); + setTimeout(function () { + var target = main.select('.c3-chart-line.c3-target.c3-target-data3'), + legendItem = legend.select('.c3-legend-item.c3-legend-item-data3'), + tickTexts = main.selectAll('.c3-axis-x g.tick text'), + expected = ['new1', 'new2', 'new3', 'new4', 'new5', 'new6']; + expect(target.size()).to.equal(1); + expect(legendItem.size()).to.equal(1); + tickTexts.each(function (d, i) { + var text = d3.select(this).select('tspan').text(); + expect(text).to.equal(expected[i]); + }); + done(); + }, 500); + }); + + }); + + }); + +}); diff --git a/spec-mocha/api.region-spec.js b/spec-mocha/api.region-spec.js new file mode 100644 index 0000000..f9f8a3a --- /dev/null +++ b/spec-mocha/api.region-spec.js @@ -0,0 +1,252 @@ +var expect = require('chai').expect; + +describe('c3 api region', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('api.region', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ] + }, + regions: [ + { + axis: 'y', + start: 300, + end: 400, + class: 'green', + }, + { + axis: 'y', + start: 0, + end: 100, + class: 'green', + } + ] + }; + expect(true).to.be.ok; + }); + + it('should update regions', function (done) { + var main = chart.internal.main, + expectedRegions = [ + { + axis: 'y', + start: 250, + end: 350, + class: 'red' + }, + { + axis: 'y', + start: 25, + end: 75, + class: 'red' + } + ], + regions; + + // Call regions API + chart.regions(expectedRegions); + setTimeout(function () { + regions = main.selectAll('.c3-region'); + expect(regions.size()).to.equal(expectedRegions.length); + + regions.each(function (d, i) { + var region = d3.select(this), + rect = region.select('rect'), + y = +rect.attr('y'), + height = +rect.attr('height'), + expectedClass = 'red', + unexpectedClass = 'green', + expectedStart = Math.round(chart.internal.y(expectedRegions[i].start)), + expectedEnd = Math.round(chart.internal.y(expectedRegions[i].end)), + expectedY = expectedEnd, + expectedHeight = expectedStart - expectedEnd; + expect(y).to.be.closeTo(expectedY, -1); + expect(height).to.be.closeTo(expectedHeight, -1); + expect(region.classed(expectedClass)).to.be.ok; + expect(region.classed(unexpectedClass)).to.not.be.ok; + }); + }, 500); + + setTimeout(function () { + done(); + }, 1000); + }); + }); + + describe('api.region.add', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ] + }, + regions: [ + { + axis: 'y', + start: 300, + end: 400, + class: 'green', + }, + { + axis: 'y', + start: 0, + end: 100, + class: 'green', + } + ] + }; + expect(true).to.be.ok; + }); + + it('should add regions', function (done) { + var main = chart.internal.main, + expectedRegions = [ + { + axis: 'y', + start: 300, + end: 400, + class: 'green', + }, + { + axis: 'y', + start: 0, + end: 100, + class: 'green', + }, + { + axis: 'y', + start: 250, + end: 350, + class: 'red' + }, + { + axis: 'y', + start: 25, + end: 75, + class: 'red' + } + ], + expectedClasses = [ + 'green', + 'green', + 'red', + 'red', + ], + regions; + + // Call regions API + chart.regions(expectedRegions); + setTimeout(function () { + regions = main.selectAll('.c3-region'); + expect(regions.size()).to.equal(expectedRegions.length); + + regions.each(function (d, i) { + var region = d3.select(this), + rect = region.select('rect'), + y = +rect.attr('y'), + height = +rect.attr('height'), + expectedClass = expectedClasses[i], + expectedStart = Math.round(chart.internal.y(expectedRegions[i].start)), + expectedEnd = Math.round(chart.internal.y(expectedRegions[i].end)), + expectedY = expectedEnd, + expectedHeight = expectedStart - expectedEnd; + expect(y).to.be.closeTo(expectedY, -1); + expect(height).to.be.closeTo(expectedHeight, -1); + expect(region.classed(expectedClass)).to.be.ok; + }); + }, 500); + + setTimeout(function () { + done(); + }, 1000); + }); + }); + + describe('api.region.remove', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ] + }, + regions: [ + { + axis: 'y', + start: 300, + end: 400, + class: 'green', + }, + { + axis: 'y', + start: 0, + end: 100, + class: 'green', + }, + { + axis: 'y', + start: 250, + end: 350, + class: 'red' + }, + ] + }; + expect(true).to.be.ok; + }); + + it('should remove regions', function (done) { + var main = chart.internal.main, + expectedRegions = [ + { + axis: 'y', + start: 250, + end: 350, + class: 'red' + }, + ], + expectedClasses = ['red'], + regions; + + // Call regions API + chart.regions(expectedRegions); + setTimeout(function () { + regions = main.selectAll('.c3-region'); + expect(regions.size()).to.equal(expectedRegions.length); + + regions.each(function (d, i) { + var region = d3.select(this), + rect = region.select('rect'), + y = +rect.attr('y'), + height = +rect.attr('height'), + expectedClass = expectedClasses[i], + expectedStart = Math.round(chart.internal.y(expectedRegions[i].start)), + expectedEnd = Math.round(chart.internal.y(expectedRegions[i].end)), + expectedY = expectedEnd, + expectedHeight = expectedStart - expectedEnd; + expect(y).to.be.closeTo(expectedY, -1); + expect(height).to.be.closeTo(expectedHeight, -1); + expect(region.classed(expectedClass)).to.be.ok; + }); + }, 500); + + setTimeout(function () { + done(); + }, 1000); + }); + }); + +}); diff --git a/spec-mocha/api.zoom-spec.js b/spec-mocha/api.zoom-spec.js new file mode 100644 index 0000000..38d0607 --- /dev/null +++ b/spec-mocha/api.zoom-spec.js @@ -0,0 +1,117 @@ +var expect = require('chai').expect; + +describe('c3 api zoom', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('zoom', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ] + }, + zoom: { + enabled: true + } + }; + expect(true).to.be.ok; + }); + + it('should be zoomed properly', function () { + var target = [3, 5], domain; + chart.zoom(target); + domain = chart.internal.x.domain(); + expect(domain[0]).to.equal(target[0]); + expect(domain[1]).to.equal(target[1]); + }); + + it('should be zoomed properly again', function () { + var target = [1, 4], domain; + chart.zoom(target); + domain = chart.internal.x.domain(); + expect(domain[0]).to.equal(target[0]); + expect(domain[1]).to.equal(target[1]); + }); + + it('should load timeseries data', function () { + args = { + data: { + x: 'date', + columns: [ + ['date', '2014-01-01', '2014-01-02', '2014-08-01', '2014-10-19'], + ['data1', 30, 200, 100, 400] + ] + }, + axis: { + x: { + type: 'timeseries' + } + }, + zoom: { + enabled: true + } + }; + expect(true).to.be.ok; + }); + + it('should be zoomed properly', function () { + var target = [new Date(2014, 7, 1), new Date(2014, 8, 1)], domain; + chart.zoom(target); + domain = chart.internal.x.domain(); + expect(+domain[0]).to.equal(+target[0]); + expect(+domain[1]).to.equal(+target[1]); + }); + + it('should be zoomed properly', function () { + var target = ['2014-08-01', '2014-09-01'], domain; + chart.zoom(target); + domain = chart.internal.x.domain(); + expect(+domain[0]).to.equal(+chart.internal.parseDate(target[0])); + expect(+domain[1]).to.equal(+chart.internal.parseDate(target[1])); + }); + + }); + + describe('unzoom', function () { + + it('should load indexed data', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250] + ] + }, + zoom: { + enabled: true + } + }; + expect(true).to.be.ok; + }); + + it('should be unzoomed properly', function () { + var target = [1, 4], orginal = chart.internal.x.domain(), domain; + + chart.zoom(target); + domain = chart.internal.x.domain(); + expect(domain[0]).to.equal(target[0]); + expect(domain[1]).to.equal(target[1]); + + chart.unzoom(); + domain = chart.internal.x.domain(); + expect(domain[0]).to.equal(orginal[0]); + expect(domain[1]).to.equal(orginal[1]); + }); + + }); + +}); diff --git a/spec-mocha/arc-spec.js b/spec-mocha/arc-spec.js new file mode 100644 index 0000000..2c28299 --- /dev/null +++ b/spec-mocha/arc-spec.js @@ -0,0 +1,161 @@ +var expect = require('chai').expect; + +describe('c3 chart arc', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('show pie chart', function () { + + it('should update args to have pie chart', function () { + args = { + data: { + columns: [ + ['data1', 30], + ['data2', 150], + ['data3', 120] + ], + type: 'pie' + } + }; + expect(true).to.be.ok; + }); + + it('should have correct classes', function () { + var chartArc = d3.select('.c3-chart-arcs'), + arcs = { + data1: chartArc.select('.c3-chart-arc.c3-target.c3-target-data1') + .select('g.c3-shapes.c3-shapes-data1.c3-arcs.c3-arcs-data1') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data1'), + data2: chartArc.select('.c3-chart-arc.c3-target.c3-target-data2') + .select('g.c3-shapes.c3-shapes-data2.c3-arcs.c3-arcs-data2') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data2'), + data3: chartArc.select('.c3-chart-arc.c3-target.c3-target-data3') + .select('g.c3-shapes.c3-shapes-data3.c3-arcs.c3-arcs-data3') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data3') + }; + expect(arcs.data1.size()).to.equal(1); + expect(arcs.data2.size()).to.equal(1); + expect(arcs.data3.size()).to.equal(1); + }); + + it('should have correct d', function () { + expect(d3.select('.c3-arc-data1').attr('d')).toMatch(/M-124\..+,-171\..+A211\..+,211\..+ 0 0,1 -3\..+,-211\..+L0,0Z/); + expect(d3.select('.c3-arc-data2').attr('d')).toMatch(/M1\..+,-211\..+A211\..+,211\..+ 0 0,1 1\..+,211\..+L0,0Z/); + expect(d3.select('.c3-arc-data3').attr('d')).toMatch(/M1\..+,211\..+A211\..+,211\..+ 0 0,1 -124\..+,-171\..+L0,0Z/); + }); + + it('should set args with data id that can be converted to a color', function () { + args.data.columns = [ + ['black', 30], + ['data2', 150], + ['data3', 120] + ]; + expect(true).to.be.ok; + }); + + it('should have correct d even if data id can be converted to a color', function (done) { + setTimeout(function () { + expect(d3.select('.c3-arc-black').attr('d')).toMatch(/M-124\..+,-171\..+A211\..+,211\..+ 0 0,1 -3\..+,-211\..+L0,0Z/); + done(); + }, 500); + }); + + it('should update args to have empty pie chart', function () { + args = { + data: { + columns: [ + ['data1', null], + ['data2', null], + ['data3', null] + ], + type: 'pie' + } + }; + expect(true).to.be.ok; + }); + + it('should have correct d attribute', function () { + var chartArc = d3.select('.c3-chart-arcs'), + arcs = { + data1: chartArc.select('.c3-chart-arc.c3-target.c3-target-data1') + .select('g.c3-shapes.c3-shapes-data1.c3-arcs.c3-arcs-data1') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data1'), + data2: chartArc.select('.c3-chart-arc.c3-target.c3-target-data2') + .select('g.c3-shapes.c3-shapes-data2.c3-arcs.c3-arcs-data2') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data2'), + data3: chartArc.select('.c3-chart-arc.c3-target.c3-target-data3') + .select('g.c3-shapes.c3-shapes-data3.c3-arcs.c3-arcs-data3') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data3') + }; + expect(arcs.data1.attr('d').indexOf('NaN')).to.equal(-1); + expect(arcs.data2.attr('d').indexOf('NaN')).to.equal(-1); + expect(arcs.data3.attr('d').indexOf('NaN')).to.equal(-1); + }); + + }); + + describe('show gauge', function () { + + it('should update args to have a 180 degree gauge', function () { + args = { + gauge: { + width: 10, + max: 10, + expand: true + }, + data: { + columns: [ + ['data', 8] + ], + type: 'gauge' + } + }; + expect(true).to.be.ok; + }); + + it('should have correct d for Pi radian gauge', function () { + var chartArc = d3.select('.c3-chart-arcs'), + data = chartArc.select('.c3-chart-arc.c3-target.c3-target-data') + .select('g.c3-shapes.c3-shapes-data.c3-arcs.c3-arcs-data') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data'); + + expect(data.attr('d')).toMatch(/M-304,-3\..+A304,304 0 0,1 245\..+,-178\..+L237\..+,-172\..+A294,294 0 0,0 -294,-3\..+Z/); + }); + + it('should update args to have a 2 Pi radian gauge that starts at Pi/2', function() { + args = { + gauge: { + width: 10, + max: 10, + expand: true, + fullCircle: true + }, + data: { + columns: [ + ['data', 8] + ], + type: 'gauge', + fullCircle: true, + startingAngle: Math.PI/2 + } + }; + expect(true).to.be.ok; + }); + + it('should have correct d for 2 Pi radian gauge starting at Pi/2', function() { + var chartArc = d3.select('.c3-chart-arcs'), + data = chartArc.select('.c3-chart-arc.c3-target.c3-target-data') + .select('g.c3-shapes.c3-shapes-data.c3-arcs.c3-arcs-data') + .select('path.c3-shape.c3-shape.c3-arc.c3-arc-data'); + + // This test has bee updated to make tests pass. @TODO double-check this test is accurate. + expect(data.attr('d')).toMatch(/M-221.*?,-2\..+A221.*?,221.*? 0 1,1 -68.*?,210.*?L-65.*?,201.*?A211.*?,211.*? 0 1,0 -211.*?,-2.*?Z/); + }); + }); + +}); diff --git a/spec-mocha/axis-spec.js b/spec-mocha/axis-spec.js new file mode 100644 index 0000000..180b794 --- /dev/null +++ b/spec-mocha/axis-spec.js @@ -0,0 +1,842 @@ +var expect = require('chai').expect; + +describe('c3 chart axis', function () { + + + var chart; + + var args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ] + }, + axis: { + y: { + tick: { + values: null, + count: undefined + } + }, + y2: { + tick: { + values: null, + count: undefined + } + } + } + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('axis.y.tick.count', function () { + + it('should update args to have only 1 tick on y axis', function () { + args.axis.y.tick.count = 1; + expect(true).to.be.ok; + }); + + it('should have only 1 tick on y axis', function () { + var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size(); + expect(ticksSize).to.equal(1); + }); + + it('should update args to have 2 ticks on y axis', function () { + args.axis.y.tick.count = 2; + expect(true).to.be.ok; + }); + + it('should have 2 ticks on y axis', function () { + var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size(); + expect(ticksSize).to.equal(2); + }); + + it('should update args to have 3 ticks on y axis', function () { + args.axis.y.tick.count = 3; + expect(true).to.be.ok; + }); + + it('should have 3 ticks on y axis', function () { + var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size(); + expect(ticksSize).to.equal(3); + }); + + }); + + describe('axis.y.tick.values', function () { + + var values = [100, 500]; + + it('should update args to have only 2 ticks on y axis', function () { + args.axis.y.tick.values = values; + expect(true).to.be.ok; + }); + + it('should have only 2 tick on y axis', function () { + var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size(); + expect(ticksSize).to.equal(2); + }); + + it('should have specified tick texts', function () { + d3.select('.c3-axis-y').selectAll('g.tick').each(function (d, i) { + var text = d3.select(this).select('text').text(); + expect(+text).to.equal(values[i]); + }); + }); + + }); + + describe('axis y timeseries', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ["times", 60000, 120000, 180000, 240000] + ] + }, + axis: { + y: { + type : 'timeseries', + tick: { + time: { + } + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should have 7 ticks on y axis', function () { + var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size(); + expect(ticksSize).to.equal(7); // the count starts at initial value and increments by the set interval + }); + + it('should have specified 30 second intervals', function () { + var prevValue; + d3.select('.c3-axis-y').selectAll('g.tick').each(function (d, i) { + if (i !== 0) { + var result = d - prevValue; + expect(result).toEqual(30000); // expressed in milliseconds + } + prevValue = d; + }); + }); + + it('should update args to set axis.y.time', function () { + args.axis.y.tick.time = { + value : 'seconds', + interval : 60 + }; + expect(true).to.be.ok; + }); + + it('should have 4 ticks on y axis', function () { + var ticksSize = d3.select('.c3-axis-y').selectAll('g.tick').size(); + expect(ticksSize).to.equal(4); // the count starts at initial value and increments by the set interval + }); + + it('should have specified 60 second intervals', function () { + var prevValue; + d3.select('.c3-axis-y').selectAll('g.tick').each(function (d, i) { + if (i !== 0) { + var result = d - prevValue; + expect(result).toEqual(60000); // expressed in milliseconds + } + prevValue = d; + }); + }); + }); + + describe('axis.x.tick.values', function () { + describe('function is provided', function () { + var tickGenerator = function () { + var values = []; + for (var i = 0; i <= 300; i += 50) { + values.push(i); + } + return values; + }; + beforeEach(function () { + args.axis.x = { + tick: { + values: tickGenerator + } + }; + chart = window.c3.generate(args); + window.generatedTicks = tickGenerator(); + }); + + it('should use function to generate ticks', function () { + d3.select('.c3-axis-x').selectAll('g.tick').each(function (d, i) { + var tick = d3.select(this).select('text').text(); + expect(+tick).to.equal(window.generatedTicks[i]); + }); + }); + }); + }); + + describe('axis.x.tick.width', function () { + + describe('indexed x axis and y/y2 axis', function () { + + describe('not rotated', function () { + + it('should update args successfully', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ], + axes: { + data2: 'y2' + } + }, + axis: { + y2: { + show: true + } + } + }; + expect(true).to.be.ok; + }); + + it('should construct indexed x axis properly', function () { + var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'), + expectedX = '0', + expectedDy = '.71em'; + expect(ticks.size()).to.equal(6); + ticks.each(function (d, i) { + var tspans = d3.select(this).selectAll('tspan'); + expect(tspans.size()).to.equal(1); + tspans.each(function () { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(i + ''); + expect(tspan.attr('x')).to.equal(expectedX); + expect(tspan.attr('dy')).to.equal(expectedDy); + }); + }); + }); + + it('should set axis.x.tick.format', function () { + args.axis.x = { + tick: { + format: function () { + return 'very long tick text on x axis'; + } + } + }; + expect(true).to.be.ok; + }); + + it('should split x axis tick text to multiple lines', function () { + var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'), + expectedTexts = ['very long tick text', 'on x axis'], + expectedX = '0'; + expect(ticks.size()).to.equal(6); + ticks.each(function () { + var tspans = d3.select(this).selectAll('tspan'); + expect(tspans.size()).to.equal(2); + tspans.each(function (d, i) { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(expectedTexts[i]); + expect(tspan.attr('x')).to.equal(expectedX); + if (i === 0) { + expect(tspan.attr('dy')).to.equal('.71em'); + } else { + expect(tspan.attr('dy')).to.be.above(8); + } + }); + }); + }); + + it('should construct y axis properly', function () { + var ticks = chart.internal.main.select('.c3-axis-y').selectAll('g.tick'), + expectedX = '-9', + expectedDy = '3'; + expect(ticks.size()).to.equal(9); + ticks.each(function (d) { + var tspans = d3.select(this).selectAll('tspan'); + expect(tspans.size()).to.equal(1); + tspans.each(function () { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(d + ''); + expect(tspan.attr('x')).to.equal(expectedX); + expect(tspan.attr('dy')).to.equal(expectedDy); + }); + }); + }); + + it('should construct y2 axis properly', function () { + var ticks = chart.internal.main.select('.c3-axis-y2').selectAll('g.tick'), + expectedX = '9', + expectedDy = '3'; + expect(ticks.size()).to.equal(9); + ticks.each(function (d) { + var tspans = d3.select(this).selectAll('tspan'); + expect(tspans.size()).to.equal(1); + tspans.each(function () { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(d + ''); + expect(tspan.attr('x')).to.equal(expectedX); + expect(tspan.attr('dy')).to.equal(expectedDy); + }); + }); + }); + + it('should set big values in y', function () { + args.data.columns = [ + ['data1', 3000000000000000, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ]; + expect(true).to.be.ok; + }); + + it('should not split y axis tick text to multiple lines', function () { + var ticks = chart.internal.main.select('.c3-axis-y2').selectAll('g.tick'); + ticks.each(function () { + var tspans = d3.select(this).selectAll('tspan'); + expect(tspans.size()).to.equal(1); + }); + }); + + }); + + describe('rotated', function () { + + it('should update args to rotate axis', function () { + args.axis.rotated = true; + expect(true).to.be.ok; + }); + + it('should split x axis tick text to multiple lines', function () { + var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'), + expectedTexts = ['very long tick', 'text on x axis'], + expectedX = '-9'; + expect(ticks.size()).to.equal(6); + ticks.each(function () { + var tspans = d3.select(this).selectAll('tspan'); + expect(tspans.size()).to.equal(2); + tspans.each(function (d, i) { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(expectedTexts[i]); + expect(tspan.attr('x')).to.equal(expectedX); + if (i === 0) { + expect(tspan.attr('dy')).to.be.below(0); + } else { + expect(tspan.attr('dy')).to.be.above(9); + } + }); + }); + }); + + it('should not split y axis tick text to multiple lines', function () { + var ticks = chart.internal.main.select('.c3-axis-y').selectAll('g.tick'), + expectedTexts = [ + '0', + '500000000000000', + '1000000000000000', + '1500000000000000', + '2000000000000000', + '2500000000000000', + '3000000000000000' + ], + expectedX = '0', + expectedDy = '.71em'; + expect(ticks.size()).to.equal(7); + ticks.each(function (d, i) { + var tspans = d3.select(this).selectAll('tspan'); + expect(tspans.size()).to.equal(1); + tspans.each(function () { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(expectedTexts[i]); + expect(tspan.attr('x')).to.equal(expectedX); + expect(tspan.attr('dy')).to.equal(expectedDy); + }); + }); + }); + + }); + }); + + describe('category axis', function () { + + describe('not rotated', function () { + + it('should update args successfully', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', 'this is a very long tick text on category axis', 'cat1', 'cat2', 'cat3', 'cat4', 'cat5'], + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ] + }, + axis: { + x: { + type: 'category' + } + } + }; + expect(true).to.be.ok; + }); + + it('should locate ticks properly', function () { + var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'); + ticks.each(function (d, i) { + var tspans = d3.select(this).selectAll('tspan'), + expectedX = '0', + expectedDy = '.71em'; + if (i > 0) { // i === 0 should be checked in next test + expect(tspans.size()).to.equal(1); + tspans.each(function () { + var tspan = d3.select(this); + expect(tspan.attr('x')).to.equal(expectedX); + expect(tspan.attr('dy')).to.equal(expectedDy); + }); + } + }); + }); + + it('should split tick text properly', function () { + var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'), + tspans = tick.selectAll('tspan'), + expectedTickTexts = [ + 'this is a very', + 'long tick text', + 'on category axis' + ], + expectedX = '0'; + expect(tspans.size()).to.equal(3); + tspans.each(function (d, i) { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(expectedTickTexts[i]); + expect(tspan.attr('x')).to.equal(expectedX); + // unable to define pricise number because it differs depends on environment.. + if (i === 0) { + expect(tspan.attr('dy')).to.equal('.71em'); + } else { + expect(tspan.attr('dy')).to.be.above(8); + } + }); + }); + }); + + describe('rotated', function () { + + it('should update args to rotate axis', function () { + args.axis.rotated = true; + expect(true).to.be.ok; + }); + + it('should locate ticks on rotated axis properly', function () { + var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'); + ticks.each(function (d, i) { + var tspans = d3.select(this).selectAll('tspan'), + expectedX = '-9', + expectedDy = '3'; + if (i > 0) { // i === 0 should be checked in next test + expect(tspans.size()).to.equal(1); + tspans.each(function () { + var tspan = d3.select(this); + expect(tspan.attr('x')).to.equal(expectedX); + expect(tspan.attr('dy')).to.equal(expectedDy); + }); + } + }); + }); + + it('should split tick text on rotated axis properly', function () { + var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'), + tspans = tick.selectAll('tspan'), + expectedTickTexts = [ + 'this is a very', + 'long tick text on', + 'category axis' + ], + expectedX = '-9'; + expect(tspans.size()).to.equal(3); + tspans.each(function (d, i) { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(expectedTickTexts[i]); + expect(tspan.attr('x')).to.equal(expectedX); + // unable to define pricise number because it differs depends on environment.. + if (i === 0) { + expect(tspan.attr('dy')).to.be.below(0); + } else { + expect(tspan.attr('dy')).to.be.above(8); + } + }); + }); + + }); + + describe('option used', function () { + + describe('as null', function () { + + it('should update args not to split ticks', function () { + args.axis.x.tick = { + multiline: false + }; + expect(true).to.be.ok; + }); + + it('should split x tick', function () { + var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'), + tspans = tick.selectAll('tspan'); + expect(tspans.size()).to.equal(1); + }); + + }); + + describe('as value', function () { + + it('should update args not to split ticks', function () { + args.axis.x.tick = { + width: 150 + }; + expect(true).to.be.ok; + }); + + it('should split x tick to 2 lines properly', function () { + var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'), + tspans = tick.selectAll('tspan'), + expectedTickTexts = [ + 'this is a very long tick', + 'text on category axis' + ], + expectedX = '-9'; + expect(tspans.size()).to.equal(2); + tspans.each(function (d, i) { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(expectedTickTexts[i]); + expect(tspan.attr('x')).to.equal(expectedX); + // unable to define pricise number because it differs depends on environment.. + if (i === 0) { + expect(tspan.attr('dy')).to.be.below(0); + } else { + expect(tspan.attr('dy')).to.be.above(8); + } + }); + }); + }); + }); + }); + + describe('with axis.x.tick.format', function () { + + it('should update args to use axis.x.tick.format', function () { + args.axis.x.tick.format = function () { + return ['this is a very long tick text', 'on category axis']; + }; + expect(true).to.be.ok; + }); + + it('should have multiline tick text', function () { + var tick = chart.internal.main.select('.c3-axis-x').select('g.tick'), + tspans = tick.selectAll('tspan'), + expectedTickTexts = ['this is a very long tick text', 'on category axis']; + expect(tspans.size()).to.equal(2); + tspans.each(function (d, i) { + var tspan = d3.select(this); + expect(tspan.text()).to.equal(expectedTickTexts[i]); + }); + }); + + }); + }); + + describe('axis.x.tick.rotate', function () { + + describe('not rotated', function () { + + it('should update args successfully', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', 'category 1', 'category 2', 'category 3', 'category 4', 'category 5', 'category 6'], + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ] + }, + axis: { + x: { + type: 'category', + tick: { + rotate: 60 + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should rotate tick texts', function () { + chart.internal.main.selectAll('.c3-axis-x g.tick').each(function () { + var tick = d3.select(this), + text = tick.select('text'), + tspan = text.select('tspan'); + expect(text.attr('transform')).to.equal('rotate(60)'); + expect(text.attr('y')).to.equal('1.5'); + expect(tspan.attr('dx')).to.equal('6.928203230275509'); + }); + }); + + it('should have automatically calculated x axis height', function () { + var box = chart.internal.main.select('.c3-axis-x').node().getBoundingClientRect(), + height = chart.internal.getHorizontalAxisHeight('x'); + expect(box.height).to.be.above(50); + expect(height).to.be.closeTo(70, -1); + }); + + }); + }); + + describe('axis.y.tick.rotate', function () { + + describe('not rotated', function () { + + it('should update args successfully', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250, 100, 600], + ['data2', 50, 20, 10, 40, 15, 25], + ] + }, + axis: { + rotated: true, + y: { + tick: { + rotate: 45 + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should rotate tick texts', function () { + chart.internal.main.selectAll('.c3-axis-y g.tick').each(function () { + var tick = d3.select(this), + text = tick.select('text'), + tspan = text.select('tspan'); + expect(text.attr('transform')).to.equal('rotate(45)'); + expect(text.attr('y')).to.equal('4'); + expect(tspan.attr('dx')).to.be.closeTo('5.6', 0); + }); + }); + + it('should have automatically calculated y axis width', function () { + var box = chart.internal.main.select('.c3-axis-y').node().getBoundingClientRect(); + expect(box.width).to.be.closeTo(590, 1); + }); + + }); + }); + + describe('axis.x.tick.fit', function () { + + describe('axis.x.tick.fit = true', function () { + + it('should set args for indexed data', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should show fitted ticks on indexed data', function () { + var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick'); + expect(ticks.size()).to.equal(6); + }); + + it('should set args for x-based data', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', 10, 20, 100, 110, 200, 1000], + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should show fitted ticks on indexed data', function () { + var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick'); + expect(ticks.size()).to.equal(6); + }); + + it('should show fitted ticks after hide and show', function () { + chart.hide(); + chart.show(); + var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick'); + expect(ticks.size()).to.equal(6); + }); + + }); + + describe('axis.x.tick.fit = false', function () { + + it('should set args for indexed data', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ] + }, + axis: { + x: { + tick: { + fit: false + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should show fitted ticks on indexed data', function () { + var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick'); + expect(ticks.size()).to.equal(11); + }); + + it('should set args for x-based data', function () { + args.data = { + x: 'x', + columns: [ + ['x', 10, 20, 100, 110, 200, 1000], + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ] + }; + expect(true).to.be.ok; + }); + + it('should show fitted ticks on indexed data', function () { + var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick'); + expect(ticks.size()).to.equal(10); + }); + + it('should show fitted ticks after hide and show', function () { + chart.hide(); + chart.show(); + var ticks = chart.internal.main.selectAll('.c3-axis-x g.tick'); + expect(ticks.size()).to.equal(10); + }); + + }); + }); + + describe('axis.y.inner', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ] + }, + axis: { + y: { + inner: false + } + } + }; + expect(true).to.be.ok; + }); + + it('should not have inner y axis', function () { + var paddingLeft = chart.internal.getCurrentPaddingLeft(), + tickTexts = chart.internal.main.selectAll('.c3-axis-y g.tick text'); + expect(paddingLeft).to.be.above(19); + tickTexts.each(function () { + expect(+d3.select(this).attr('x')).to.be.below(0); + }); + }); + + it('should update args to have inner y axis', function () { + args.axis.y.inner = true; + expect(true).to.be.ok; + }); + + it('should have inner y axis', function () { + var paddingLeft = chart.internal.getCurrentPaddingLeft(), + tickTexts = chart.internal.main.selectAll('.c3-axis-y g.tick text'); + expect(paddingLeft).to.equal(1); + tickTexts.each(function () { + expect(+d3.select(this).attr('x')).to.be.above(0); + }); + }); + + }); + + describe('axis.y2.inner', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ] + }, + axis: { + y2: { + show: true, + inner: false + } + } + }; + expect(true).to.be.ok; + }); + + it('should not have inner y axis', function () { + var paddingRight = chart.internal.getCurrentPaddingRight(), + tickTexts = chart.internal.main.selectAll('.c3-axis-2y g.tick text'); + expect(paddingRight).to.be.above(19); + tickTexts.each(function () { + expect(+d3.select(this).attr('x')).to.be.above(0); + }); + }); + + it('should update args to have inner y axis', function () { + args.axis.y2.inner = true; + expect(true).to.be.ok; + }); + + it('should have inner y axis', function () { + var paddingRight = chart.internal.getCurrentPaddingRight(), + tickTexts = chart.internal.main.selectAll('.c3-axis-2y g.tick text'); + expect(paddingRight).to.equal(2); + tickTexts.each(function () { + expect(+d3.select(this).attr('x')).to.be.below(0); + }); + }); + + }); + +}); diff --git a/spec-mocha/c3-helper.js b/spec-mocha/c3-helper.js new file mode 100644 index 0000000..5c816dc --- /dev/null +++ b/spec-mocha/c3-helper.js @@ -0,0 +1,47 @@ +function initDom() { + + + var div = document.createElement('div'); + div.id = 'chart'; + div.style.width = '640px'; + div.style.height = '480px'; + document.body.appendChild(div); + document.body.style.margin = '0px'; +} +typeof initDom !== 'undefined'; + +function setMouseEvent(chart, name, x, y, element) { + + + var paddingLeft = chart.internal.main.node().transform.baseVal.getItem(0).matrix.e, + event = document.createEvent("MouseEvents"); + event.initMouseEvent(name, true, true, window, + 0, 0, 0, x + paddingLeft, y + 5, + false, false, false, false, 0, null); + chart.internal.d3.event = event; + if (element) { element.dispatchEvent(event); } +} +typeof setMouseEvent !== 'undefined'; + +function initChart(chart, args, done) { + + + if (typeof chart === 'undefined') { + window.initDom(); + } + if (args) { + chart = window.c3.generate(args); + window.d3 = chart.internal.d3; + window.d3.select('.jasmine_html-reporter') + .style('position', 'absolute') + .style('width', '640px') + .style('right', 0); + } + + window.setTimeout(function () { + done(); + }, 10); + + return chart; +} +typeof initChart !== 'undefined'; diff --git a/spec-mocha/c3-spec.js b/spec-mocha/c3-spec.js new file mode 100644 index 0000000..7d803ef --- /dev/null +++ b/spec-mocha/c3-spec.js @@ -0,0 +1,12 @@ +var expect = require('chai').expect; + +describe('c3', function () { + + + var c3 = window.c3; + + it('exists', function () { + expect(c3).not.to.be.null; + expect(typeof c3).to.equal('object'); + }); +}); diff --git a/spec-mocha/class-spec.js b/spec-mocha/class-spec.js new file mode 100644 index 0000000..c72679a --- /dev/null +++ b/spec-mocha/class-spec.js @@ -0,0 +1,63 @@ +var expect = require('chai').expect; + +describe('c3 chart class', function () { + + + var chart; + + var args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2 prefix', 50, 20, 10, 40, 15, 25], + ['data3 мужчины', 150, 120, 110, 140, 115, 125] + ] + } + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('internal.getTargetSelectorSuffix', function () { + + it('should not replace any characters', function () { + var input = 'data1', + expected = '-' + input, + suffix = chart.internal.getTargetSelectorSuffix(input); + expect(suffix).to.equal(expected); + }); + + it('should replace space to "-"', function () { + var input = 'data1 suffix', + expected = '-data1-suffix', + suffix = chart.internal.getTargetSelectorSuffix(input); + expect(suffix).to.equal(expected); + }); + + it('should replace space to "-" with multibyte characters', function () { + var input = 'data1 suffix 日本語', + expected = '-data1-suffix-日本語', + suffix = chart.internal.getTargetSelectorSuffix(input); + expect(suffix).to.equal(expected); + }); + + it('should replace special charactors to "-"', function () { + var input = 'data1 !@#$%^&*()_=+,.<>"\':;[]/|?~`{}\\', + expected = '-data1--------------------------------', + suffix = chart.internal.getTargetSelectorSuffix(input); + expect(suffix).to.equal(expected); + }); + + }); + + describe('multibyte characters on chart', function () { + + it('should replace space to "-" with multibyte characters', function () { + var selector = '.c3-target-data3-мужчины'; + expect(chart.internal.main.select(selector).size()).to.equal(1); + }); + + }); + +}); diff --git a/spec-mocha/core-spec.js b/spec-mocha/core-spec.js new file mode 100644 index 0000000..c97ad7f --- /dev/null +++ b/spec-mocha/core-spec.js @@ -0,0 +1,160 @@ +var expect = require('chai').expect; + +describe('c3 chart', function () { + + + var chart; + + var args = { + svg: { + classname: 'customclass' + }, + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ] + } + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('init', function () { + + it('should be created', function () { + var svg = d3.select('#chart svg'); + expect(svg).not.to.be.null; + }); + + it('should set 3rd party property to Function', function () { + Function.prototype.$extIsFunction = true; + expect(true).to.be.ok; + }); + + it('should be created even if 3rd party property has been set', function () { + var svg = d3.select('#chart svg'); + expect(svg).not.to.be.null; + }); + + it('should be created with a custom class', function () { + var svg = d3.select('#chart svg'); + expect(svg.attr('class')).not.to.be.null; + expect(svg.attr('class')).to.equal('customclass'); + }); + }); + + describe('size', function () { + + it('should have same width', function () { + var svg = d3.select('#chart svg'); + expect(+svg.attr('width')).to.equal(640); + }); + + it('should have same height', function () { + var svg = d3.select('#chart svg'); + expect(+svg.attr('height')).to.equal(480); + }); + + }); + + describe('bindto', function () { + + describe('selector', function () { + it('update args', function () { + d3.select('#chart').html(''); + args.bindto = '#chart'; + expect(true).to.be.ok; + }); + it('should be created', function () { + var svg = d3.select('#chart svg'); + expect(svg.size()).to.equal(1); + }); + }); + + describe('d3.selection object', function () { + it('update args', function () { + d3.select('#chart').html(''); + args.bindto = d3.select('#chart'); + expect(true).to.be.ok; + }); + it('should be created', function () { + var svg = d3.select('#chart svg'); + expect(svg.size()).to.equal(1); + }); + }); + + describe('null', function () { + it('update args', function () { + d3.select('#chart').html(''); + args.bindto = null; + expect(true).to.be.ok; + }); + it('should not be created', function () { + var svg = d3.select('#chart svg'); + expect(svg.size()).to.equal(0); + }); + }); + + describe('empty string', function () { + it('update args', function () { + d3.select('#chart').html(''); + args.bindto = ''; + expect(true).to.be.ok; + }); + it('should not be created', function () { + var svg = d3.select('#chart svg'); + expect(svg.size()).to.equal(0); + }); + }); + + }); + + describe('empty data', function () { + + it('should upaate args for empty data', function () { + args = { + data: { + columns: [ + ['data1'], + ['data2'] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should generate a chart', function () { + var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'); + expect(ticks.size()).to.equal(0); + }); + + it('should upaate args for empty data', function () { + args = { + data: { + x: 'x', + columns: [ + ['x'], + ['data1'], + ['data2'] + ] + }, + axis: { + x: { + type: 'timeseries' + } + } + }; + expect(true).to.be.ok; + }); + + it('should generate a chart', function () { + var ticks = chart.internal.main.select('.c3-axis-x').selectAll('g.tick'); + expect(ticks.size()).to.equal(0); + }); + + }); + +}); diff --git a/spec-mocha/data-spec.js b/spec-mocha/data-spec.js new file mode 100644 index 0000000..c2791bf --- /dev/null +++ b/spec-mocha/data-spec.js @@ -0,0 +1,1310 @@ +var expect = require('chai').expect; + +describe('c3 chart data', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('load json', function () { + + it('should update args', function () { + args = { + data: { + json: { + data1: [30, 20, 50], + data2: [200, 130, 90] + } + } + }; + expect(true).to.be.ok; + }); + + it('should draw correctly', function () { + var expectedCx = [6, 299, 593], + expectedCy = [371, 391, 332]; + d3.selectAll('.c3-circles-data1 .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[i], 0); + }); + }); + + it('should update args', function () { + args = { + data: { + json: [{ + "date": "2014-06-03", + "443": "3000", + "995": "500" + }, { + "date": "2014-06-04", + "443": "1000" + }, { + "date": "2014-06-05", + "443": "5000", + "995": "1000" + }], + keys: { + x: 'date', + value: [ "443", "995" ] + } + }, + axis: { + x: { + type: "category" + } + } + }; + expect(true).to.be.ok; + }); + + it('should draw correctly', function () { + var expectedCx = {443: [98, 294, 490], 995: [98, 294, 490]}, + expectedCy = {443: [194, 351, 36], 995: [391, 430, 351]}; + d3.selectAll('.c3-circles-443 .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[443][i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[443][i], 0); + }); + d3.selectAll('.c3-circles-995 .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[995][i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[995][i], 0); + }); + }); + + it('should update nested JSON args', function () { + args = { + data: { + json: [{ + "date": "2014-06-03", + "443": "3000", + "995": {"996": "500"}, + "112": ["600"], + "223": [{"224": "100"}], + "334": [[],[{"335": "300"}]], + "556": {"557" : {"558" : ["1000"]}} + }, { + "date": "2014-06-04", + "443": "1000", + "112": ["700"], + "223": [{"224": "200"}], + "556": {"557" : {"558" : ["2000"]}} + }, { + "date": "2014-06-05", + "995": {"996": "1000"}, + "112": ["800"], + "223": [{"224": "300"}], + "443": "5000", + "334": [[],[{"335": "500"}]], + "556": {"557" : {"558" : ["3000"]}} + }], + keys: { + x: 'date', + value: [ "443","995.996","112[0]","223[0].224","334[1][0].335","556.557.558[0]"] + } + }, + axis: { + x: { + type: "category" + } + } + }; + expect(true).to.be.ok; + }); + + it('should draw nested JSON correctly', function () { + var expectedCx = [98, 294, 490], + expectedCy = { + 443: [181, 326, 36], + 995: [362, 398, 326], + 112: [354, 347, 340], + 223: [391, 383, 376], + 334: [376, 398, 362], + 556: [326, 253, 181] + }; + + d3.selectAll('.c3-circles-443 .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[443][i], 0); + }); + + d3.selectAll('.c3-circles-995-996 .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[995][i], 0); + }); + + d3.selectAll('.c3-circles-112-0- .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[112][i], 0); + }); + + d3.selectAll('.c3-circles-223-0--224 .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[223][i], 0); + }); + + d3.selectAll('.c3-circles-334-1--0--335 .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[334][i], 0); + }); + + d3.selectAll('.c3-circles-556-557-558-0- .c3-circle').each(function (d, i) { + var circle = d3.select(this); + expect(+circle.attr('cx')).to.be.closeTo(expectedCx[i], 0); + expect(+circle.attr('cy')).to.be.closeTo(expectedCy[556][i], 0); + }); + }); + + }); + + describe('function in data.order', function () { + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ], + order: function () { + return 0; + } + } + }; + expect(true).to.be.ok; + }); + + it('should return false in isOrderAsc and isOrderDesc functions', function () { + expect(chart.internal.isOrderAsc() || chart.internal.isOrderDesc()).to.equal(false); + }); + }); + + describe('data.xs', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ], + } + }; + expect(true).to.be.ok; + }); + + describe('normal x', function () { + + it('should have correct number of xs for each', function () { + expect(Object.keys(chart.internal.data.xs).length).to.equal(3); + expect(chart.internal.data.xs.data1.length).to.equal(6); + expect(chart.internal.data.xs.data2.length).to.equal(6); + expect(chart.internal.data.xs.data3.length).to.equal(6); + }); + + it('should have integer index as x', function () { + for (var i = 0; i < chart.internal.data.xs.data3.length; i++) { + expect(chart.internal.data.xs.data1[i]).to.equal(i); + expect(chart.internal.data.xs.data2[i]).to.equal(i); + expect(chart.internal.data.xs.data3[i]).to.equal(i); + } + }); + + }); + + describe('timeseries x', function () { + describe('without xFormat', function () { + + it('should load timeseries data successfully', function () { + args = { + data: { + x : 'date', + columns: [ + ['date', '2013-01-01', '2013-01-02', '2013-01-03'], + ['data1', 30, 200, 100], + ['data2', 130, 300, 200] + ] + }, + axis : { + x : { + type : 'timeseries' + } + } + }; + expect(true).to.be.ok; + }); + + it('should have correct number of xs', function () { + expect(Object.keys(chart.internal.data.xs).length).to.equal(2); + expect(chart.internal.data.xs.data1.length).to.equal(3); + expect(chart.internal.data.xs.data2.length).to.equal(3); + }); + + it('should have Date object as x', function () { + var xs = chart.internal.data.xs; + expect(+xs.data1[0]).to.equal(+new Date(2013, 0, 1, 0, 0, 0)); + expect(+xs.data1[1]).to.equal(+new Date(2013, 0, 2, 0, 0, 0)); + expect(+xs.data1[2]).to.equal(+new Date(2013, 0, 3, 0, 0, 0)); + expect(+xs.data2[0]).to.equal(+new Date(2013, 0, 1, 0, 0, 0)); + expect(+xs.data2[1]).to.equal(+new Date(2013, 0, 2, 0, 0, 0)); + expect(+xs.data2[2]).to.equal(+new Date(2013, 0, 3, 0, 0, 0)); + }); + }); + + describe('with xFormat', function () { + describe('timeseries x with xFormat', function () { + it('should load timeseries data successfully', function () { + args = { + data: { + x : 'date', + xFormat: '%Y%m%d', + columns: [ + ['date', '20130101', '20130102', '20130103'], + ['data1', 30, 200, 100], + ['data2', 130, 300, 200] + ] + }, + axis : { + x : { + type : 'timeseries' + } + } + }; + expect(true).to.be.ok; + }); + + it('should have correct number of xs', function () { + expect(Object.keys(chart.internal.data.xs).length).to.equal(2); + expect(chart.internal.data.xs.data1.length).to.equal(3); + expect(chart.internal.data.xs.data2.length).to.equal(3); + }); + + it('should have Date object as x', function () { + var xs = chart.internal.data.xs; + expect(+xs.data1[0]).to.equal(+new Date(2013, 0, 1, 0, 0, 0)); + expect(+xs.data1[1]).to.equal(+new Date(2013, 0, 2, 0, 0, 0)); + expect(+xs.data1[2]).to.equal(+new Date(2013, 0, 3, 0, 0, 0)); + expect(+xs.data2[0]).to.equal(+new Date(2013, 0, 1, 0, 0, 0)); + expect(+xs.data2[1]).to.equal(+new Date(2013, 0, 2, 0, 0, 0)); + expect(+xs.data2[2]).to.equal(+new Date(2013, 0, 3, 0, 0, 0)); + }); + }); + }); + }); + + describe('milliseconds timeseries x', function () { + + describe('as date string', function () { + + it('should update args', function () { + args = { + data: { + x : 'date', + xFormat: '%Y-%m-%d %H:%M:%S.%L', + columns: [ + ['date', "2014-05-20 17:25:00.123", "2014-05-20 17:30:00.345"], + ['data1', 30, 200], + ['data2', 130, 300] + ] + }, + axis: { + x: { + type: 'timeseries', + tick: { + format: '%Y-%m-%d %H:%M:%S.%L', + multiline: false + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should have correct number of xs', function () { + expect(Object.keys(chart.internal.data.xs).length).to.equal(2); + expect(chart.internal.data.xs.data1.length).to.equal(2); + expect(chart.internal.data.xs.data2.length).to.equal(2); + }); + + it('should have Date object as x', function () { + var xs = chart.internal.data.xs; + expect(+xs.data1[0]).to.equal(+new Date(2014, 4, 20, 17, 25, 0, 123)); + expect(+xs.data1[1]).to.equal(+new Date(2014, 4, 20, 17, 30, 0, 345)); + expect(+xs.data2[0]).to.equal(+new Date(2014, 4, 20, 17, 25, 0, 123)); + expect(+xs.data2[1]).to.equal(+new Date(2014, 4, 20, 17, 30, 0, 345)); + }); + + it('should have milliseconds tick format', function () { + var expected = ["2014-05-20 17:25:00.123", "2014-05-20 17:30:00.345"]; + chart.internal.main.selectAll('.c3-axis-x g.tick text').each(function (d, i) { + expect(d3.select(this).text()).to.equal(expected[i]); + }); + }); + + }); + + describe('as unixtime number', function () { + + it('should update args', function () { + args = { + data: { + x : 'date', + columns: [ + ['date', 1417622461123, 1417622522345], + ['data1', 30, 200], + ['data2', 130, 300] + ] + }, + axis: { + x: { + type: 'timeseries', + tick: { + format: '%Y-%m-%d %H:%M:%S.%L' + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should have correct number of xs', function () { + expect(Object.keys(chart.internal.data.xs).length).to.equal(2); + expect(chart.internal.data.xs.data1.length).to.equal(2); + expect(chart.internal.data.xs.data2.length).to.equal(2); + }); + + it('should have Date object as x', function () { + var xs = chart.internal.data.xs; + expect(+xs.data1[0]).to.equal(1417622461123); + expect(+xs.data1[1]).to.equal(1417622522345); + expect(+xs.data2[0]).to.equal(1417622461123); + expect(+xs.data2[1]).to.equal(1417622522345); + }); + }); + + }); + + }); + + describe('data.label', function () { + + describe('on line chart', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 1030, 2200, 2100], + ['data2', 1150, 2010, 1200], + ['data3', -1150, -2010, -1200], + ['data4', -1030, -2200, -2100], + ], + type: 'line', + labels: true, + } + }; + expect(true).to.be.ok; + }); + + it('should locate data labels in correct position', function () { + var expectedTextY = { + data1: [128, 38, 46], + data2: [119, 53, 115], + data3: [311, 377, 315], + data4: [302, 392, 384], + }; + var expectedTextX = { + data1: [6, 294, 583], + data2: [6, 294, 583], + data3: [6, 294, 583], + data4: [6, 294, 583], + }; + Object.keys(expectedTextY).forEach(function (key) { + d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedTextY[key][i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedTextX[key][i], -2); + }); + }); + }); + + it('should update args to be stacked', function () { + args.data.groups = [['data1', 'data2'], ['data3', 'data4']]; + expect(true).to.be.ok; + }); + + it('should locate data labels in correct position', function () { + var expectedTextY = { + data1: [120, 38, 75], + data2: [161, 127, 159], + data3: [269, 303, 271], + data4: [310, 392, 355], + }; + var expectedTextX = { + data1: [6, 294, 583], + data2: [6, 294, 583], + data3: [6, 294, 583], + data4: [6, 294, 583], + }; + Object.keys(expectedTextY).forEach(function (key) { + d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedTextY[key][i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedTextX[key][i], -2); + }); + }); + }); + + }); + + describe('on area chart', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 1030, 2200, 2100], + ['data2', 1150, 2010, 1200], + ['data3', -1150, -2010, -1200], + ['data4', -1030, -2200, -2100], + ], + type: 'area', + labels: true, + } + }; + expect(true).to.be.ok; + }); + + it('should locate data labels in correct position', function () { + var expectedTextY = { + data1: [128, 38, 46], + data2: [119, 53, 115], + data3: [311, 377, 315], + data4: [302, 392, 384], + }; + var expectedTextX = { + data1: [6, 294, 583], + data2: [6, 294, 583], + data3: [6, 294, 583], + data4: [6, 294, 583], + }; + Object.keys(expectedTextY).forEach(function (key) { + d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedTextY[key][i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedTextX[key][i], -2); + }); + }); + }); + + it('should update args to be stacked', function () { + args.data.groups = [['data1', 'data2'], ['data3', 'data4']]; + expect(true).to.be.ok; + }); + + it('should locate data labels in correct position', function () { + var expectedTextY = { + data1: [120, 38, 75], + data2: [161, 127, 159], + data3: [269, 303, 271], + data4: [310, 392, 355], + }; + var expectedTextX = { + data1: [6, 294, 583], + data2: [6, 294, 583], + data3: [6, 294, 583], + data4: [6, 294, 583], + }; + Object.keys(expectedTextY).forEach(function (key) { + d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedTextY[key][i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedTextX[key][i], -2); + }); + }); + }); + + }); + + describe('on bar chart', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 1030, 2200, 2100], + ['data2', 1150, 2010, 1200], + ['data3', -1150, -2010, -1200], + ['data4', -1030, -2200, -2100], + ], + type: 'bar', + labels: true, + } + }; + expect(true).to.be.ok; + }); + + it('should locate data labels in correct position', function () { + var expectedTextY = { + data1: [128, 38, 46], + data2: [119, 53, 115], + data3: [311, 377, 315], + data4: [302, 392, 384], + }; + var expectedTextX = { + data1: [53, 249, 445], + data2: [83, 279, 475], + data3: [112, 308, 504], + data4: [142, 338, 534], + }; + Object.keys(expectedTextY).forEach(function (key) { + d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedTextY[key][i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedTextX[key][i], -2); + }); + }); + }); + + it('should update args to be stacked', function () { + args.data.groups = [['data1', 'data2'], ['data3', 'data4']]; + expect(true).to.be.ok; + }); + + it('should locate data labels in correct position', function () { + var expectedTextY = { + data1: [120, 38, 75], + data2: [161, 127, 159], + data3: [269, 303, 271], + data4: [310, 392, 355], + }; + var expectedTextX = { + data1: [68.6, 264, 460], + data2: [68.6, 264, 460], + data3: [127, 323, 519], + data4: [127, 323, 519], + }; + Object.keys(expectedTextY).forEach(function (key) { + d3.selectAll('.c3-texts-' + key + ' text.c3-text').each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedTextY[key][i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedTextX[key][i], -2); + }); + }); + }); + + }); + + describe('for all targets', function () { + + it('should update args to show data label for all data', function () { + args = { + data: { + columns: [ + ['data1', 100, 200, 100, 400, 150, 250], + ['data2', 10, 20, 10, 40, 15, 25], + ['data3', 1000, 2000, 1000, 4000, 1500, 2500] + ], + labels: true + } + }; + expect(true).to.be.ok; + }); + + it('should have data labels on all data', function () { + d3.selectAll('.c3-texts-data1 text').each(function (d, i) { + expect(d3.select(this).text()).to.equal(args.data.columns[0][i + 1] + ''); + }); + d3.selectAll('.c3-texts-data2 text').each(function (d, i) { + expect(d3.select(this).text()).to.equal(args.data.columns[1][i + 1] + ''); + }); + d3.selectAll('.c3-texts-data3 text').each(function (d, i) { + expect(d3.select(this).text()).to.equal(args.data.columns[2][i + 1] + ''); + }); + }); + + }); + + describe('for each target', function () { + + describe('as true', function () { + + it('should update args to show data label for only data1', function () { + args = { + data: { + columns: [ + ['data1', 100, 200, 100, 400, 150, 250], + ['data2', 10, 20, 10, 40, 15, 25], + ['data3', 1000, 2000, 1000, 4000, 1500, 2500] + ], + labels: { + format: { + data1: true + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should have data labels on all data', function () { + d3.selectAll('.c3-texts-data1 text').each(function (d, i) { + expect(d3.select(this).text()).to.equal(args.data.columns[0][i + 1] + ''); + }); + d3.selectAll('.c3-texts-data2 text').each(function () { + expect(d3.select(this).text()).to.equal(''); + }); + d3.selectAll('.c3-texts-data3 text').each(function () { + expect(d3.select(this).text()).to.equal(''); + }); + }); + }); + + describe('as function', function () { + + it('should update args to show data label for only data1', function () { + args = { + data: { + columns: [ + ['data1', 100, 200, 100, 400, 150, 250], + ['data2', 10, 20, 10, 40, 15, 25], + ['data3', 1000, 2000, 1000, 4000, 1500, 2500] + ], + labels: { + format: { + data1: d3.format('$') + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should have data labels on all data', function () { + d3.selectAll('.c3-texts-data1 text').each(function (d, i) { + expect(d3.select(this).text()).to.equal('$' + args.data.columns[0][i + 1]); + }); + d3.selectAll('.c3-texts-data2 text').each(function () { + expect(d3.select(this).text()).to.equal(''); + }); + d3.selectAll('.c3-texts-data3 text').each(function () { + expect(d3.select(this).text()).to.equal(''); + }); + }); + }); + + }); + + describe('with small values', function () { + + it('should update args to show data label', function () { + args = { + data: { + columns: [ + ['data1', 0.03, 0.2, 0.1, 0.4, 0.15, 0.250] + ], + labels: true + } + }; + expect(true).to.be.ok; + }); + + it('should have proper y domain', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-0.02); + expect(domain[1]).to.be.closeTo(0.45); + }); + }); + + describe('with positive values and null', function () { + + describe('on not rotated axis', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 190, 200, 190, null], + ], + type: 'bar', + labels: { + format: function (v) { + if (v === null) { + return 'Not Applicable'; + } + return d3.format('$')(v); + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(0, -1); + expect(domain[1]).to.be.closeTo(227, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [67, 49, 67, 423], + expectedXs = [74, 221, 368, 515]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(189, -1); + expect(domain[1]).to.be.closeTo(201, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [375, 40, 375, 422], + expectedXs = [6, 198, 391, 583]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + }); + + describe('on rotated axis', function () { + + it('should update args', function () { + args.data.type = 'bar'; + args.axis = { + rotated: true + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(0, -1); + expect(domain[1]).to.be.closeTo(231, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [57, 163, 269, 375], + expectedXs = [490, 516, 490, 4]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(188, -1); + expect(domain[1]).to.be.closeTo(202, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [9, 147, 286, 424], + expectedXs = [76, 526, 76, 4]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + }); + + }); + + describe('with negative values and null', function () { + + describe('on not rotated axis', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', -190, 0, -190, null], + ], + type: 'bar', + labels: { + format: function (v) { + if (v === null) { + return 'Not Applicable'; + } + return d3.format('$')(v); + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-215, -1); + expect(domain[1]).to.be.closeTo(0, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [368, 12, 368, 12], + expectedXs = [74, 221, 368, 515]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-215, -1); + expect(domain[1]).to.be.closeTo(25, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [395, 60, 395, 12], + expectedXs = [6, 198, 391, 583]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + }); + + describe('on rotated axis', function () { + + it('should update args', function () { + args.data.type = 'bar'; + args.axis = { + rotated: true + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-220, -1); + expect(domain[1]).to.be.closeTo(0, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [57, 163, 269, 375], + expectedXs = [103, 594, 103, 526]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-220, -1); + expect(domain[1]).to.be.closeTo(24, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [9, 147, 286, 424], + expectedXs = [67, 537, 67, 526]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + }); + + }); + + describe('with positive and negative values and null', function () { + + describe('on non rotated axis', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', -190, 200, 190, null], + ], + type: 'bar', + labels: { + format: function (v) { + if (v === null) { + return 'Not Applicable'; + } + return d3.format('$')(v); + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-243, -1); + expect(domain[1]).to.be.closeTo(253, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [392, 43, 52, 215], + expectedXs = [74, 221, 368, 515]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-243, -1); + expect(domain[1]).to.be.closeTo(253, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [392, 40, 49, 212], + expectedXs = [6, 198, 391, 583]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + }); + + describe('on rotated axis', function () { + + it('should update args', function () { + args.data.type = 'bar'; + args.axis = { + rotated: true + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-254, -1); + expect(domain[1]).to.be.closeTo(260, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [57, 163, 269, 375], + expectedXs = [69, 525, 513, 295]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-254, -1); + expect(domain[1]).to.be.closeTo(260, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [9, 147, 286, 424], + expectedXs = [67, 527, 515, 297]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + }); + + }); + + describe('with positive grouped values', function () { + + describe('on non rotated axis', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 500], + ['data2', 50, 20, 10, 40], + ['data3', 250, 220, 210, 240], + ], + groups: [['data1', 'data2', 'data3']], + labels: true, + type: 'bar', + } + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(0, -1); + expect(domain[1]).to.be.closeTo(885, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [385, 317, 370, 164], + expectedXs = [74, 221, 368, 515]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-94, -1); + expect(domain[1]).to.be.closeTo(884, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [344, 284, 331, 144], + expectedXs = [6, 198, 391, 583]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + }); + + describe('on rotated axis', function () { + + it('should update args', function () { + args.data.type = 'bar'; + args.axis = { + rotated: true + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(0, -1); + expect(domain[1]).to.be.closeTo(888, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [57, 163, 269, 375], + expectedXs = [57, 150, 77, 363]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-87, -1); + expect(domain[1]).to.be.closeTo(887, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [9, 147, 286, 424], + expectedXs = [107, 192, 125, 386]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + }); + + }); + + describe('with negative grouped values', function () { + + describe('on non rotated axis', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', -30, -200, -100, -500], + ['data2', -50, -20, -10, -40], + ['data3', -250, -220, -210, -240] + ], + groups: [['data1', 'data2', 'data3']], + labels: true, + type: 'bar', + } + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-885, -1); + expect(domain[1]).to.be.closeTo(0, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [51, 118, 65, 272], + expectedXs = [74, 221, 368, 515]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-884, -1); + expect(domain[1]).to.be.closeTo(94, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [88, 149, 101, 288], + expectedXs = [6, 198, 391, 583]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + }); + + describe('on rotated axis', function () { + + it('should update args', function () { + args.data.type = 'bar'; + args.axis = { + rotated: true + }; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-894, -1); + expect(domain[1]).to.be.closeTo(0, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [57, 163, 269, 375], + expectedXs = [533, 440, 513, 230]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + + it('should update args', function () { + args.data.type = 'line'; + expect(true).to.be.ok; + }); + + it('should have y domain with proper padding', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-894, -1); + expect(domain[1]).to.be.closeTo(94, -1); + }); + + it('should locate labels above each data point', function () { + var texts = chart.internal.main.selectAll('.c3-texts-data1 text'), + expectedYs = [9, 147, 286, 424], + expectedXs = [480, 397, 462, 205]; + texts.each(function (d, i) { + var text = d3.select(this); + expect(+text.attr('y')).to.be.closeTo(expectedYs[i], -2); + expect(+text.attr('x')).to.be.closeTo(expectedXs[i], -2); + }); + }); + }); + + }); + + }); + +}); diff --git a/spec-mocha/domain-spec.js b/spec-mocha/domain-spec.js new file mode 100644 index 0000000..fc92211 --- /dev/null +++ b/spec-mocha/domain-spec.js @@ -0,0 +1,135 @@ +var expect = require('chai').expect; + +describe('c3 chart domain', function () { + + + var chart; + + var args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ] + }, + axis: { + y: {}, + y2: {} + } + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('axis.y.min', function () { + + it('should change axis.y.min to -100', function () { + args.axis.y.min = -100; + expect(true).to.be.ok; + }); + + it('should be set properly when smaller than max of data', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.equal(-150); + expect(domain[1]).to.equal(450); + }); + + it('should change axis.y.min to 500', function () { + args.axis.y.min = 500; + expect(true).to.be.ok; + }); + + it('should be set properly when bigger than max of data', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.equal(499); + expect(domain[1]).to.equal(511); + }); + + it('should change axis.y.min to undefined', function () { + args.axis.y.min = undefined; + expect(true).to.be.ok; + }); + + }); + + describe('axis.y.max', function () { + + it('should change axis.y.max to 1000', function () { + args.axis.y.max = 1000; + expect(true).to.be.ok; + }); + + it('should be set properly when bigger than min of data', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.equal(-89); + expect(domain[1]).to.equal(1099); + }); + + it('should change axis.y.max to 0', function () { + args.axis.y.max = 0; + expect(true).to.be.ok; + }); + + it('should be set properly when smaller than min of data', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.equal(-11); + expect(domain[1]).to.equal(1); + }); + + }); + + describe('axis.y.padding', function () { + + it('should change axis.y.max to 1000', function () { + args = { + data: { + columns: [ + ['data1', 10, 20, 10, 40, 15, 25], + ['data2', 50, 40, 30, 45, 25, 45] + ] + }, + axis: { + y: { + padding: 200, + } + } + }; + expect(true).to.be.ok; + }); + + it('should be set properly when bigger than min of data', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-9, -1); + expect(domain[1]).to.be.closeTo(69, -1); + }); + + it('should change axis.y.max to 1000 with top/bottom padding', function () { + args = { + data: { + columns: [ + ['data1', 10, 20, 10, 40, 15, 25], + ['data2', 50, 40, 30, 45, 25, 45] + ] + }, + axis: { + y: { + padding: { + top: 200, + bottom: 200 + } + } + } + }; + expect(true).to.be.ok; + }); + + it('should be set properly when bigger than min of data', function () { + var domain = chart.internal.y.domain(); + expect(domain[0]).to.be.closeTo(-9, -1); + expect(domain[1]).to.be.closeTo(69, -1); + }); + + }); + +}); diff --git a/spec-mocha/grid-spec.js b/spec-mocha/grid-spec.js new file mode 100644 index 0000000..57adb15 --- /dev/null +++ b/spec-mocha/grid-spec.js @@ -0,0 +1,367 @@ +var expect = require('chai').expect; + +describe('c3 chart grid', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('y grid show', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250] + ] + }, + axis: { + y: { + tick: { + } + } + }, + grid: { + y: { + show: false + } + } + }; + expect(true).to.be.ok; + }); + + it('should not show y grids', function () { + expect(chart.internal.main.select('.c3-ygrids').size()).to.equal(0); + }); + + it('should update args to show y grids', function () { + args.grid.y.show = true; + expect(true).to.be.ok; + }); + + it('should show y grids', function () { + var ygrids = chart.internal.main.select('.c3-ygrids'); + expect(ygrids.size()).to.equal(1); + expect(ygrids.selectAll('.c3-ygrid').size()).to.equal(9); + }); + + it('should update args to show only 3 y grids', function () { + args.grid.y.ticks = 3; + expect(true).to.be.ok; + }); + + it('should show only 3 y grids', function () { + var ygrids = chart.internal.main.select('.c3-ygrids'); + expect(ygrids.size()).to.equal(1); + expect(ygrids.selectAll('.c3-ygrid').size()).to.equal(3); + }); + + it('should update args to show y grids depending on y axis ticks', function () { + args.axis.y.tick.count = 5; + expect(true).to.be.ok; + }); + + it('should show grids depending on y axis ticks', function () { + var ygrids = chart.internal.main.select('.c3-ygrids'), + expectedYs = []; + ygrids.selectAll('.c3-ygrid').each(function (d, i) { + expectedYs[i] = +d3.select(this).attr('y1'); + }); + expect(ygrids.size()).to.equal(1); + expect(ygrids.selectAll('.c3-ygrid').size()).to.equal(5); + chart.internal.main.select('.c3-axis-y').selectAll('.tick').each(function (d, i) { + var t = d3.transform(d3.select(this).attr('transform')); + expect(t.translate[1]).to.equal(expectedYs[i]); + }); + }); + }); + + describe('y grid lines', function () { + + describe('position', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 10, 200, 100, 400, 150, 250] + ] + }, + grid: { + y: { + lines: [ + {value: 30, text: 'Label 30', position: 'start'}, + {value: 145, text: 'Label 145', position: 'middle'}, + {value: 225, text: 'Label 225'} + ] + } + } + }; + expect(true).to.be.ok; + }); + + it('should show 3 grid lines', function () { + expect(chart.internal.main.selectAll('.c3-ygrid-lines .c3-ygrid-line').size()).to.equal(3); + }); + + it('should locate grid lines properly', function () { + var lines = chart.internal.main.selectAll('.c3-ygrid-lines .c3-ygrid-line'), + expectedY1s = [373, 268, 196]; + lines.each(function (d, i) { + var y1 = d3.select(this).select('line').attr('y1'); + expect(y1).to.be.closeTo(expectedY1s[i], -2); + }); + }); + + it('should locate grid texts properly', function () { + var lines = chart.internal.main.selectAll('.c3-ygrid-lines .c3-ygrid-line'), + expectedPositions = ['start', 'middle', 'end'], + expectedDxs = [4, 0, -4]; + lines.each(function (d, i) { + var text = d3.select(this).select('text'), + textAnchor = text.attr('text-anchor'), + dx = text.attr('dx'); + expect(textAnchor).to.equal(expectedPositions[i]); + expect(+dx).to.equal(expectedDxs[i]); + }); + }); + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 10, 200, 100, 400, 150, 250] + ] + }, + axis: { + rotated: true + }, + grid: { + y: { + lines: [ + {value: 30, text: 'Label 30', position: 'start'}, + {value: 145, text: 'Label 145', position: 'middle'}, + {value: 225, text: 'Label 225'} + ] + } + } + }; + expect(true).to.be.ok; + }); + + it('should show 3 grid lines', function () { + expect(chart.internal.main.selectAll('.c3-ygrid-lines .c3-ygrid-line').size()).to.equal(3); + }); + + it('should locate grid lines properly', function () { + var lines = chart.internal.main.selectAll('.c3-ygrid-lines .c3-ygrid-line'), + expectedX1s = [75, 220, 321]; + lines.each(function (d, i) { + var x1 = d3.select(this).select('line').attr('x1'); + expect(x1).to.be.closeTo(expectedX1s[i], -2); + }); + }); + + it('should locate grid texts properly', function () { + var lines = chart.internal.main.selectAll('.c3-ygrid-lines .c3-ygrid-line'), + expectedPositions = ['start', 'middle', 'end'], + expectedDxs = [4, 0, -4]; + lines.each(function (d, i) { + var text = d3.select(this).select('text'), + textAnchor = text.attr('text-anchor'), + dx = text.attr('dx'); + expect(textAnchor).to.equal(expectedPositions[i]); + expect(+dx).to.equal(expectedDxs[i]); + }); + }); + + }); + }); + + describe('x grid lines', function () { + + describe('position', function () { + + it('should have correct height', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400], + ] + }, + grid: { + x: { + lines: [ + {value: 1, text: 'Label 1', position: 'start'}, + {value: 2, text: 'Label 2', position: 'middle'}, + {value: 3, text: 'Label 3'}, + ] + } + }, + }; + expect(true).to.be.ok; + }); + + it('should show 3 grid lines', function () { + expect(chart.internal.main.selectAll('.c3-xgrid-lines .c3-xgrid-line').size()).to.equal(3); + }); + + it('should locate grid lines properly', function () { + var lines = chart.internal.main.selectAll('.c3-xgrid-lines .c3-xgrid-line'), + expectedX1s = [202, 397, 593]; + lines.each(function (d, i) { + var x1 = d3.select(this).select('line').attr('x1'); + expect(x1).to.be.closeTo(expectedX1s[i], -2); + }); + }); + + it('should locate grid texts properly', function () { + var lines = chart.internal.main.selectAll('.c3-xgrid-lines .c3-xgrid-line'), + expectedPositions = ['start', 'middle', 'end'], + expectedDxs = [4, 0, -4]; + lines.each(function (d, i) { + var text = d3.select(this).select('text'), + textAnchor = text.attr('text-anchor'), + dx = text.attr('dx'); + expect(textAnchor).to.equal(expectedPositions[i]); + expect(+dx).to.equal(expectedDxs[i]); + }); + }); + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400], + ] + }, + axis: { + rotated: true + }, + grid: { + x: { + lines: [ + {value: 1, text: 'Label 1', position: 'start'}, + {value: 2, text: 'Label 2', position: 'middle'}, + {value: 3, text: 'Label 3'}, + ] + } + }, + }; + expect(true).to.be.ok; + }); + + it('should show 3 grid lines', function () { + expect(chart.internal.main.selectAll('.c3-xgrid-lines .c3-xgrid-line').size()).to.equal(3); + }); + + it('should locate grid lines properly', function () { + var lines = chart.internal.main.selectAll('.c3-xgrid-lines .c3-xgrid-line'), + expectedY1s = [144, 283, 421]; + lines.each(function (d, i) { + var y1 = d3.select(this).select('line').attr('y1'); + expect(y1).to.be.closeTo(expectedY1s[i], -2); + }); + }); + + it('should locate grid texts properly', function () { + var lines = chart.internal.main.selectAll('.c3-xgrid-lines .c3-xgrid-line'), + expectedPositions = ['start', 'middle', 'end'], + expectedDxs = [4, 0, -4]; + lines.each(function (d, i) { + var text = d3.select(this).select('text'), + textAnchor = text.attr('text-anchor'), + dx = text.attr('dx'); + expect(textAnchor).to.equal(expectedPositions[i]); + expect(+dx).to.equal(expectedDxs[i]); + }); + }); + + }); + + describe('with padding.top', function () { + + it('should have correct height', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400], + ] + }, + grid: { + x: { + lines: [ + {value: 3, text: 'Label 3'} + ] + } + }, + padding: { + top: 50 + } + }; + expect(true).to.be.ok; + }); + + it('should show x grid lines', function () { + var lines = chart.internal.main.select('.c3-xgrid-lines .c3-xgrid-line'), + expectedX1 = 593, + expectedText = ['Label 3']; + lines.each(function (id, i) { + var line = d3.select(this), + l = line.select('line'), + t = line.select('text'); + expect(+l.attr('x1')).to.be.closeTo(expectedX1, -2); + expect(t.text()).to.equal(expectedText[i]); + }); + }); + + }); + + describe('on category axis', function () { + + it('should update args', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', 'a', 'b', 'c', 'd'], + ['data1', 30, 200, 100, 400], + ] + }, + axis: { + x: { + type: 'category' + } + }, + grid: { + x: { + lines: [ + {value: 3, text: 'Label 3'}, + {value: 'a', text: 'Label a'} + ] + } + } + }; + expect(true).to.be.ok; + }); + + it('should show x grid lines', function () { + var lines = chart.internal.main.selectAll('.c3-xgrid-lines .c3-xgrid-line'), + expectedX1 = [515, 74], + expectedText = ['Label 3', 'Label a']; + lines.each(function (id, i) { + var line = d3.select(this), + l = line.select('line'), + t = line.select('text'); + expect(+l.attr('x1')).to.be.closeTo(expectedX1[i], -2); + expect(t.text()).to.equal(expectedText[i]); + }); + }); + + }); + }); + +}); diff --git a/spec-mocha/interaction-spec.js b/spec-mocha/interaction-spec.js new file mode 100644 index 0000000..14c97d3 --- /dev/null +++ b/spec-mocha/interaction-spec.js @@ -0,0 +1,118 @@ +var expect = require('chai').expect; + +describe('c3 chart interaction', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('generate event rects', function () { + + describe('custom x', function () { + + it('should generate bar chart', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', 0, 1000, 3000, 10000], + ['data', 10, 10, 10, 10] + ], + type: 'bar' + } + }; + expect(true).to.be.ok; + }); + + it('should have 4 event rects properly', function () { + var lefts = [78, 138, 205.5, 407.5], + widths = [60, 67.5, 202, 194]; + d3.selectAll('.c3-event-rect').each(function (d, i) { + var box = d3.select(this).node().getBoundingClientRect(); + expect(box.left).to.be.closeTo(lefts[i], -2); + expect(box.width).to.be.closeTo(widths[i], -2); + }); + }); + + it('should generate bar chart with only one data', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', 0], + ['data', 10] + ], + type: 'bar' + } + }; + expect(true).to.be.ok; + }); + + it('should have 1 event rects properly', function () { + var eventRects = d3.selectAll('.c3-event-rect'); + expect(eventRects.size()).to.equal(1); + eventRects.each(function () { + var box = d3.select(this).node().getBoundingClientRect(); + expect(box.left).to.be.closeTo(40.5, -2); + expect(box.width).to.be.closeTo(598, -2); + }); + }); + }); + + describe('timeseries', function () { + + it('should generate line chart with timeseries', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', '20140101', '20140201', '20140210', '20140301'], + ['data', 10, 10, 10, 10] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should have 4 event rects properly', function () { + var lefts = [43.5, 193, 353, 500], + widths = [149.5, 160, 147, 136]; + d3.selectAll('.c3-event-rect').each(function (d, i) { + var box = d3.select(this).node().getBoundingClientRect(); + expect(box.left).to.be.closeTo(lefts[i], -2); + expect(box.width).to.be.closeTo(widths[i], -2); + }); + + }); + + it('should generate line chart with only 1 data timeseries', function () { + args = { + data: { + x: 'x', + columns: [ + ['x', '20140101'], + ['data', 10] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should have 1 event rects properly', function () { + var eventRects = d3.selectAll('.c3-event-rect'); + expect(eventRects.size()).to.equal(1); + eventRects.each(function () { + var box = d3.select(this).node().getBoundingClientRect(); + expect(box.left).to.be.closeTo(40.5, -2); + expect(box.width).to.be.closeTo(598, -2); + }); + }); + + }); + + }); + +}); diff --git a/spec-mocha/legend-spec.js b/spec-mocha/legend-spec.js new file mode 100644 index 0000000..ca7726b --- /dev/null +++ b/spec-mocha/legend-spec.js @@ -0,0 +1,280 @@ +var expect = require('chai').expect; + +describe('c3 chart legend', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('legend when multiple charts rendered', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30], + ['data2', 50], + ['data3', 100] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should update args with long data names', function () { + args = { + data: { + columns: [ + ['long data name 1', 30], + ['long data name 2', 50], + ['long data name 3', 50], + ] + } + }; + expect(true).to.be.ok; + }); + + it('should have properly computed legend width', function () { + var expectedLeft = [148, 226, 384], + expectedWidth = [118, 118, 108]; + d3.selectAll('.c3-legend-item').each(function (d, i) { + var rect = d3.select(this).node().getBoundingClientRect(); + expect(rect.left).to.be.closeTo(expectedLeft[i], -2); + expect(rect.width).to.be.closeTo(expectedWidth[i], -2); + }); + }); + }); + + describe('legend position', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ] + } + }; + expect(true).to.be.ok; + }); + + it('should be located on the center of chart', function () { + var box = chart.internal.legend.node().getBoundingClientRect(); + expect(box.left + box.right).to.equal(640); + }); + + }); + + describe('legend as inset', function () { + + it('should change the legend to "inset" successfully', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25] + ] + }, + legend: { + position: 'inset', + inset: { + step: null + } + } + }; + expect(true).to.be.ok; + }); + + it('should be positioned properly', function () { + var box = d3.select('.c3-legend-background').node().getBoundingClientRect(); + expect(box.top).to.equal(5.5); + expect(box.left).to.be.above(30); + }); + + it('should have automatically calculated height', function () { + var box = d3.select('.c3-legend-background').node().getBoundingClientRect(); + expect(box.height).to.equal(48); + }); + + it('should change the legend step to 1 successfully', function () { + args.legend.inset.step = 1; + expect(true).to.be.ok; + }); + + it('should have automatically calculated height', function () { + var box = d3.select('.c3-legend-background').node().getBoundingClientRect(); + expect(box.height).to.equal(28); + }); + + it('should change the legend step to 2 successfully', function () { + args.legend.inset.step = 2; + expect(true).to.be.ok; + }); + + it('should have automatically calculated height', function () { + var box = d3.select('.c3-legend-background').node().getBoundingClientRect(); + expect(box.height).to.equal(48); + }); + + it('should update args to have only one series', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ] + }, + legend: { + position: 'inset' + } + }; + expect(true).to.be.ok; + }); + + it('should locate legend properly', function () { + var box = d3.select('.c3-legend-background').node().getBoundingClientRect(); + expect(box.height).to.equal(28); + expect(box.width).to.be.above(64); + }); + + }); + + describe('legend.hide', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 130, 100, 200, 100, 250, 150] + ] + }, + legend: { + hide: true + } + }; + expect(true).to.be.ok; + }); + + it('should not show legends', function () { + d3.selectAll('.c3-legend-item').each(function () { + expect(d3.select(this).style('visibility')).to.equal('hidden'); + }); + }); + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 130, 100, 200, 100, 250, 150] + ] + }, + legend: { + hide: 'data2' + } + }; + expect(true).to.be.ok; + }); + + it('should not show legends', function () { + expect(d3.select('.c3-legend-item-data1').style('visibility')).to.equal('visible'); + expect(d3.select('.c3-legend-item-data2').style('visibility')).to.equal('hidden'); + }); + + }); + + describe('legend.show', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 130, 100, 200, 100, 250, 150] + ] + }, + legend: { + show: false + } + }; + expect(true).to.be.ok; + }); + + it('should not initially have rendered any legend items', function () { + expect(d3.selectAll('.c3-legend-item').empty()).to.equal(true); + }); + + it('allows us to show the legend on showLegend call', function () { + chart.legend.show(); + d3.selectAll('.c3-legend-item').each(function () { + expect(d3.select(this).style('visibility')).to.equal('visible'); + // This selects all the children, but we expect it to be empty + expect(d3.select(this).selectAll("*").length).not.toEqual(0); + }); + }); + + }); + + describe('custom legend size', function() { + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 130, 100, 200, 100, 250, 150] + ] + }, + legend: { + item: { + tile: { + width: 15, + height: 2 + } + } + } + }; + expect(true).to.be.ok; + }); + + it('renders the legend item with the correct width and height', function () { + d3.selectAll('.c3-legend-item-tile').each(function () { + expect(d3.select(this).style('stroke-width')).to.equal(args.legend.item.tile.height + 'px'); + var tileWidth = d3.select(this).attr('x2') - d3.select(this).attr('x1'); + expect(tileWidth).to.equal(args.legend.item.tile.width); + }); + }); + }); + + describe('custom legend padding', function() { + it('should update args', function () { + args = { + data: { + columns: [ + ['padded1', 30, 200, 100, 400, 150, 250], + ['padded2', 130, 100, 200, 100, 250, 150] + ] + }, + legend: { + padding: 10 + } + }; + expect(true).to.be.ok; + }); + + it('renders the correct amount of padding on the legend element', function () { + d3.selectAll('.c3-legend-item-padded1 .c3-legend-item-tile, .c3-legend-item-padded2 .c3-legend-item-tile').each(function (el, index) { + var itemWidth = d3.select(this).node().parentNode.getBBox().width, + textBoxWidth = d3.select(d3.select(this).node().parentNode).select('text').node().getBBox().width, + tileWidth = 15, // default value is 10, plus 5 more for padding + expectedWidth = textBoxWidth + tileWidth + (index ? 0 : 10) + args.legend.padding; + + expect(itemWidth).to.equal(expectedWidth); + }); + }); + }); + +}); diff --git a/spec-mocha/shape.bar-spec.js b/spec-mocha/shape.bar-spec.js new file mode 100644 index 0000000..cfd4f1f --- /dev/null +++ b/spec-mocha/shape.bar-spec.js @@ -0,0 +1,183 @@ +var expect = require('chai').expect; + +var setMouseEvent = window.setMouseEvent; + +describe('c3 chart shape bar', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('with groups', function () { + + describe('with indexed data', function () { + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, -100, 400, -150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ], + groups: [ + ['data1', 'data2'], + ], + type: 'bar' + }, + }; + expect(true).to.be.ok; + }); + it('should be stacked', function () { + var expectedBottom = [275, 293, 365, 281, 395, 290]; + chart.internal.main.selectAll('.c3-bars-data1 .c3-bar').each(function (d, i) { + var rect = d3.select(this).node().getBoundingClientRect(); + expect(rect.bottom).to.be.closeTo(expectedBottom[i], -1); + }); + }); + }); + + describe('with timeseries data', function () { + it('should update args', function () { + args = { + data: { + x: 'date', + columns: [ + ['date', '2012-12-24', '2012-12-25', '2012-12-26', '2012-12-27', '2012-12-28', '2012-12-29'], + ['data1', 30, 200, -100, 400, -150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ], + groups: [ + ['data1', 'data2'], + ], + type: 'bar' + }, + axis: { + x: { + type: 'timeseries', + } + } + }; + expect(true).to.be.ok; + }); + it('should be stacked', function () { + var expectedBottom = [275, 293, 365, 281, 395, 290]; + chart.internal.main.selectAll('.c3-bars-data1 .c3-bar').each(function (d, i) { + var rect = d3.select(this).node().getBoundingClientRect(); + expect(rect.bottom).to.be.closeTo(expectedBottom[i], -1); + }); + }); + }); + + describe('with category data', function () { + it('should update args', function () { + args = { + data: { + x: 'date', + columns: [ + ['date', '2012-12-24', '2012-12-25', '2012-12-26', '2012-12-27', '2012-12-28', '2012-12-29'], + ['data1', 30, 200, -100, 400, -150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ], + groups: [ + ['data1', 'data2'], + ], + type: 'bar' + }, + axis: { + x: { + type: 'category', + } + } + }; + expect(true).to.be.ok; + }); + it('should be stacked', function () { + var expectedBottom = [275, 293, 365, 281, 395, 290]; + chart.internal.main.selectAll('.c3-bars-data1 .c3-bar').each(function (d, i) { + var rect = d3.select(this).node().getBoundingClientRect(); + expect(rect.bottom).to.be.closeTo(expectedBottom[i], -1); + }); + }); + }); + + }); + + describe('internal.isWithinBar', function () { + + describe('with normal axis', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, -150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', -150, 120, 110, 140, 115, 125] + ], + type: 'bar' + }, + axis: { + rotated: false + } + }; + expect(true).to.be.ok; + }); + + it('should not be within bar', function () { + var bar = d3.select('.c3-target-data1 .c3-bar-0').node(); + setMouseEvent(chart, 'click', 0, 0); + expect(chart.internal.isWithinBar(bar)).to.not.be.ok; + }); + + it('should be within bar', function () { + var bar = d3.select('.c3-target-data1 .c3-bar-0').node(); + setMouseEvent(chart, 'click', 31, 280); + expect(chart.internal.isWithinBar(bar)).to.be.ok; + }); + + it('should not be within bar of negative value', function () { + var bar = d3.select('.c3-target-data3 .c3-bar-0').node(); + setMouseEvent(chart, 'click', 68, 280); + expect(chart.internal.isWithinBar(bar)).to.not.be.ok; + }); + + it('should be within bar of negative value', function () { + var bar = d3.select('.c3-target-data3 .c3-bar-0').node(); + setMouseEvent(chart, 'click', 68, 350); + expect(chart.internal.isWithinBar(bar)).to.be.ok; + }); + + }); + + describe('with rotated axis', function () { + + it('should change the chart as axis rotated', function () { + args.axis.rotated = true; + expect(true).to.be.ok; + }); + + it('should not be within bar', function () { + var bar = d3.select('.c3-target-data1 .c3-bar-0').node(); + setMouseEvent(chart, 'click', 0, 0); + expect(chart.internal.isWithinBar(bar)).to.not.be.ok; + }); + + it('should be within bar', function () { + var bar = d3.select('.c3-target-data1 .c3-bar-0').node(); + setMouseEvent(chart, 'click', 190, 20); + expect(chart.internal.isWithinBar(bar)).to.be.ok; + }); + + it('should be within bar of negative value', function () { + var bar = d3.select('.c3-target-data3 .c3-bar-0').node(); + setMouseEvent(chart, 'click', 68, 50); + expect(chart.internal.isWithinBar(bar)).to.be.ok; + }); + + }); + + }); + +}); diff --git a/spec-mocha/shape.line-spec.js b/spec-mocha/shape.line-spec.js new file mode 100644 index 0000000..e2a3033 --- /dev/null +++ b/spec-mocha/shape.line-spec.js @@ -0,0 +1,172 @@ +var expect = require('chai').expect; + +describe('c3 chart shape line', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + var parseSvgPath = window.parseSvgPath; + + describe('shape-rendering for line chart', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, -150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', -150, 120, 110, 140, 115, 125] + ], + type: 'line' + } + }; + expect(true).to.be.ok; + + }); + + it("Should render the lines correctly", function(done) { + setTimeout(function () { + var target = chart.internal.main.select('.c3-chart-line.c3-target-data1'); + var commands = parseSvgPath( target.select('.c3-line-data1').attr('d')); + expect(commands.length).to.equal(6); + done(); + }, 500); + }); + + it("should not have shape-rendering when it's line chart", function () { + d3.selectAll('.c3-line').each(function () { + var style = d3.select(this).style('shape-rendering'); + expect(style).to.equal('auto'); + }); + }); + + it('should change to step chart', function () { + args.data.type = 'step'; + expect(true).to.be.ok; + }); + + it("should have shape-rendering = crispedges when it's step chart", function () { + d3.selectAll('.c3-line').each(function () { + var style = d3.select(this).style('shape-rendering').toLowerCase(); + expect(style).to.equal('crispedges'); + }); + }); + + it('should change to spline chart', function () { + args.data.type = 'spline'; + expect(true).to.be.ok; + }); + + it('should use cardinal interpolation by default', function () { + expect(chart.internal.config.spline_interpolation_type).to.equal('cardinal'); + }); + + }); + + describe('point.show option', function () { + + it('should change args to include null data', function () { + args = { + data: { + columns: [ + ['data1', 30, null, 100, 400, -150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', -150, 120, 110, 140, 115, 125] + ], + type: 'line' + } + }; + expect(true).to.be.ok; + }); + + it('should not show the circle for null', function (done) { + setTimeout(function () { + var target = chart.internal.main.select('.c3-chart-line.c3-target-data1'); + expect(+target.select('.c3-circle-0').style('opacity')).to.equal(1); + expect(+target.select('.c3-circle-1').style('opacity')).to.equal(0); + expect(+target.select('.c3-circle-2').style('opacity')).to.equal(1); + done(); + }, 500); + }); + + it('should not draw a line segment for null data', function(done) { + setTimeout(function () { + var target = chart.internal.main.select('.c3-chart-line.c3-target-data1'); + var commands = parseSvgPath( target.select('.c3-line-data1').attr('d')); + var segments = 0; + for(var i = 0; i < commands.length; i++) { + (commands[i].command === 'L') ? segments++ : null; + } + expect(segments).to.equal(3); + done(); + }, 500); + }); + + // it('should change args to include null data on scatter plot', function () { + // args = { + // data: { + // columns: [ + // ['data1', 30, null, 100, 400, -150, 250], + // ['data2', 50, 20, 10, 40, 15, 25], + // ['data3', -150, 120, 110, 140, 115, 125] + // ], + // type: 'scatter' + // } + // }; + // expect(true).to.be.ok; + // }); + + // it('should not show the circle for null', function (done) { + // setTimeout(function () { + // var target = chart.internal.main.select('.c3-chart-line.c3-target-data1'); + // expect(+target.select('.c3-circle-0').style('opacity')).to.equal(0.5); + // expect(+target.select('.c3-circle-1').style('opacity')).to.equal(0); + // expect(+target.select('.c3-circle-2').style('opacity')).to.equal(0.5); + // done(); + // }, 500); + // }); + + }); + + describe('spline.interpolation option', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, -150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', -150, 120, 110, 140, 115, 125] + ], + type: 'spline' + }, + spline: { + interpolation: { + type: 'monotone' + } + } + }; + + expect(true).to.be.ok; + }); + + it('should update interpolation function', function() { + expect(chart.internal.getInterpolate(chart.data()[0])).to.equal('monotone'); + }); + + it('should not use a non-valid interpolation', function () { + args.spline.interpolation.type = 'foo'; + expect(true).to.be.ok; + }); + + it('should use cardinal interpolation when given option is not valid', function() { + expect(chart.internal.getInterpolate(chart.data()[0])).to.equal('cardinal'); + }); + + }); + +}); diff --git a/spec-mocha/svg-helper.js b/spec-mocha/svg-helper.js new file mode 100644 index 0000000..251fe47 --- /dev/null +++ b/spec-mocha/svg-helper.js @@ -0,0 +1,50 @@ +/** + * Parse the d property of an SVG path into an array of drawing commands. + * @param {String} d SvgPath d attribute.] + * @return {Array} an array of drawing commands. + */ + +function parseSvgPath(d) { //jshint ignore:line + + + var commands = []; + var commandTokens = ['M','L','I','H','V','C','S','Q','T','A']; + var command; + var in_x = false; + var in_y = false; + var x = ''; + var y = ''; + for(var i = 0; i <= d.length; i++) { + if (commandTokens.indexOf(d[i]) !== -1) { + if (in_x || in_y) { + commands.push({command: command, x: x, y: y}); + x = ''; + y = ''; + } + command = d[i]; + in_x = true; + in_y = false; + } + else { + if (d[i] === ',') { + if (in_y) { + commands.push({command: command, x: x, y: y}); + x = ''; + y = ''; + } + in_x = !in_x; + in_y = !in_y; + } + else if (in_x) { + x += d[i]; + } + else if (in_y) { + y += d[i]; + } + } + } + if (d[i] !== ',' && in_y) { + commands.push({command: command, x: x, y: y}); + } + return commands; +} diff --git a/spec-mocha/title-spec.js b/spec-mocha/title-spec.js new file mode 100644 index 0000000..8b6f91d --- /dev/null +++ b/spec-mocha/title-spec.js @@ -0,0 +1,100 @@ +var expect = require('chai').expect; + +describe('c3 chart title', function () { + + + var chart, config; + + describe('when given a title config option', function () { + describe('with no padding and no position', function () { + beforeEach(function(done) { + config = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250] + ] + }, + title: { + text: 'new title' + } + }; + chart = window.initChart(chart, config, done); + }); + + it('renders the title at the default config position', function () { + var titleEl = d3.select(".c3-title"); + expect(+titleEl.attr("x")).to.be.closeTo(294, -2); + expect(+titleEl.attr("y")).toEqual(titleEl.node().getBBox().height); + }); + + it('renders the title text', function () { + var titleEl = d3.select(".c3-title"); + expect(titleEl.node().textContent).toEqual('new title'); + }); + }); + + describe('with padding', function () { + var config, getConfig = function (titlePosition) { + return { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250] + ] + }, + title: { + text: 'positioned title', + padding: { + top: 20, + right: 30, + bottom: 40, + left: 50 + }, + position: titlePosition + } + }; + }; + + describe('and position center', function () { + beforeEach(function(done) { + config = getConfig('top-center'); + chart = window.initChart(chart, config, done); + }); + it('renders the title at the default config position', function () { + var titleEl = d3.select(".c3-title"); + expect(+titleEl.attr("x")).to.be.closeTo(275, -2); + expect(+titleEl.attr("y")).to.be.closeTo(34, -1); + }); + it('adds the correct amount of padding to fit the title', function() { + expect(chart.internal.getCurrentPaddingTop()).toEqual( + config.title.padding.top + d3.select('.c3-title').node().getBBox().height + config.title.padding.bottom + ); + }); + }); + + describe('and position left', function () { + beforeEach(function(done) { + config = getConfig('top-left'); + chart = window.initChart(chart, config, done); + }); + it('renders the title at the default config position', function () { + var titleEl = d3.select(".c3-title"); + expect(+titleEl.attr("x")).to.be.closeTo(50, -1); + expect(+titleEl.attr("y")).to.be.closeTo(34, -1); + }); + }); + + describe('and position right', function () { + beforeEach(function(done) { + config = getConfig('top-right'); + chart = window.initChart(chart, config, done); + }); + it('renders the title at the default config position', function () { + var titleEl = d3.select(".c3-title"); + expect(+titleEl.attr("x")).to.be.closeTo(520, -2); + expect(+titleEl.attr("y")).to.be.closeTo(34, -1); + }); + }); + + }); + }); +}); diff --git a/spec-mocha/tooltip-spec.js b/spec-mocha/tooltip-spec.js new file mode 100644 index 0000000..abe5f66 --- /dev/null +++ b/spec-mocha/tooltip-spec.js @@ -0,0 +1,124 @@ +var expect = require('chai').expect; + +describe('c3 chart tooltip', function () { + + + var chart; + var tooltipConfiguration; + + var args = function () { + return { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ], + }, + tooltip: tooltipConfiguration + }; + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args(), done); + }); + + describe('tooltip position', function () { + beforeAll(function () { + tooltipConfiguration = {}; + }); + + describe('without left margin', function () { + + it('should show tooltip on proper position', function () { + var eventRect = d3.select('.c3-event-rect-2').node(); + window.setMouseEvent(chart, 'mousemove', 100, 100, eventRect); + + var tooltipContainer = d3.select('.c3-tooltip-container'), + top = Math.floor(+tooltipContainer.style('top').replace(/px/, '')), + left = Math.floor(+tooltipContainer.style('left').replace(/px/, '')), + topExpected = 115, + leftExpected = 280; + expect(top).to.equal(topExpected); + expect(left).to.be.above(leftExpected); + }); + + }); + + describe('with left margin', function () { + + it('should set left margin', function () { + d3.select('#chart').style('margin-left', '300px'); + expect(true).to.be.ok; + }); + + it('should show tooltip on proper position', function () { + var eventRect = d3.select('.c3-event-rect-2').node(); + window.setMouseEvent(chart, 'mousemove', 100, 100, eventRect); + + var tooltipContainer = d3.select('.c3-tooltip-container'), + top = Math.floor(+tooltipContainer.style('top').replace(/px/, '')), + left = Math.floor(+tooltipContainer.style('left').replace(/px/, '')), + topExpected = 115, + leftExpected = 280; + expect(top).to.equal(topExpected); + expect(left).to.be.above(leftExpected); + }); + + }); + + }); + + describe('tooltip positionFunction', function () { + var topExpected = 37, leftExpected = 79; + + beforeAll(function () { + tooltipConfiguration = { + position: function (data, width, height, element) { + expect(data.length).to.equal(args().data.columns.length); + expect(data[0]).toEqual(jasmine.objectContaining({ + index: 2, + value: 100, + id: 'data1' + })); + expect(width).to.be.above(0); + expect(height).to.be.above(0); + expect(element).to.equal(d3.select('.c3-event-rect-2').node()); + return {top: topExpected, left: leftExpected}; + } + }; + }); + + it('should be set to the coordinate where the function returned', function () { + var eventRect = d3.select('.c3-event-rect-2').node(); + window.setMouseEvent(chart, 'mousemove', 100, 100, eventRect); + + var tooltipContainer = d3.select('.c3-tooltip-container'), + top = Math.floor(+tooltipContainer.style('top').replace(/px/, '')), + left = Math.floor(+tooltipContainer.style('left').replace(/px/, '')); + expect(top).to.equal(topExpected); + expect(left).to.equal(leftExpected); + }); + }); + + describe('tooltip getTooltipContent', function () { + beforeAll(function () { + tooltipConfiguration = { + data_order: 'desc' + }; + }); + + it('should sort values desc', function () { + var eventRect = d3.select('.c3-event-rect-2').node(); + window.setMouseEvent(chart, 'mousemove', 100, 100, eventRect); + + var tooltipTable = d3.select('.c3-tooltip')[0]; + var expected = ["", "c3-tooltip-name--data3", + "c3-tooltip-name--data1", "c3-tooltip-name--data2"]; + var i; + for (i = 0; i < tooltipTable[0].rows.length; i++) { + expect(tooltipTable[0].rows[i].className).to.equal(expected[i]); + } + }); + }); +}); diff --git a/spec-mocha/type-spec.js b/spec-mocha/type-spec.js new file mode 100644 index 0000000..7c8e783 --- /dev/null +++ b/spec-mocha/type-spec.js @@ -0,0 +1,139 @@ +var expect = require('chai').expect; + +describe('c3 chart types', function () { + + + var chart, args; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('internal.hasArcType', function () { + + describe('with data', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ], + type: 'pie' + } + }; + expect(true).to.be.ok; + }); + + it('should return true', function () { + expect(chart.internal.hasArcType()).to.be.ok; + }); + + it('should change chart type to "bar"', function () { + args.data.type = 'bar'; + expect(true).to.be.ok; + }); + + it('should return false', function () { + expect(chart.internal.hasArcType()).to.not.be.ok; + }); + + }); + + describe('with empty data', function () { + + it('should update args to have empty data', function () { + args = { + data: { + columns: [], + type: 'pie' + } + }; + expect(true).to.be.ok; + }); + + it('should return true', function () { + expect(chart.internal.hasArcType()).to.be.ok; + }); + + it('should change chart type to "bar"', function () { + args.data.type = 'bar'; + expect(true).to.be.ok; + }); + + it('should return false', function () { + expect(chart.internal.hasArcType()).to.not.be.ok; + }); + + }); + + }); + + describe('internal.hasType', function () { + + it('should update args', function () { + args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 150, 250], + ['data2', 50, 20, 10, 40, 15, 25], + ['data3', 150, 120, 110, 140, 115, 125] + ], + type: 'pie' + } + }; + expect(true).to.be.ok; + }); + + it('should return true for "pie" type', function () { + expect(chart.internal.hasType('pie')).to.be.ok; + }); + + it('should return false for "line" type', function () { + expect(chart.internal.hasType('line')).to.not.be.ok; + }); + + it('should return false for "bar" type', function () { + expect(chart.internal.hasType('bar')).to.not.be.ok; + }); + + it('should unload successfully', function () { + chart.unload([]); + expect(true).to.be.ok; + }); + + it('should return true for "pie" type even if no data', function () { + expect(chart.internal.hasType('pie')).to.be.ok; + }); + + it('should return false for "line" type even if no data', function () { + expect(chart.internal.hasType('line')).to.not.be.ok; + }); + + it('should return false for "bar" type even if no data', function () { + expect(chart.internal.hasType('bar')).to.not.be.ok; + }); + + it('should change chart type to "bar" successfully', function () { + args.data.type = 'bar'; + expect(true).to.be.ok; + }); + + it('should return false for "pie" type even if no data', function () { + expect(chart.internal.hasType('pie')).to.not.be.ok; + }); + + it('should return false for "line" type even if no data', function () { + expect(chart.internal.hasType('line')).to.not.be.ok; + }); + + it('should return true for "bar" type even if no data', function () { + expect(chart.internal.hasType('bar')).to.be.ok; + }); + + + }); + +}); diff --git a/spec-mocha/zoom-spec.js b/spec-mocha/zoom-spec.js new file mode 100644 index 0000000..ed1acd9 --- /dev/null +++ b/spec-mocha/zoom-spec.js @@ -0,0 +1,69 @@ +var expect = require('chai').expect; + +describe('c3 chart zoom', function () { + + + var chart; + + var args = { + data: { + columns: [ + ['data1', 30, 200, 100, 400, 3150, 250], + ['data2', 50, 20, 10, 40, 15, 6025] + ] + }, + axis: { + x: { + extent: [1, 2] + } + }, + zoom: { + enable: true + }, + subchart: { + show: true + } + }; + + beforeEach(function (done) { + chart = window.initChart(chart, args, done); + }); + + describe('default extent', function () { + + describe('main chart domain', function () { + + it('should have original y domain', function () { + var yDomain = chart.internal.y.domain(), + expectedYDomain = [-591.5, 6626.5]; + expect(yDomain[0]).to.equal(expectedYDomain[0]); + expect(yDomain[1]).to.equal(expectedYDomain[1]); + }); + + }); + + describe('main chart domain', function () { + + it('should have original y domain in subchart', function () { + var yDomain = chart.internal.y.domain(), + subYDomain = chart.internal.subY.domain(); + expect(subYDomain[0]).to.equal(yDomain[0]); + expect(subYDomain[1]).to.equal(yDomain[1]); + }); + + }); + + describe('main chart domain', function () { + + it('should have specified brush extent', function () { + var brushExtent = chart.internal.brush.extent(), + expectedBrushExtent = [1, 2]; + expect(brushExtent[0]).to.equal(expectedBrushExtent[0]); + expect(brushExtent[1]).to.equal(expectedBrushExtent[1]); + }); + + }); + + }); + +}); diff --git a/spec/c3-helper.js b/spec/c3-helper.js index cc19bdf..970cd1b 100644 --- a/spec/c3-helper.js +++ b/spec/c3-helper.js @@ -1,5 +1,3 @@ -import * as c3 from '../src/index'; - function initDom() { 'use strict'; @@ -32,9 +30,9 @@ function initChart(chart, args, done) { window.initDom(); } if (args) { - chart = c3.generate(args); - const d3 = chart.internal.d3; - d3.select('.jasmine_html-reporter') + chart = window.c3.generate(args); + window.d3 = chart.internal.d3; + window.d3.select('.jasmine_html-reporter') .style('position', 'absolute') .style('width', '640px') .style('right', 0);