UNPKG

dc

Version:

A multi-dimensional charting library built to work natively with crossfilter and rendered using d3.js

856 lines (820 loc) 36.6 kB
/* global appendChartID, loadDateFixture, makeDate */ describe('dc.pieChart', function () { var width = 200; var height = 200; var radius = 100; var defaultCenter = {x: width / 2, y: height / 2}; var newCenter = {x: 101, y: 99}; var innerRadius = 30; var data, valueDimension, valueGroup; var regionDimension, statusDimension; var countryDimension, countryGroup, dateDimension; var statusGroup, statusMultiGroup; beforeEach(function () { data = crossfilter(loadDateFixture()); valueDimension = data.dimension(function (d) { return d.value; }); valueGroup = valueDimension.group(); regionDimension = data.dimension(function (d) { return d.region; }); statusDimension = data.dimension(function (d) { return d.status; }); countryDimension = data.dimension(function (d) { return d.countrycode; }); countryGroup = countryDimension.group(); dateDimension = data.dimension(function (d) { return d3.utcDay(d.dd); }); statusGroup = statusDimension.group(); statusMultiGroup = statusGroup.reduce( //add function (p, v) { ++p.count; p.total += +v.value; return p; }, //remove function (p, v) { --p.count; p.total -= +v.value; return p; }, //init function () { return {count: 0, total: 0, getTotal: function () { return this.total; }}; } ); }); function buildChart (id) { var div = appendChartID(id); div.append('a').attr('class', 'reset').style('display', 'none'); div.append('span').attr('class', 'filter').style('display', 'none'); var chart = dc.pieChart('#' + id); chart.dimension(valueDimension).group(valueGroup) .width(width) .height(height) .radius(radius) .transitionDuration(0); chart.render(); return chart; } function buildCountryChart (id) { var div = appendChartID(id); div.append('a').attr('class', 'reset').style('display', 'none'); div.append('span').attr('class', 'filter').style('display', 'none'); var chart = dc.pieChart('#' + id); chart.dimension(countryDimension).group(countryGroup) .width(width) .height(height) .radius(radius) .transitionDuration(0); chart.render(); return chart; } describe('generation', function () { var chart, countryChart; beforeEach(function () { chart = buildChart('pie-chart-age'); chart.innerRadius(innerRadius); chart.render(); }); it('we get something', function () { expect(chart).not.toBeNull(); }); it('should be registered', function () { expect(dc.hasChart(chart)).toBeTruthy(); }); it('dc-chart class should be turned on for parent div', function () { expect(d3.select('#pie-chart-age').attr('class')).toEqual('dc-chart'); }); it('inner radius can be set', function () { expect(chart.innerRadius()).toEqual(innerRadius); }); it('svg should be created', function () { expect(chart.select('svg').empty()).toBeFalsy(); }); it('default color scheme should be created', function () { expect(chart.colors().length > 0).toBeTruthy(); }); it('dimension should be set', function () { expect(chart.dimension()).toBe(valueDimension); }); it('group should be set', function () { expect(chart.group()).toEqual(valueGroup); }); it('width should be set', function () { expect(chart.width()).toEqual(width); }); it('height should be set', function () { expect(chart.height()).toEqual(height); }); it('radius should be set', function () { expect(chart.radius()).toEqual(radius); }); it('cx should be set', function () { expect(chart.cx()).toEqual(defaultCenter.x); }); it('cy should be set', function () { expect(chart.cy()).toEqual(defaultCenter.y); }); it('height should be used for svg', function () { expect(chart.select('svg').attr('height')).toEqual(String(height)); }); it('root g should be created', function () { expect(chart.select('svg g').empty()).toBeFalsy(); }); it('root g should be translated to center', function () { expect(chart.select('svg g').attr('transform')).toMatchTranslate(defaultCenter.x, defaultCenter.y); }); it('slice g should be created with class', function () { expect(chart.selectAll('svg g g.pie-slice').data().length).toEqual(5); }); it('slice path should be created', function () { expect(chart.selectAll('svg g g.pie-slice path').data().length).toEqual(5); }); it('slice css class should be numbered with index', function () { chart.selectAll('g.pie-slice').each(function (p, i) { expect(d3.select(this).attr('class')).toEqual('pie-slice _' + i); }); }); it('slice path should be filled', function () { chart.selectAll('svg g g.pie-slice path').each(function (p) { expect(d3.select(this).attr('fill') !== '').toBeTruthy(); }); }); it('slice path d should be created', function () { chart.selectAll('svg g g.pie-slice path').each(function (p) { expect(d3.select(this).attr('d') !== '').toBeTruthy(); }); }); it('slice path fill should be set correctly', function () { var numSlices = 5; for (var i = 0; i < numSlices; i++) { expect(d3.select(chart.selectAll('g.pie-slice path').nodes()[i]).attr('fill')) .toMatchColor(dc.config.defaultColors()[i]); } }); it('slice label should be created', function () { expect(chart.selectAll('svg text.pie-slice').data().length).toEqual(5); }); it('slice label transform to centroid', function () { expect(chart.selectAll('svg g text.pie-slice').attr('transform')) .toMatchTranslate(52.58610463437159, -38.20604139901075, 3); }); it('slice label text should be set', function () { chart.selectAll('svg g text.pie-slice').call(function (p) { expect(p.text()).toEqual(p.datum().data.key); }); }); it('slice label should be middle anchored', function () { chart.selectAll('svg g text.pie-slice').each(function (p) { expect(d3.select(this).attr('text-anchor')).toEqual('middle'); }); }); it('reset link hidden after init rendering', function () { expect(chart.select('a.reset').style('display')).toEqual('none'); }); it('filter printer should be set', function () { expect(chart.filterPrinter()).not.toBeNull(); }); it('filter info should be hidden after init rendering', function () { expect(chart.select('span.filter').style('display')).toEqual('none'); }); describe('center positioning', function () { beforeEach(function () { chart .cx(newCenter.x) .cy(newCenter.y) .render(); return chart; }); afterEach(function () { chart .cx(defaultCenter.x) .cy(defaultCenter.y) .render(); return chart; }); it('root g should be translated to ' + newCenter.x + ',' + newCenter.y, function () { expect(chart.select('svg g').attr('transform')).toMatchTranslate(newCenter.x, newCenter.y); }); }); describe('with radius padding', function () { beforeEach(function () { chart.externalRadiusPadding(17) .render(); return chart; }); it('should not change center', function () { expect(chart.select('svg g').attr('transform')).toMatchTranslate(defaultCenter.x, defaultCenter.y); }); it('should decrease outer radius', function () { expect(chart.select('svg g.pie-slice path').attr('d')).toMatch(/83[, ]83/); // i.e. 100-17 }); }); describe('re-render', function () { beforeEach(function () { chart.render(); return chart; }); it('multiple invocation of render should update chart', function () { expect(d3.selectAll('#pie-chart-age svg').nodes().length).toEqual(1); }); }); describe('filter', function () { beforeEach(function () { regionDimension.filter('East'); chart.render(); }); it('label should be hidden if filtered out', function () { expect(chart.selectAll('svg g text.pie-slice').nodes()[0].textContent).toEqual('22'); expect(chart.selectAll('svg g text.pie-slice').nodes()[1].textContent).toEqual(''); }); afterEach(function () { regionDimension.filterAll(); }); }); describe('n/a filter', function () { beforeEach(function () { statusDimension.filter('E'); chart.render(); return chart; }); it('should draw an empty chart', function () { expect(chart.select('g').classed('empty-chart')).toBeTruthy(); }); it('should have one slice', function () { expect(chart.selectAll('svg g text.pie-slice').nodes().length).toBe(1); }); afterEach(function () { statusDimension.filterAll(); }); }); describe('slice selection', function () { it('on click function should be defined', function () { expect(chart.selectAll('svg g g.pie-slice path').on('click') !== undefined).toBeTruthy(); }); it('by default no slice should be selected', function () { expect(chart.hasFilter()).toBeFalsy(); }); it('be able to set selected slice', function () { expect(chart.filter('66').filter()).toEqual('66'); expect(chart.hasFilter()).toBeTruthy(); chart.filterAll(); }); it('should filter dimension by single selection', function () { chart.filter('22'); expect(countryGroup.all()[0].value).toEqual(1); expect(countryGroup.all()[1].value).toEqual(1); chart.filterAll(); }); it('should filter dimension by multiple selections', function () { chart.filter('66'); chart.filter('22'); expect(countryGroup.all()[0].value).toEqual(1); expect(countryGroup.all()[1].value).toEqual(2); chart.filterAll(); }); it('should filter dimension with deselection', function () { chart.filter('22'); chart.filter('66'); chart.filter('22'); expect(countryGroup.all()[0].value).toEqual(0); expect(countryGroup.all()[1].value).toEqual(1); chart.filterAll(); }); it('should highlight selected slices', function () { chart.filter('66'); chart.filter('22'); chart.render(); chart.selectAll('g.pie-slice').each(function (d) { if (d.data.key === '66' || d.data.key === '22') { expect(d3.select(this).attr('class').indexOf('selected') > 0).toBeTruthy(); } else { expect(d3.select(this).attr('class').indexOf('deselected') > 0).toBeTruthy(); } }); chart.filterAll(); }); it('reset link shown after slice selection', function () { chart.filter('66'); expect(chart.select('a.reset').style('display')).not.toEqual('none'); }); it('filter info shown after slice selection', function () { chart.filter(null); chart.filter('66'); expect(chart.select('span.filter').style('display')).not.toEqual('none'); expect(chart.select('span.filter').text()).toEqual('66'); }); it('should remove highlight if no slice selected', function () { chart.filterAll(); chart.redraw(); chart.selectAll('.pie-slice path').each(function (d) { var cls = d3.select(this).attr('class'); expect(cls === null || cls === '').toBeTruthy(); }); }); }); describe('filter through clicking', function () { it('onClick should trigger filtering of according group', function () { chart.onClick(chart.group().all()[0]); expect(chart.filter()).toEqual('22'); }); it('onClick should reset filter if clicked twice', function () { chart.onClick(chart.group().all()[0]); chart.onClick(chart.group().all()[0]); expect(chart.filter()).toEqual(null); }); it('multiple onClick should trigger filtering of according groups', function () { chart.onClick(chart.group().all()[0]); chart.onClick(chart.group().all()[1]); expect(chart.hasFilter('22')).toBeTruthy(); expect(chart.hasFilter('33')).toBeTruthy(); }); }); describe('group order with capping', function () { beforeEach(function () { chart.cap(4); }); // group.all starts with 22 -> 2, 33 -> 2, 44 -> 3, 55 -> 2, 66 -> 1 describe('with usual top->bottom sorting and cap', function () { beforeEach(function () { chart.cap(4).ordering(function (kv) { return -kv.value; }).redraw(); }); it('should show top 4 groups and others', function () { // crossfilter's quicksort is stable for < 32 elements, so the value:2's are still in alphabetical order expect(['44', '22', '33', '55', 'Others']).toEqual(chart.data().map(dc.pluck('key'))); }); }); describe('with key ordering', function () { beforeEach(function () { chart .ordering(dc.pluck('key')) .redraw(); }); it('should show lowest 4 groups by key and others', function () { expect(['22', '33', '44', '55', 'Others']).toEqual(chart.data().map(dc.pluck('key'))); }); }); }); describe('comparing crossfilter and chart ordering', function () { var crossfilterOrder, crossfilterTop2; beforeEach(function () { countryChart = buildCountryChart('country-chart'); countryChart.innerRadius(innerRadius); // group.all returns array sorted in ascending key order. // [{"key":"CA","value":2},{"key":"US","value":8}] crossfilterOrder = countryGroup.all(); // group.top returns array sorted in descending value order // [{"key":"US","value":8}] crossfilterTop2 = countryGroup.top(2); }); describe('with ordering and capping not set', function () { it('should match the crossfilter top 2', function () { expect(countryChart.data()).toEqual(crossfilterTop2); }); }); describe('with ordering by key', function () { beforeEach(function () { countryChart.ordering(function (kv) { return kv.key; }).redraw(); }); it('should should match crossfilter top(2)', function () { expect(countryChart.data()).toEqual(crossfilterOrder); }); describe('with cap(1)', function () { beforeEach(function () { countryChart.cap(1).redraw(); }); it('should show the top value, and others', function () { expect(countryChart.data().map(dc.pluck('key'))).toEqual(['CA', 'Others']); }); }); }); describe('with default ordering and cap(1)', function () { beforeEach(function () { countryChart.cap(1).redraw(); }); it('should show the largest value\'s key, and others', function () { expect(['US', 'Others']).toEqual(countryChart.data().map(dc.pluck('key'))); }); describe('and takeFront(false)', function () { beforeEach(function () { countryChart.takeFront(false).redraw(); }); it('should show the smallest value\'s key, and others', function () { expect(['CA','Others']).toEqual(countryChart.data().map(dc.pluck('key'))); }); }); }); }); }); describe('redraw after empty selection', function () { var chart; beforeEach(function () { chart = buildChart('pie-chart2'); dateDimension.filter([makeDate(2010, 0, 1), makeDate(2010, 0, 3)]); chart.redraw(); dateDimension.filter([makeDate(2012, 0, 1), makeDate(2012, 11, 30)]); chart.redraw(); }); it('pie chart should be restored', function () { chart.selectAll('g.pie-slice path').each(function (p) { expect(d3.select(this).attr('d').indexOf('NaN') < 0).toBeTruthy(); }); }); afterEach(function () { dateDimension.filterAll(); }); }); describe('small slices', function () { var chart; beforeEach(function () { chart = buildChart('pie-chart3'); chart.minAngleForLabel(1) .renderTitle(true); chart.render(); }); it('label should not be generated if the slice is too small', function () { // slice '66' expect(d3.select(chart.selectAll('text.pie-slice').nodes()[4]).text()).toEqual(''); }); describe('selected', function () { beforeEach(function () { chart.filter('66').redraw(); }); it('a small slice should be labelled if it is selected', function () { expect(d3.select(chart.selectAll('text.pie-slice').nodes()[4]).text()).toEqual('66'); }); afterEach(function () { chart.filter(null); }); }); }); describe('custom label & title generation', function () { var chart; beforeEach(function () { chart = buildChart('pie-chart3'); chart.label(function (d) { return 'custom'; }) .title(function (d) { return 'custom'; }) .minAngleForLabel(1) .renderTitle(true); chart.render(); }); it('should render correct number of text', function () { expect(chart.selectAll('text.pie-slice').nodes().length).toEqual(5); }); it('custom function should be used to dynamically generate label', function () { expect(d3.select(chart.selectAll('text.pie-slice').nodes()[0]).text()).toEqual('custom'); }); it('label should not be generated if the slice is too small', function () { // slice '66' expect(d3.select(chart.selectAll('text.pie-slice').nodes()[4]).text()).toEqual(''); }); it('should render correct number of title', function () { expect(chart.selectAll('g.pie-slice title').nodes().length).toEqual(5); }); it('custom function should be used to dynamically generate title', function () { chart.selectAll('g.pie-slice title').each(function (p) { expect(d3.select(this).text()).toEqual('custom'); }); }); }); describe('pie chart slices cap and group switching', function () { // again, group.all starts with 22 -> 2, 33 -> 2, 44 -> 3, 55 -> 2, 66 -> 1 var chart; beforeEach(function () { chart = buildChart('pie-chart4'); chart.slicesCap(2) .renderTitle(true) .othersLabel('small'); chart.render(); }); describe('with normal valueAccessor and descending value ordering', function () { beforeEach(function () { chart.dimension(valueDimension).group(valueGroup) .valueAccessor(dc.pluck('value')) .ordering(function (kv) { return -kv.value; }) .render(); }); it('produce expected number of slices', function () { expect(chart.selectAll('text.pie-slice').nodes().length).toEqual(3); }); it('others slice should use custom name', function () { expect(d3.select(chart.selectAll('text.pie-slice').nodes()[2]).text()).toEqual('small'); }); it('remaining slices should be in descending value order', function () { expect(chart.selectAll('text.pie-slice').data().map(dc.pluck('value'))) .toEqual([3,2,5]); }); describe('clicking others slice', function () { var event; beforeEach(function () { event = document.createEvent('MouseEvents'); event.initEvent('click', true, true); chart.selectAll('.pie-slice path').nodes()[2].dispatchEvent(event); }); it('should filter three smallest', function () { expect(chart.filters()).toEqual(['33', '55', '66','small']); }); describe('clicking again', function () { beforeEach(function () { chart.selectAll('.pie-slice path').nodes()[2].dispatchEvent(event); }); it('should reset filter', function () { expect(chart.filters()).toEqual([]); }); }); }); }); describe('with custom valueAccessor', function () { // statusMultiGroup has // [{"key":"F","value":{"count":5,"total":220}},{"key":"T","value":{"count":5,"total":198}}] beforeEach(function () { chart.dimension(statusDimension).group(statusMultiGroup) .valueAccessor(function (d) { return d.value.total; }) .ordering(function (d) { return -d.value.total; }) .render(); return chart; }); it('correct values, no others slice', function () { expect(chart.selectAll('g.pie-slice').data().map(dc.pluck('value'))) .toEqual([220, 198]); }); describe('with cap(1)', function () { beforeEach(function () { chart.cap(1).render(); }); it('correct values, others slice', function () { expect(chart.selectAll('title').nodes().map(function (t) {return d3.select(t).text();})) .toEqual(['F: 220', 'small: 198']); }); }); }); describe('with custom valueAccessor calling function', function () { // statusMultiGroup has // [{"key":"F","value":{"count":5,"total":220}},{"key":"T","value":{"count":5,"total":198}}] beforeEach(function () { chart.dimension(statusDimension).group(statusMultiGroup) .valueAccessor(function (d) { return d.value.getTotal(); }) .ordering(function (d) { return -d.value.getTotal(); }) .render(); return chart; }); it('correct values, no others slice', function () { expect(chart.selectAll('g.pie-slice').data().map(dc.pluck('value'))) .toEqual([220, 198]); }); describe('with cap(1)', function () { beforeEach(function () { chart.cap(1).render(); }); it('correct values, others slice', function () { expect(chart.selectAll('title').nodes().map(function (t) {return d3.select(t).text();})) .toEqual(['F: 220', 'small: 198']); }); }); }); afterEach(function () { valueDimension.filterAll(); }); }); describe('pie chart w/o label', function () { var chart; beforeEach(function () { chart = buildChart('pie-chart4'); chart.innerRadius(innerRadius); chart.renderLabel(false); chart.render(); }); it('slice label should not be created', function () { expect(chart.selectAll('svg g text.pie-slice').data().length).toEqual(0); }); }); describe('renderlet', function () { var chart; beforeEach(function () { chart = buildChart('chart-renderlet'); chart.on('renderlet', function () { chart.selectAll('path').attr('fill', 'red'); }); }); it('custom renderlet should be invoked with render', function () { chart.render(); expect(chart.selectAll('path').attr('fill')).toEqual('red'); }); it('custom renderlet should be invoked with redraw', function () { chart.redraw(); expect(chart.selectAll('path').attr('fill')).toEqual('red'); }); }); describe('pie chart label and title w/ value accessor', function () { var chart; beforeEach(function () { chart = buildChart('pie-chart-default-label-title'); chart.dimension(statusGroup) .group(statusMultiGroup) .valueAccessor(function (d) { return d.value.count; }) .renderLabel(true).renderTitle(true); chart.render(); return chart; }); it('default function should be used to dynamically generate label', function () { expect(d3.select(chart.selectAll('text.pie-slice').nodes()[0]).text()).toEqual('F'); }); it('default function should be used to dynamically generate title', function () { expect(d3.select(chart.selectAll('g.pie-slice title').nodes()[0]).text()).toEqual('F: 5'); }); describe('with n/a filter', function () { beforeEach(function () { regionDimension.filter('nowhere'); chart.render(); return chart; }); it('should draw an empty chart', function () { expect(chart.select('g').classed('empty-chart')).toBeTruthy(); }); it('should have one slice', function () { expect(chart.selectAll('svg g text.pie-slice').nodes().length).toBe(1); }); it('should have slice labeled empty', function () { expect(d3.select(chart.selectAll('text.pie-slice').nodes()[0]).text()).toEqual('empty'); }); describe('with emptyTitle', function () { beforeEach(function () { chart.emptyTitle('nothing').render(); }); it('should respect the emptyTitle', function () { expect(d3.select(chart.selectAll('text.pie-slice').nodes()[0]).text()).toEqual('nothing'); }); afterEach(function () { chart.emptyTitle('empty'); }); }); afterEach(function () { regionDimension.filterAll(); }); }); }); describe('custom filter handler', function () { var chart; beforeEach(function () { chart = buildChart('pie-chart-filter-handler'); chart.filterHandler(function (dimension, filters) { dimension.filter('66'); return ['66']; }); return chart; }); it('default function should be used to dynamically generate label', function () { chart.filter(6); expect(chart.filter()).toEqual('66'); }); afterEach(function () { valueDimension.filterAll(); }); }); describe('external labeling', function () { var chart; beforeEach(function () { chart = buildChart('pie-chart-external-labeling') .externalLabels(10) .drawPaths(true) .render(); }); it('should place labels outside of pie offset by given radius', function () { var label = d3.select('#pie-chart-external-labeling svg g text.pie-slice'); var centroid = d3.arc() .outerRadius(chart.radius() + 10) .innerRadius(chart.radius() + 10) .centroid(label.datum()); expect(label.attr('transform')).toMatchTranslate(centroid[0], centroid[1], 3); }); it('gives labels class "external"', function () { d3.selectAll('#pie-chart-external-labeling svg g text.pie-slice').each(function () { expect(d3.select(this).classed('external')).toBeTruthy(); }); }); it('returns radius when given no arguments', function () { expect(chart.externalLabels()).toEqual(10); }); it('resets to default when given falsey argument', function () { chart.externalLabels(false).render(); d3.selectAll('#pie-chart-external-labeling svg g text.pie-slice').each(function () { var label = d3.select(this); var centroid = d3.arc() .outerRadius(chart.radius()) .innerRadius(chart.innerRadius()) .centroid(label.datum()); expect(label.attr('transform')).toMatchTranslate(centroid[0], centroid[1], 3); expect(label.classed('external')).toBeFalsy(); }); }); it('hovering on label should highlight corresponding slice', function () { chart.selectAll('#pie-chart-external-labeling text.pie-slice').each(function (d, i) { var legendItem = d3.select(this); legendItem.on('mouseover')(legendItem.datum(), i); expect(chart.select('g.pie-slice._' + i).classed('highlight')).toBeTruthy(); legendItem.on('mouseout')(legendItem.datum()); }); }); it('unhovering label removes highlight from corresponding slice', function () { chart.selectAll('#pie-chart-external-labeling text.pie-slice').each(function (d, i) { var legendItem = d3.select(this); legendItem.on('mouseover')(legendItem.datum(), i); legendItem.on('mouseout')(legendItem.datum(), i); expect(chart.select('.pie-slice._' + i).classed('highlight')).toBeFalsy(); }); }); it('hovering on path should highlight corresponding slice', function () { chart.selectAll('#pie-chart-external-labeling polyline.pie-path').each(function (d, i) { var legendItem = d3.select(this); legendItem.on('mouseover')(legendItem.datum(), i); expect(chart.select('g.pie-slice._' + i).classed('highlight')).toBeTruthy(); legendItem.on('mouseout')(legendItem.datum()); }); }); it('unhovering label removes highlight from corresponding slice', function () { chart.selectAll('#pie-chart-external-labeling polyline.pie-path').each(function (d, i) { var legendItem = d3.select(this); legendItem.on('mouseover')(legendItem.datum(), i); legendItem.on('mouseout')(legendItem.datum(), i); expect(chart.select('.pie-slice._' + i).classed('highlight')).toBeFalsy(); }); }); }); describe('legends', function () { var chart; beforeEach(function () { chart = buildChart('pie-chart-legend') .cap(3) .legend(dc.legend()) .render(); }); it('should generate items for each slice', function () { expect(chart.selectAll('g.dc-legend g.dc-legend-item').size()).toEqual(chart.data().length); }); it('should include "others" item', function () { var numOthersGroups = chart.selectAll('g.dc-legend g.dc-legend-item text').filter(function (d, i) { return d.name === 'Others'; }).size(); expect(numOthersGroups).toEqual(1); }); it('items should be colored', function () { chart.selectAll('g.dc-legend g.dc-legend-item').each(function () { expect(d3.select(this).select('rect').attr('fill')).not.toEqual(undefined); }); }); it('hovering on items should highlight corresponding slice', function () { chart.selectAll('g.dc-legend g.dc-legend-item').each(function (d, i) { var legendItem = d3.select(this); legendItem.on('mouseover')(legendItem.datum()); expect(chart.select('g.pie-slice._' + i).classed('highlight')).toBeTruthy(); legendItem.on('mouseout')(legendItem.datum()); }); }); it('unhovering removes highlight from corresponding slice', function () { chart.selectAll('g.dc-legend g.dc-legend-item').each(function (d, i) { var legendItem = d3.select(this); legendItem.on('mouseover')(legendItem.datum()); legendItem.on('mouseout')(legendItem.datum()); expect(chart.select('g.pie-slice._' + i).classed('highlight')).toBeFalsy(); }); }); it('clicking on items filters them', function () { chart.selectAll('g.dc-legend g.dc-legend-item').each(function (d, i) { var legendItem = d3.select(this); legendItem.on('click')(legendItem.datum()); expect(chart.hasFilter(d.name)).toBeTruthy(); }); }); afterEach(function () { valueDimension.filterAll(); }); }); });