diff --git a/bower.json b/bower.json index 0e9e03c554587..ff6f7b9c673a9 100644 --- a/bower.json +++ b/bower.json @@ -42,7 +42,7 @@ "leaflet": "0.7.3", "lesshat": "~3.0.2", "lodash": "~2.4.1", - "moment": "~2.5.1", + "moment": "~2.9.0", "moment-timezone": "~0.0.3", "ng-clip": "~0.2.4", "require-css": "~0.1.2", diff --git a/src/kibana/components/agg_response/point_series/_ordered_date_axis.js b/src/kibana/components/agg_response/point_series/_ordered_date_axis.js index 06e28aec27127..3fbd64517cfd1 100644 --- a/src/kibana/components/agg_response/point_series/_ordered_date_axis.js +++ b/src/kibana/components/agg_response/point_series/_ordered_date_axis.js @@ -1,31 +1,25 @@ define(function (require) { return function PointSeriesOrderedDateAxis(timefilter) { var moment = require('moment'); - var interval = require('utils/interval'); - return function orderedDateAxis(vis, table, chart) { + return function orderedDateAxis(vis, chart) { var aspects = chart.aspects; - var bounds = timefilter.getBounds(); - var format = interval.calculate( - moment(bounds.min.valueOf()), - moment(bounds.max.valueOf()), - table.rows.length - ).format; + var buckets = aspects.x.agg.buckets; + var format = buckets.getScaledDateFormat(); chart.xAxisFormatter = function (val) { return moment(val).format(format); }; - var xAggOutput = aspects.x.agg.write(); chart.ordered = { date: true, - interval: interval.toMs(xAggOutput.params.interval) + interval: buckets.getInterval(), }; - if (vis.indexPattern.timeFieldName) { - var timeBounds = timefilter.getBounds(); - chart.ordered.min = timeBounds.min.valueOf(); - chart.ordered.max = timeBounds.max.valueOf(); + var bounds = buckets.getBounds(); + if (bounds) { + chart.ordered.min = bounds.min; + chart.ordered.max = bounds.max; } }; }; diff --git a/src/kibana/components/agg_response/point_series/point_series.js b/src/kibana/components/agg_response/point_series/point_series.js index 3e13fd4975152..eece0f4b22fab 100644 --- a/src/kibana/components/agg_response/point_series/point_series.js +++ b/src/kibana/components/agg_response/point_series/point_series.js @@ -20,7 +20,7 @@ define(function (require) { var datedX = aspects.x.agg.type.ordered && aspects.x.agg.type.ordered.date; if (datedX) { - setupOrderedDateXAxis(vis, table, chart); + setupOrderedDateXAxis(vis, chart); } chart.series = getSeries(table.rows, chart); diff --git a/src/kibana/components/agg_types/_agg_type.js b/src/kibana/components/agg_types/_agg_type.js index 858f9fa13dc90..ae34b14905a15 100644 --- a/src/kibana/components/agg_types/_agg_type.js +++ b/src/kibana/components/agg_types/_agg_type.js @@ -102,6 +102,12 @@ define(function (require) { * or undefined */ this.getResponseAggs = config.getResponseAggs || _.noop; + + /** + * A function that will be called each time an aggConfig of this type + * is created, giving the agg type a chance to modify the agg config + */ + this.decorateAggConfig = config.decorateAggConfig || null; } return AggType; diff --git a/src/kibana/components/agg_types/buckets/_interval_options.js b/src/kibana/components/agg_types/buckets/_interval_options.js index 19d0d991becec..2e2fd220113cc 100644 --- a/src/kibana/components/agg_types/buckets/_interval_options.js +++ b/src/kibana/components/agg_types/buckets/_interval_options.js @@ -1,6 +1,7 @@ define(function (require) { return function IntervalOptionsService(Private) { var moment = require('moment'); + require('directives/input_whole_number'); // shorthand var ms = function (type) { return moment.duration(1, type).asMilliseconds(); }; @@ -15,38 +16,31 @@ define(function (require) { }, { display: 'Second', - val: 'second', - ms: ms('second') + val: 'second' }, { display: 'Minute', - val: 'minute', - ms: ms('minute') + val: 'minute' }, { display: 'Hourly', - val: 'hour', - ms: ms('hour') + val: 'hour' }, { display: 'Daily', - val: 'day', - ms: ms('day') + val: 'day' }, { display: 'Weekly', - val: 'week', - ms: ms('week') + val: 'week' }, { display: 'Monthly', - val: 'month', - ms: ms('month') + val: 'month' }, { display: 'Yearly', - val: 'year', - ms: ms('year') + val: 'year' } ]; }; diff --git a/src/kibana/components/agg_types/buckets/create_filter/date_histogram.js b/src/kibana/components/agg_types/buckets/create_filter/date_histogram.js index f2b7f4ec89fd2..acbecec937102 100644 --- a/src/kibana/components/agg_types/buckets/create_filter/date_histogram.js +++ b/src/kibana/components/agg_types/buckets/create_filter/date_histogram.js @@ -1,18 +1,16 @@ define(function (require) { - var moment = require('moment'); - var interval = require('utils/interval'); - var buildRangeFilter = require('components/filter_manager/lib/range'); - return function createDateHistogramFilterProvider(Private) { - var calculateInterval = Private(require('components/agg_types/param_types/_calculate_interval')); - return function (aggConfig, key) { - var result = calculateInterval(aggConfig); - var date = moment(key).add(result.interval, 'ms'); + var moment = require('moment'); + var buildRangeFilter = require('components/filter_manager/lib/range'); + + return function (agg, key) { + var start = moment(key); + var interval = agg.buckets.getInterval(); - return buildRangeFilter(aggConfig.params.field, { - gte: parseInt(key, 10), - lte: date.valueOf() - }, aggConfig.vis.indexPattern); + return buildRangeFilter(agg.params.field, { + gte: start.valueOf(), + lte: start.add(interval).subtract(1, 'ms').valueOf() + }, agg.vis.indexPattern); }; }; diff --git a/src/kibana/components/agg_types/buckets/date_histogram.js b/src/kibana/components/agg_types/buckets/date_histogram.js index 28419d9ee2807..c0b933b82510a 100644 --- a/src/kibana/components/agg_types/buckets/date_histogram.js +++ b/src/kibana/components/agg_types/buckets/date_histogram.js @@ -2,18 +2,13 @@ define(function (require) { return function DateHistogramAggType(timefilter, config, Private) { var _ = require('lodash'); var moment = require('moment'); - var interval = require('utils/interval'); var BucketAggType = Private(require('components/agg_types/buckets/_bucket_agg_type')); - var calculateInterval = Private(require('components/agg_types/param_types/_calculate_interval')); - + var TimeBuckets = Private(require('components/time_buckets/time_buckets')); var createFilter = Private(require('components/agg_types/buckets/create_filter/date_histogram')); - require('filters/field_type'); + var tzOffset = moment().format('Z'); - var pickInterval = function (bounds, targetBuckets) { - bounds || (bounds = timefilter.getBounds()); - return interval.calculate(bounds.min, bounds.max, targetBuckets); - }; + require('filters/field_type'); return new BucketAggType({ name: 'date_histogram', @@ -21,26 +16,34 @@ define(function (require) { ordered: { date: true }, - makeLabel: function (aggConfig) { - var output = this.params.write(aggConfig); + makeLabel: function (agg) { + var output = this.params.write(agg); var params = output.params; - - if (output.metricScaleText) return params.field + ' per ' + output.metricScaleText; - - var aggInterval = _.find(this.params.byName.interval.options, { - ms: interval.toMs(params.interval) - }); - - if (aggInterval) return aggInterval.display + ' ' + params.field; - else return params.field + ' per ' + interval.describe(params.interval); + return params.field + ' per ' + (output.metricScaleText || output.bucketInterval.description); }, createFilter: createFilter, + decorateAggConfig: function () { + var buckets; + return { + buckets: { + configurable: true, + get: function () { + if (buckets) return buckets; + + buckets = new TimeBuckets(); + buckets.setInterval(_.get(this, ['params', 'interval'])); + buckets.setBounds(timefilter.getActiveBounds()); + return buckets; + } + } + }; + }, params: [ { name: 'field', filterFieldTypes: 'date', - default: function (aggConfig) { - return aggConfig.vis.indexPattern.timeFieldName; + default: function (agg) { + return agg.vis.indexPattern.timeFieldName; } }, @@ -50,40 +53,35 @@ define(function (require) { default: 'auto', options: Private(require('components/agg_types/buckets/_interval_options')), editor: require('text!components/agg_types/controls/interval.html'), - write: function (aggConfig, output, locals) { - // Get the selection - var selection = aggConfig.params.interval; - - // If the selection isn't an object then it's going to be - // a string so we need to make it look like an object. - if (!_.isObject(selection)) { - // custom selection - selection = { - display: selection, - val: selection - }; + onRequest: function (agg) { + // flag that prevents us from clobbering on subsequest calls to write() + agg.buckets._sentToEs = true; + agg.buckets.setBounds(timefilter.getActiveBounds()); + }, + write: function (agg, output) { + if (!agg.buckets._sentToEs) { + agg.buckets.setBounds(timefilter.getActiveBounds()); } - // If the selection is set to auto and the locals.renderBot - // exists we need to blow up (for some unknown reason) - if (selection.val === 'auto' && locals.renderBot) { - throw new Error('not implemented'); - } + agg.buckets.setInterval(agg.params.interval); - // Calculate the actual interval - var result = calculateInterval(aggConfig); + var interval = agg.buckets.getInterval(); + output.bucketInterval = interval; + output.params.interval = interval.expression; + output.params.pre_zone = tzOffset; + output.params.pre_zone_adjust_large_interval = true; - // Set the output params interval along with the metric scale and - // the metric scale test which is used in the label - output.params.interval = result.interval + 'ms'; - if (result.metricScale) output.metricScale = result.metricScale; - output.metricScaleText = result.description; + var scaleMetrics = interval.scaled && interval.scale < 1; + if (scaleMetrics) { + scaleMetrics = _.every(agg.vis.aggs.bySchemaGroup.metrics, function (agg) { + return agg.type.name === 'count' || agg.type.name === 'sum'; + }); + } - // Since this is side effecting on output - // we will return right here. - // TODO: refactor so it's not side effecting so we can write tests - // around it. - return; + if (scaleMetrics) { + output.metricScale = interval.scale; + output.metricScaleText = interval.preScaled.description; + } } }, @@ -99,19 +97,23 @@ define(function (require) { { name: 'extended_bounds', default: {}, - write: function (aggConfig, output) { - var val = aggConfig.params.extended_bounds; + write: function (agg, output) { + var val = agg.params.extended_bounds; if (val.min != null || val.max != null) { output.params.extended_bounds = { min: moment(val.min).valueOf(), max: moment(val.max).valueOf() }; - } else if (aggConfig.vis.indexPattern.timeFieldName) { - var tfBounds = timefilter.getBounds(); + + return; + } + + var bounds = timefilter.getActiveBounds(); + if (bounds) { output.params.extended_bounds = { - min: moment(tfBounds.min).valueOf(), - max: moment(tfBounds.max).valueOf() + min: moment(bounds.min).valueOf(), + max: moment(bounds.max).valueOf() }; } } diff --git a/src/kibana/components/agg_types/controls/interval.html b/src/kibana/components/agg_types/controls/interval.html index d7f56dfb7195d..7a417a39836e3 100644 --- a/src/kibana/components/agg_types/controls/interval.html +++ b/src/kibana/components/agg_types/controls/interval.html @@ -1,8 +1,17 @@
- + + Scale Y-Axis to Data Bounds + +
\ No newline at end of file diff --git a/src/kibana/plugins/vis_types/vislib/area.js b/src/kibana/plugins/vis_types/vislib/area.js index 99b12a7f61063..04b097804b1be 100644 --- a/src/kibana/plugins/vis_types/vislib/area.js +++ b/src/kibana/plugins/vis_types/vislib/area.js @@ -16,7 +16,8 @@ define(function (require) { shareYAxis: true, addTooltip: true, addLegend: true, - mode: 'stacked' + mode: 'stacked', + defaultYExtents: false }, modes: ['stacked', 'overlap', 'percentage', 'wiggle', 'silhouette'], editor: require('text!plugins/vis_types/vislib/editors/area.html') diff --git a/src/kibana/plugins/vis_types/vislib/editors/histogram.html b/src/kibana/plugins/vis_types/vislib/editors/histogram.html index 2e7ce2a23c176..74ded7c8eaad5 100644 --- a/src/kibana/plugins/vis_types/vislib/editors/histogram.html +++ b/src/kibana/plugins/vis_types/vislib/editors/histogram.html @@ -5,5 +5,4 @@ - diff --git a/src/kibana/plugins/vis_types/vislib/histogram.js b/src/kibana/plugins/vis_types/vislib/histogram.js index be99dc4b01ac8..16f88d8b5411e 100644 --- a/src/kibana/plugins/vis_types/vislib/histogram.js +++ b/src/kibana/plugins/vis_types/vislib/histogram.js @@ -14,7 +14,8 @@ define(function (require) { shareYAxis: true, addTooltip: true, addLegend: true, - mode: 'stacked' + mode: 'stacked', + defaultYExtents: false }, modes: ['stacked', 'percentage', 'grouped'], editor: require('text!plugins/vis_types/vislib/editors/histogram.html') diff --git a/src/kibana/plugins/vis_types/vislib/line.js b/src/kibana/plugins/vis_types/vislib/line.js index 9d42b94229728..3931c43861a36 100644 --- a/src/kibana/plugins/vis_types/vislib/line.js +++ b/src/kibana/plugins/vis_types/vislib/line.js @@ -14,6 +14,7 @@ define(function (require) { shareYAxis: true, addTooltip: true, addLegend: true, + defaultYExtents: false }, editor: require('text!plugins/vis_types/vislib/editors/basic.html') }, diff --git a/src/kibana/services/timefilter.js b/src/kibana/services/timefilter.js index cfbfcdae12508..f674eedcbfe2e 100644 --- a/src/kibana/services/timefilter.js +++ b/src/kibana/services/timefilter.js @@ -86,6 +86,10 @@ define(function (require) { }; }; + Timefilter.prototype.getActiveBounds = function () { + if (this.enabled) return this.getBounds(); + }; + return new Timefilter(); }); diff --git a/src/kibana/utils/_mixins_chainable.js b/src/kibana/utils/_mixins_chainable.js index c84981a8d785e..b3b8ceb4277e7 100644 --- a/src/kibana/utils/_mixins_chainable.js +++ b/src/kibana/utils/_mixins_chainable.js @@ -247,6 +247,14 @@ define(function (require) { }); return buckets; - } + }, + + /** + * Shortcut for the simple version of _.deepGet + * @return {any} + */ + get: function (obj, path) { + return _.deepGet(obj, path); + }, }; }); diff --git a/src/kibana/utils/datemath.js b/src/kibana/utils/datemath.js index eee4db5dd33cb..1cb2da10e3039 100644 --- a/src/kibana/utils/datemath.js +++ b/src/kibana/utils/datemath.js @@ -2,8 +2,14 @@ define(function (require) { var _ = require('lodash'); var moment = require('moment'); + var units = ['y', 'M', 'w', 'd', 'h', 'm', 's']; + var unitsAsc = _.sortBy(units, function (unit) { + return moment.duration(1, unit).valueOf(); + }); + var unitsDesc = unitsAsc.reverse(); + /* This is a simplified version of elasticsearch's date parser */ - var parse = function (text, roundUp) { + function parse(text, roundUp) { if (!text) return undefined; if (moment.isMoment(text)) return text; if (_.isDate(text)) return moment(text); @@ -33,11 +39,10 @@ define(function (require) { } return parseDateMath(mathString, time, roundUp); - }; + } - var parseDateMath = function (mathString, time, roundUp) { - var dateTime = time, - spans = ['s', 'm', 'h', 'd', 'w', 'M', 'y']; + function parseDateMath(mathString, time, roundUp) { + var dateTime = time; for (var i = 0; i < mathString.length;) { var c = mathString.charAt(i++), @@ -71,22 +76,26 @@ define(function (require) { } unit = mathString.charAt(i++); - if (!_.contains(spans, unit)) { + if (!_.contains(units, unit)) { return undefined; } else { if (type === 0) { roundUp ? dateTime.endOf(unit) : dateTime.startOf(unit); } else if (type === 1) { - dateTime.add(unit, num); + dateTime.add(num, unit); } else if (type === 2) { - dateTime.subtract(unit, num); + dateTime.subtract(num, unit); } } } return dateTime; - }; + } return { - parse: parse + parse: parse, + + units: Object.freeze(units), + unitsAsc: Object.freeze(unitsAsc), + unitsDesc: Object.freeze(unitsDesc) }; }); \ No newline at end of file diff --git a/src/kibana/utils/interval.js b/src/kibana/utils/interval.js deleted file mode 100644 index e78b3ce01348c..0000000000000 --- a/src/kibana/utils/interval.js +++ /dev/null @@ -1,126 +0,0 @@ -define(function (require) { - var _ = require('lodash'); - var moment = require('moment'); - var datemath = require('utils/datemath'); - - require('directives/input_whole_number'); - - /** - * Calculate a graph interval - * - * from:: Moment object containing the start time - * to:: Moment object containing the finish time - * target:: Calculate to approximately this many bars - * round:: Round to a nice value (default: true) - * - * - */ - var calculate = function (from, to, target, round) { - var rawInterval; - round = round || true; - from = datemath.parse(from).valueOf(); - to = datemath.parse(to, true).valueOf(); - rawInterval = ((to - from) / target); - var rounded = roundInterval(rawInterval); - if (!round) rounded.interval = rawInterval; - return rounded; - }; - - // these are the rounding rules used by roundInterval() - var roundingRules = [ - // bound, interval/desc, format - ['500ms', '100 ms', 'hh:mm:ss.SSS'], - ['5s', 'second', 'HH:mm:ss'], - ['7.5s', '5 sec', 'HH:mm:ss'], - ['15s', '10 sec', 'HH:mm:ss'], - ['45s', '30 sec', 'HH:mm:ss'], - ['3m', 'minute', 'HH:mm'], - ['9m', '5 min', 'HH:mm'], - ['20m', '10 min', 'HH:mm'], - ['45m', '30 min', 'YYYY-MM-DD HH:mm'], - ['2h', 'hour', 'YYYY-MM-DD HH:mm'], - ['6h', '3 hours', 'YYYY-MM-DD HH:mm'], - ['24h', '12 hours', 'YYYY-MM-DD HH:mm'], - ['1w', '1 day', 'YYYY-MM-DD'], - ['3w', '1 week', 'YYYY-MM-DD'], - ['1y', '1 month', 'YYYY-MM'], - [null, '1 year', 'YYYY'] // default - ]; - var boundCache = {}; - - /** - * Round a millisecond interval to the closest "clean" interval, - * - * @param {ms} interval - interval in milliseconds - * @return {[type]} [description] - */ - var roundInterval = function (interval) { - var rule = _.find(roundingRules, function (rule, i, rules) { - var remaining = rules.length - i - 1; - // no bound? then succeed - if (!rule[0]) return true; - - var bound = boundCache[rule[0]] || (boundCache[rule[0]] = toMs(rule[0])); - // check that we are below or equal to the bounds - if (remaining > 1 && interval <= bound) return true; - // the last rule before the default shouldn't include the default (which is the bound) - if (remaining === 1 && interval < bound) return true; - }); - return { - description: rule[1], - interval: toMs(rule[1]), - format: rule[2] - }; - }; - - // map of moment's short/long unit ids and elasticsearch's long unit ids - // to their value in milliseconds - var vals = _.transform([ - ['ms', 'milliseconds', 'millisecond'], - ['s', 'seconds', 'second', 'sec'], - ['m', 'minutes', 'minute', 'min'], - ['h', 'hours', 'hour'], - ['d', 'days', 'day'], - ['w', 'weeks', 'week'], - ['M', 'months', 'month'], - ['quarter'], - ['y', 'years', 'year'] - ], function (vals, units) { - var normal = moment.normalizeUnits(units[0]); - var val = moment.duration(1, normal).asMilliseconds(); - [].concat(normal, units).forEach(function (unit) { - vals[unit] = val; - }); - }, {}); - // match any key from the vals object prececed by an optional number - var parseRE = new RegExp('^(\\d+(?:\\.\\d*)?)?\\s*(' + _.keys(vals).join('|') + ')$'); - - // Months and years are not handled here since they have sort of fuzzy values - var describe = function (intervalString) { - var totalMs = toMs(intervalString); - var weeks = parseInt(totalMs / (1000 * 60 * 60 * 24 * 7)); - var days = parseInt((totalMs / (1000 * 60 * 60 * 24))) % 7; - var hours = parseInt(totalMs / 3600000) % 24; - var minutes = parseInt(totalMs / 60000) % 60; - var seconds = parseInt(totalMs / 1000) % 60; - var ms = totalMs % 1000; - - return ((weeks ? weeks + 'w ' : '') + - (days ? days + 'd ' : '') + - (hours ? hours + 'h ' : '') + - (minutes ? minutes + 'm ' : '') + - (seconds ? seconds + 's ' : '') + - (ms ? ms + 'ms' : '')).trim(); - }; - - var toMs = function (expr) { - var match = expr.match(parseRE); - if (match) return parseFloat(match[1] || 1) * vals[match[2]]; - }; - - return { - toMs: toMs, - calculate: calculate, - describe: describe - }; -}); \ No newline at end of file diff --git a/test/unit/fixtures/agg_resp/date_histogram.js b/test/unit/fixtures/agg_resp/date_histogram.js new file mode 100644 index 0000000000000..3c66a87dd4bec --- /dev/null +++ b/test/unit/fixtures/agg_resp/date_histogram.js @@ -0,0 +1,252 @@ +define(function () { + return { + 'took': 35, + 'timed_out': false, + '_shards': { + 'total': 2, + 'successful': 2, + 'failed': 0 + }, + 'hits': { + 'total': 32899, + 'max_score': 0, + 'hits': [] + }, + 'aggregations': { + '1': { + 'buckets': [ + { + 'key_as_string': '2015-01-30T01:00:00.000Z', + 'key': 1422579600000, + 'doc_count': 18 + }, + { + 'key_as_string': '2015-01-30T02:00:00.000Z', + 'key': 1422583200000, + 'doc_count': 68 + }, + { + 'key_as_string': '2015-01-30T03:00:00.000Z', + 'key': 1422586800000, + 'doc_count': 146 + }, + { + 'key_as_string': '2015-01-30T04:00:00.000Z', + 'key': 1422590400000, + 'doc_count': 149 + }, + { + 'key_as_string': '2015-01-30T05:00:00.000Z', + 'key': 1422594000000, + 'doc_count': 363 + }, + { + 'key_as_string': '2015-01-30T06:00:00.000Z', + 'key': 1422597600000, + 'doc_count': 555 + }, + { + 'key_as_string': '2015-01-30T07:00:00.000Z', + 'key': 1422601200000, + 'doc_count': 878 + }, + { + 'key_as_string': '2015-01-30T08:00:00.000Z', + 'key': 1422604800000, + 'doc_count': 1133 + }, + { + 'key_as_string': '2015-01-30T09:00:00.000Z', + 'key': 1422608400000, + 'doc_count': 1438 + }, + { + 'key_as_string': '2015-01-30T10:00:00.000Z', + 'key': 1422612000000, + 'doc_count': 1719 + }, + { + 'key_as_string': '2015-01-30T11:00:00.000Z', + 'key': 1422615600000, + 'doc_count': 1813 + }, + { + 'key_as_string': '2015-01-30T12:00:00.000Z', + 'key': 1422619200000, + 'doc_count': 1790 + }, + { + 'key_as_string': '2015-01-30T13:00:00.000Z', + 'key': 1422622800000, + 'doc_count': 1582 + }, + { + 'key_as_string': '2015-01-30T14:00:00.000Z', + 'key': 1422626400000, + 'doc_count': 1439 + }, + { + 'key_as_string': '2015-01-30T15:00:00.000Z', + 'key': 1422630000000, + 'doc_count': 1154 + }, + { + 'key_as_string': '2015-01-30T16:00:00.000Z', + 'key': 1422633600000, + 'doc_count': 847 + }, + { + 'key_as_string': '2015-01-30T17:00:00.000Z', + 'key': 1422637200000, + 'doc_count': 588 + }, + { + 'key_as_string': '2015-01-30T18:00:00.000Z', + 'key': 1422640800000, + 'doc_count': 374 + }, + { + 'key_as_string': '2015-01-30T19:00:00.000Z', + 'key': 1422644400000, + 'doc_count': 152 + }, + { + 'key_as_string': '2015-01-30T20:00:00.000Z', + 'key': 1422648000000, + 'doc_count': 140 + }, + { + 'key_as_string': '2015-01-30T21:00:00.000Z', + 'key': 1422651600000, + 'doc_count': 73 + }, + { + 'key_as_string': '2015-01-30T22:00:00.000Z', + 'key': 1422655200000, + 'doc_count': 28 + }, + { + 'key_as_string': '2015-01-30T23:00:00.000Z', + 'key': 1422658800000, + 'doc_count': 9 + }, + { + 'key_as_string': '2015-01-31T00:00:00.000Z', + 'key': 1422662400000, + 'doc_count': 29 + }, + { + 'key_as_string': '2015-01-31T01:00:00.000Z', + 'key': 1422666000000, + 'doc_count': 38 + }, + { + 'key_as_string': '2015-01-31T02:00:00.000Z', + 'key': 1422669600000, + 'doc_count': 70 + }, + { + 'key_as_string': '2015-01-31T03:00:00.000Z', + 'key': 1422673200000, + 'doc_count': 136 + }, + { + 'key_as_string': '2015-01-31T04:00:00.000Z', + 'key': 1422676800000, + 'doc_count': 173 + }, + { + 'key_as_string': '2015-01-31T05:00:00.000Z', + 'key': 1422680400000, + 'doc_count': 370 + }, + { + 'key_as_string': '2015-01-31T06:00:00.000Z', + 'key': 1422684000000, + 'doc_count': 545 + }, + { + 'key_as_string': '2015-01-31T07:00:00.000Z', + 'key': 1422687600000, + 'doc_count': 845 + }, + { + 'key_as_string': '2015-01-31T08:00:00.000Z', + 'key': 1422691200000, + 'doc_count': 1070 + }, + { + 'key_as_string': '2015-01-31T09:00:00.000Z', + 'key': 1422694800000, + 'doc_count': 1419 + }, + { + 'key_as_string': '2015-01-31T10:00:00.000Z', + 'key': 1422698400000, + 'doc_count': 1725 + }, + { + 'key_as_string': '2015-01-31T11:00:00.000Z', + 'key': 1422702000000, + 'doc_count': 1801 + }, + { + 'key_as_string': '2015-01-31T12:00:00.000Z', + 'key': 1422705600000, + 'doc_count': 1823 + }, + { + 'key_as_string': '2015-01-31T13:00:00.000Z', + 'key': 1422709200000, + 'doc_count': 1657 + }, + { + 'key_as_string': '2015-01-31T14:00:00.000Z', + 'key': 1422712800000, + 'doc_count': 1454 + }, + { + 'key_as_string': '2015-01-31T15:00:00.000Z', + 'key': 1422716400000, + 'doc_count': 1131 + }, + { + 'key_as_string': '2015-01-31T16:00:00.000Z', + 'key': 1422720000000, + 'doc_count': 810 + }, + { + 'key_as_string': '2015-01-31T17:00:00.000Z', + 'key': 1422723600000, + 'doc_count': 583 + }, + { + 'key_as_string': '2015-01-31T18:00:00.000Z', + 'key': 1422727200000, + 'doc_count': 384 + }, + { + 'key_as_string': '2015-01-31T19:00:00.000Z', + 'key': 1422730800000, + 'doc_count': 165 + }, + { + 'key_as_string': '2015-01-31T20:00:00.000Z', + 'key': 1422734400000, + 'doc_count': 135 + }, + { + 'key_as_string': '2015-01-31T21:00:00.000Z', + 'key': 1422738000000, + 'doc_count': 72 + }, + { + 'key_as_string': '2015-01-31T22:00:00.000Z', + 'key': 1422741600000, + 'doc_count': 8 + } + ] + } + } + }; +}); \ No newline at end of file diff --git a/test/unit/specs/components/agg_response/point_series/_ordered_date_axis.js b/test/unit/specs/components/agg_response/point_series/_ordered_date_axis.js index 1ebbd0a7d0525..d36d2fb44f6a7 100644 --- a/test/unit/specs/components/agg_response/point_series/_ordered_date_axis.js +++ b/test/unit/specs/components/agg_response/point_series/_ordered_date_axis.js @@ -2,7 +2,7 @@ define(function (require) { return ['orderedDateAxis', function () { var moment = require('moment'); var _ = require('lodash'); - var interval = require('utils/interval'); + var sinon = require('test_utils/auto_release_sinon'); var baseArgs = { vis: { @@ -10,14 +10,15 @@ define(function (require) { timeFieldName: '@timestamp' } }, - table: { - rows: new Array(50) - }, chart: { aspects: { x: { agg: { - write: _.constant({ params: { interval: '10m' } }) + buckets: { + getScaledDateFormat: _.constant('hh:mm:ss'), + getInterval: _.constant(moment.duration(15, 'm')), + getBounds: _.constant({ min: moment().subtract(15, 'm'), max: moment() }) + } } } } @@ -34,7 +35,7 @@ define(function (require) { describe('xAxisFormatter', function () { it('sets the xAxisFormatter', function () { var args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.table, args.chart); + orderedDateAxis(args.vis, args.chart); expect(args.chart).to.have.property('xAxisFormatter'); expect(args.chart.xAxisFormatter).to.be.a('function'); @@ -42,7 +43,7 @@ define(function (require) { it('formats values using moment, and returns strings', function () { var args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.table, args.chart); + orderedDateAxis(args.vis, args.chart); var val = '2014-08-06T12:34:01'; expect(args.chart.xAxisFormatter(val)) @@ -53,7 +54,7 @@ define(function (require) { describe('ordered object', function () { it('sets date: true', function () { var args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.table, args.chart); + orderedDateAxis(args.vis, args.chart); expect(args.chart) .to.have.property('ordered'); @@ -62,26 +63,26 @@ define(function (require) { .to.have.property('date', true); }); - it('sets interval after parsing the output of the x.agg', function () { + it('relies on agg.buckets for the interval', function () { var args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.table, args.chart); - expect(args.chart.ordered.interval).to.be(interval.toMs('10m')); + var spy = sinon.spy(args.chart.aspects.x.agg.buckets, 'getInterval'); + orderedDateAxis(args.vis, args.chart); + expect(spy).to.have.property('callCount', 1); }); - it('sets the min and max when the indexPattern has a timeField', function () { + it('sets the min/max when the buckets are bounded', function () { var args = _.cloneDeep(baseArgs); - orderedDateAxis(args.vis, args.table, args.chart); - - expect(args.chart.ordered.min).to.be.a('number'); - expect(args.chart.ordered.max).to.be.a('number'); - - args = _.cloneDeep(baseArgs); - delete args.vis.indexPattern.timeFieldName; - orderedDateAxis(args.vis, args.table, args.chart); + orderedDateAxis(args.vis, args.chart); + expect(moment.isMoment(args.chart.ordered.min)).to.be(true); + expect(moment.isMoment(args.chart.ordered.max)).to.be(true); + }); - expect(args.chart.ordered) - .to.not.have.property('min') - .and.not.have.property('max'); + it('does not set the min/max when the buckets are unbounded', function () { + var args = _.cloneDeep(baseArgs); + args.chart.aspects.x.agg.buckets.getBounds = _.constant(); + orderedDateAxis(args.vis, args.chart); + expect(args.chart.ordered).to.not.have.property('min'); + expect(args.chart.ordered).to.not.have.property('max'); }); }); }]; diff --git a/test/unit/specs/components/agg_types/buckets/_date_histogram.js b/test/unit/specs/components/agg_types/buckets/_date_histogram.js index 0c92c2b3fc81a..1061c38afae03 100644 --- a/test/unit/specs/components/agg_types/buckets/_date_histogram.js +++ b/test/unit/specs/components/agg_types/buckets/_date_histogram.js @@ -5,50 +5,99 @@ define(function (require) { describe('params', function () { var paramWriter; + var aggTypes; + var AggConfig; + var setTimeBounds; beforeEach(module('kibana')); - beforeEach(inject(function (Private) { + beforeEach(inject(function (Private, $injector) { var AggParamWriter = Private(require('test_utils/agg_param_writer')); + var timefilter = $injector.get('timefilter'); + + aggTypes = Private(require('components/agg_types/index')); + AggConfig = Private(require('components/vis/_agg_config')); + paramWriter = new AggParamWriter({ aggType: 'date_histogram' }); + + var now = moment(); + setTimeBounds = function (n, units) { + timefilter.enabled = true; + timefilter.getBounds = _.constant({ + min: now.clone().subtract(n, units), + max: now.clone() + }); + }; })); describe('interval', function () { - it('should accept a valid interval', function () { + it('accepts a valid interval', function () { var output = paramWriter.write({ interval: 'day' }); - expect(output.params).to.have.property('interval', '86400000ms'); + expect(output.params).to.have.property('interval', '1d'); }); - it('should throw an error if an invalid interval is given', function () { - expect(function () { - paramWriter.write({ interval: 'foo' }); - }).to.throwError(); + it('ignores invalid intervals', function () { + var output = paramWriter.write({ interval: 'foo' }); + expect(output.params).to.have.property('interval', '0ms'); }); - it('should automatically pick an interval', function () { + it('automatically picks an interval', function () { + setTimeBounds(15, 'minutes'); var output = paramWriter.write({ interval: 'auto' }); - expect(output.params.interval).to.be('30000ms'); - expect(output.metricScaleText).to.be('30 sec'); + expect(output.params.interval).to.be('30s'); }); - it('should scale if there are too many buckets', function () { + it('scales up the interval if it will make too many buckets', function () { + setTimeBounds(30, 'minutes'); var output = paramWriter.write({ interval: 'second' }); - expect(output.params.interval).to.be('10000ms'); + expect(output.params.interval).to.be('10s'); expect(output.metricScaleText).to.be('second'); expect(output.metricScale).to.be(0.1); }); - it('should not scale if there are too many buckets and the metric is not sum or count', function () { - paramWriter.vis.aggs.bySchemaGroup.metrics[0].type.name = 'average'; - var output = paramWriter.write({ interval: 'second' }); - expect(output).to.not.have.property('metricScale'); - - paramWriter.vis.aggs.bySchemaGroup.metrics[0].type.name = 'max'; - output = paramWriter.write({ interval: 'second' }); - expect(output).to.not.have.property('metricScale'); + it('does not scale down the interval', function () { + setTimeBounds(1, 'minutes'); + var output = paramWriter.write({ interval: 'hour' }); + expect(output.params.interval).to.be('1h'); + expect(output.metricScaleText).to.be(undefined); + expect(output.metricScale).to.be(undefined); + }); - paramWriter.vis.aggs.bySchemaGroup.metrics[0].type.name = 'sum'; - output = paramWriter.write({ interval: 'second' }); - expect(output).to.have.property('metricScale'); + describe('only scales when all metrics are sum or count', function () { + var tests = [ + [ false, 'avg', 'count', 'sum' ], + [ true, 'count', 'sum' ], + [ false, 'count', 'cardinality' ] + ]; + + tests.forEach(function (test) { + var should = test.shift(); + var typeNames = test.slice(); + + it(typeNames.join(', ') + ' should ' + (should ? '' : 'not') + ' scale', function () { + setTimeBounds(1, 'year'); + + var vis = paramWriter.vis; + vis.aggs.splice(0); + + var histoConfig = new AggConfig(vis, { + type: aggTypes.byName.date_histogram, + schema: 'segment', + params: { interval: 'second' } + }); + + vis.aggs.push(histoConfig); + + typeNames.forEach(function (type) { + vis.aggs.push(new AggConfig(vis, { + type: aggTypes.byName[type], + schema: 'metric' + })); + }); + + var output = histoConfig.write(); + expect(_.has(output, 'metricScale')).to.be(should); + }); + }); }); }); diff --git a/test/unit/specs/components/agg_types/buckets/create_filter/date_histogram.js b/test/unit/specs/components/agg_types/buckets/create_filter/date_histogram.js index dca71f349ab1d..4fb3901b785fc 100644 --- a/test/unit/specs/components/agg_types/buckets/create_filter/date_histogram.js +++ b/test/unit/specs/components/agg_types/buckets/create_filter/date_histogram.js @@ -1,55 +1,100 @@ define(function (require) { - var _ = require('lodash'); - var moment = require('moment'); describe('AggConfig Filters', function () { - describe('date_historgram', function () { - var AggConfig; - var indexPattern; - var Vis; - var createFilter; + describe('date_histogram', function () { + var _ = require('lodash'); + var moment = require('moment'); + var sinon = require('test_utils/auto_release_sinon'); + var aggResp = require('fixtures/agg_resp/date_histogram'); + + var vis; + var agg; + var field; + var filter; + var bucketKey; + var bucketStart; + var getIntervalStub; + var intervalOptions; + + var init; beforeEach(module('kibana')); - beforeEach(inject(function (Private) { - Vis = Private(require('components/vis/vis')); - AggConfig = Private(require('components/vis/_agg_config')); - indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern')); - createFilter = Private(require('components/agg_types/buckets/create_filter/date_histogram')); - })); + beforeEach(inject(function (Private, $injector) { + var Vis = Private(require('components/vis/vis')); + var indexPattern = Private(require('fixtures/stubbed_logstash_index_pattern')); + var createFilter = Private(require('components/agg_types/buckets/create_filter/date_histogram')); + var TimeBuckets = Private(require('components/time_buckets/time_buckets')); + intervalOptions = Private(require('components/agg_types/buckets/_interval_options')); - var runTest = function (intervalString, interval) { - it('should return a range filter for ' + intervalString, function () { - var vis = new Vis(indexPattern, { + init = function (interval, duration) { + interval = interval || 'auto'; + duration = duration || moment.duration(15, 'minutes'); + field = _.sample(indexPattern.fields.byType.date); + vis = new Vis(indexPattern, { type: 'histogram', aggs: [ { type: 'date_histogram', schema: 'segment', - params: { field: '@timestamp', interval: intervalString } + params: { field: field.name, interval: interval } } ] }); - var aggConfig = vis.aggs.byTypeName.date_histogram[0]; - var date = moment('2014-01-01 12:00'); - var max = date.clone().add(interval, 'ms'); - var filter = createFilter(aggConfig, date.valueOf()); - expect(filter).to.have.property('range'); - expect(filter).to.have.property('meta'); - expect(filter.range).to.have.property('@timestamp'); - expect(filter.range['@timestamp']).to.have.property('gte', date.valueOf()); - expect(filter.range['@timestamp']).to.have.property('lte', max.valueOf()); - expect(filter.meta).to.have.property('index', indexPattern.id); - }); - }; - runTest('auto', 30000); - runTest('second', 10000); - runTest('minute', 60000); - runTest('hour', 3600000); - runTest('day', 86400000); - runTest('week', 604800000); - runTest('month', 2592000000); - runTest('year', 31536000000); + agg = vis.aggs[0]; + bucketKey = _.sample(aggResp.aggregations['1'].buckets).key; + bucketStart = moment(bucketKey); + + var timePad = moment.duration(duration / 2); + agg.buckets.setBounds({ + min: bucketStart.clone().subtract(timePad), + max: bucketStart.clone().add(timePad), + }); + agg.buckets.setInterval(interval); + + filter = createFilter(agg, bucketKey); + }; + })); + + it('creates a valid range filter', function () { + init(); + + expect(filter).to.have.property('range'); + expect(filter.range).to.have.property(field.name); + var fieldParams = filter.range[field.name]; + expect(fieldParams).to.have.property('gte'); + expect(fieldParams.gte).to.be.a('number'); + + expect(fieldParams).to.have.property('lte'); + expect(fieldParams.lte).to.be.a('number'); + + expect(fieldParams.gte).to.be.lessThan(fieldParams.lte); + + expect(filter).to.have.property('meta'); + expect(filter.meta).to.have.property('index', vis.indexPattern.id); + }); + + + it('extends the filter edge to 1ms before the next bucket for all interval options', function () { + intervalOptions.forEach(function (option) { + var duration; + if (moment(1, option.val).isValid()) { + duration = moment.duration(10, option.val); + + if (+duration < 10) { + throw new Error('unable to create interval for ' + option.val); + } + } + + init(option.val, duration); + + var interval = agg.buckets.getInterval(); + var params = filter.range[field.name]; + + expect(params.gte).to.be(+bucketStart); + expect(params.lte).to.be(+bucketStart.clone().add(interval).subtract(1, 'ms')); + }); + }); }); }); }); diff --git a/test/unit/specs/plugins/vis_types/vislib/_renderbot.js b/test/unit/specs/plugins/vis_types/vislib/_renderbot.js index e7ccdc20dfcda..4cabe77f9f9cd 100644 --- a/test/unit/specs/plugins/vis_types/vislib/_renderbot.js +++ b/test/unit/specs/plugins/vis_types/vislib/_renderbot.js @@ -24,7 +24,6 @@ define(function (require) { Renderbot = Private(require('plugins/vis_types/_renderbot')); VislibRenderbot = Private(require('plugins/vis_types/vislib/_vislib_renderbot')); normalizeChartData = Private(require('components/agg_response/index')); - }); } @@ -85,7 +84,7 @@ define(function (require) { } }, mockVisType) }; - var $el = 'element'; + var $el = $('
testing
'); var createVisSpy; var getParamsStub; var renderbot; diff --git a/test/unit/specs/utils/interval.js b/test/unit/specs/utils/interval.js deleted file mode 100644 index bf5e4b2d47ddd..0000000000000 --- a/test/unit/specs/utils/interval.js +++ /dev/null @@ -1,138 +0,0 @@ -define(function (require) { - var interval = require('utils/interval'); - var moment = require('moment'); - var _ = require('lodash'); - var sinon = require('sinon/sinon'); - - describe('interval', function () { - - describe('toMs', function () { - it('return the number of milliseconds represented by the string', function () { - expect(interval.toMs('1ms')).to.be(1); - expect(interval.toMs('1s')).to.be(1000); - expect(interval.toMs('1m')).to.be(60000); - expect(interval.toMs('1h')).to.be(3600000); - expect(interval.toMs('1d')).to.be(86400000); - expect(interval.toMs('1w')).to.be(604800000); - expect(interval.toMs('1M')).to.be(2592000000); // actually 30d - expect(interval.toMs('1y')).to.be(31536000000); // 1000*60*60*24*365 - }); - }); - - describe('description', function () { - it('returns a readable description for an interval', function () { - expect(interval.describe('1s')).to.be('1s'); - expect(interval.describe('1m')).to.be('1m'); - expect(interval.describe('1h')).to.be('1h'); - expect(interval.describe('1d')).to.be('1d'); - expect(interval.describe('1w')).to.be('1w'); - - expect(interval.describe('2w')).to.be('2w'); - expect(interval.describe('86400000ms')).to.be('1d'); - expect(interval.describe('86400001ms')).to.be('1d 1ms'); - expect(interval.describe('90000000ms')).to.be('1d 1h'); - expect(interval.describe('90060000ms')).to.be('1d 1h 1m'); - expect(interval.describe('90061000ms')).to.be('1d 1h 1m 1s'); - expect(interval.describe('90061300ms')).to.be('1d 1h 1m 1s 300ms'); - }); - }); - - describe('rounding', function () { - var mmnt, date, string, now, clock, then; - - beforeEach(function () { - clock = sinon.useFakeTimers(); - now = moment(); - then = moment(); - }); - - afterEach(function () { - clock.restore(); - }); - - it('should calculate an appropriate interval for 10s', function () { - var _t = then.subtract(10, 'seconds'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('100ms')); - }); - - it('should calculate an appropriate interval for 1m', function () { - var _t = then.subtract(1, 'minute'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1s')); - }); - - it('should calculate an appropriate interval for 10m', function () { - var _t = then.subtract(10, 'minutes'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('5s')); - }); - - it('should calculate an appropriate interval for 15m', function () { - var _t = then.subtract(15, 'minutes'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('10s')); - }); - - it('should calculate an appropriate interval for 1h', function () { - var _t = then.subtract(1, 'hour'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('30s')); - }); - - it('should calculate an appropriate interval for 90m', function () { - var _t = then.subtract(90, 'minutes'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1m')); - }); - - it('should calculate an appropriate interval for 6h', function () { - var _t = then.subtract(6, 'hours'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('5m')); - }); - - it('should calculate an appropriate interval for 24h', function () { - var _t = then.subtract(24, 'hours'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('10m')); - }); - - it('should calculate an appropriate interval for 3d', function () { - var _t = then.subtract(3, 'days'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('30m')); - }); - - it('should calculate an appropriate interval for 1w', function () { - var _t = then.subtract(1, 'week'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1h')); - }); - - it('should calculate an appropriate interval for 2w', function () { - var _t = then.subtract(2, 'weeks'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('3h')); - }); - - it('should calculate an appropriate interval for 1M', function () { - var _t = then.subtract(1, 'month'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('12h')); - }); - - it('should calculate an appropriate interval for 4M', function () { - var _t = then.subtract(4, 'months'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1d')); - }); - - it('should calculate an appropriate interval for 2y', function () { - var _t = then.subtract(2, 'years'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1w')); - }); - - it('should calculate an appropriate interval for 25y', function () { - var _t = then.subtract(25, 'years'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1M')); - }); - - - it('should calculate an appropriate interval for a 100y', function () { - var _t = then.subtract(100, 'years'); - expect(interval.calculate(_t, now, 100).interval).to.be(interval.toMs('1y')); - }); - - }); - - }); - -}); \ No newline at end of file diff --git a/test/unit/specs/vislib/components/tooltip/_positioning.js b/test/unit/specs/vislib/components/tooltip/_positioning.js index f6a1b1709a573..bc087ecad9df8 100644 --- a/test/unit/specs/vislib/components/tooltip/_positioning.js +++ b/test/unit/specs/vislib/components/tooltip/_positioning.js @@ -70,7 +70,7 @@ define(function (require) { var w = sinon.spy($.fn, 'outerWidth'); var h = sinon.spy($.fn, 'outerHeight'); - posTT.getTtSize($tooltip, $sizer); + posTT.getTtSize($tooltip.html(), $sizer); [w, h].forEach(function (spy) { expect(spy).to.have.property('callCount', 1); @@ -84,7 +84,7 @@ define(function (require) { describe('getBasePosition()', function () { it('calculates the offset values for the four positions', function () { - var size = posTT.getTtSize($tooltip, $sizer); + var size = posTT.getTtSize($tooltip.html(), $sizer); var pos = posTT.getBasePosition(size, makeEvent()); positions.forEach(function (p) { @@ -114,7 +114,7 @@ define(function (require) { // size the tooltip very small so it won't collide with the edges $tooltip.css({ width: 15, height: 15 }); $sizer.css({ width: 15, height: 15 }); - var size = posTT.getTtSize($tooltip, $sizer); + var size = posTT.getTtSize($tooltip.html(), $sizer); expect(size).to.have.property('width', 15); expect(size).to.have.property('height', 15); @@ -131,7 +131,7 @@ define(function (require) { }); it('identifies an overflow with a positive value in that direction', function () { - var size = posTT.getTtSize($tooltip, $sizer); + var size = posTT.getTtSize($tooltip.html(), $sizer); // position the element based on a mouse that is in the bottom right hand courner of the chart var pos = posTT.getBasePosition(size, makeEvent(0.99, 0.99)); diff --git a/test/unit/specs/vislib/fixture/mock_data/date_histogram/_series_neg.js b/test/unit/specs/vislib/fixture/mock_data/date_histogram/_series_neg.js new file mode 100644 index 0000000000000..746c011778d18 --- /dev/null +++ b/test/unit/specs/vislib/fixture/mock_data/date_histogram/_series_neg.js @@ -0,0 +1,152 @@ +define(function (require) { + var moment = require('moment'); + + return { + 'label': '', + 'xAxisLabel': '@timestamp per 30 sec', + 'ordered': { + 'date': true, + 'min': 1411761457636, + 'max': 1411762357636, + 'interval': 30000 + }, + 'yAxisLabel': 'Count of documents', + 'series': [ + { + 'values': [ + { + 'x': 1411761450000, + 'y': -41 + }, + { + 'x': 1411761480000, + 'y': -18 + }, + { + 'x': 1411761510000, + 'y': -22 + }, + { + 'x': 1411761540000, + 'y': -17 + }, + { + 'x': 1411761570000, + 'y': -17 + }, + { + 'x': 1411761600000, + 'y': -21 + }, + { + 'x': 1411761630000, + 'y': -16 + }, + { + 'x': 1411761660000, + 'y': -17 + }, + { + 'x': 1411761690000, + 'y': -15 + }, + { + 'x': 1411761720000, + 'y': -19 + }, + { + 'x': 1411761750000, + 'y': -11 + }, + { + 'x': 1411761780000, + 'y': -13 + }, + { + 'x': 1411761810000, + 'y': -24 + }, + { + 'x': 1411761840000, + 'y': -20 + }, + { + 'x': 1411761870000, + 'y': -20 + }, + { + 'x': 1411761900000, + 'y': -21 + }, + { + 'x': 1411761930000, + 'y': -17 + }, + { + 'x': 1411761960000, + 'y': -20 + }, + { + 'x': 1411761990000, + 'y': -13 + }, + { + 'x': 1411762020000, + 'y': -14 + }, + { + 'x': 1411762050000, + 'y': -25 + }, + { + 'x': 1411762080000, + 'y': -17 + }, + { + 'x': 1411762110000, + 'y': -14 + }, + { + 'x': 1411762140000, + 'y': -22 + }, + { + 'x': 1411762170000, + 'y': -14 + }, + { + 'x': 1411762200000, + 'y': -19 + }, + { + 'x': 1411762230000, + 'y': -22 + }, + { + 'x': 1411762260000, + 'y': -17 + }, + { + 'x': 1411762290000, + 'y': -8 + }, + { + 'x': 1411762320000, + 'y': -15 + }, + { + 'x': 1411762350000, + 'y': -4 + } + ] + } + ], + 'hits': 533, + 'xAxisFormatter': function (thing) { + return moment(thing); + }, + 'tooltipFormatter': function (d) { + return d; + } + }; +}); diff --git a/test/unit/specs/vislib/fixture/mock_data/date_histogram/_series_pos_neg.js b/test/unit/specs/vislib/fixture/mock_data/date_histogram/_series_pos_neg.js new file mode 100644 index 0000000000000..d514b93c31cd2 --- /dev/null +++ b/test/unit/specs/vislib/fixture/mock_data/date_histogram/_series_pos_neg.js @@ -0,0 +1,152 @@ +define(function (require) { + var moment = require('moment'); + + return { + 'label': '', + 'xAxisLabel': '@timestamp per 30 sec', + 'ordered': { + 'date': true, + 'min': 1411761457636, + 'max': 1411762357636, + 'interval': 30000 + }, + 'yAxisLabel': 'Count of documents', + 'series': [ + { + 'values': [ + { + 'x': 1411761450000, + 'y': 41 + }, + { + 'x': 1411761480000, + 'y': 18 + }, + { + 'x': 1411761510000, + 'y': -22 + }, + { + 'x': 1411761540000, + 'y': -17 + }, + { + 'x': 1411761570000, + 'y': -17 + }, + { + 'x': 1411761600000, + 'y': -21 + }, + { + 'x': 1411761630000, + 'y': -16 + }, + { + 'x': 1411761660000, + 'y': 17 + }, + { + 'x': 1411761690000, + 'y': 15 + }, + { + 'x': 1411761720000, + 'y': 19 + }, + { + 'x': 1411761750000, + 'y': 11 + }, + { + 'x': 1411761780000, + 'y': -13 + }, + { + 'x': 1411761810000, + 'y': -24 + }, + { + 'x': 1411761840000, + 'y': -20 + }, + { + 'x': 1411761870000, + 'y': -20 + }, + { + 'x': 1411761900000, + 'y': -21 + }, + { + 'x': 1411761930000, + 'y': 17 + }, + { + 'x': 1411761960000, + 'y': 20 + }, + { + 'x': 1411761990000, + 'y': -13 + }, + { + 'x': 1411762020000, + 'y': -14 + }, + { + 'x': 1411762050000, + 'y': 25 + }, + { + 'x': 1411762080000, + 'y': -17 + }, + { + 'x': 1411762110000, + 'y': -14 + }, + { + 'x': 1411762140000, + 'y': -22 + }, + { + 'x': 1411762170000, + 'y': -14 + }, + { + 'x': 1411762200000, + 'y': 19 + }, + { + 'x': 1411762230000, + 'y': 22 + }, + { + 'x': 1411762260000, + 'y': 17 + }, + { + 'x': 1411762290000, + 'y': 8 + }, + { + 'x': 1411762320000, + 'y': -15 + }, + { + 'x': 1411762350000, + 'y': -4 + } + ] + } + ], + 'hits': 533, + 'xAxisFormatter': function (thing) { + return moment(thing); + }, + 'tooltipFormatter': function (d) { + return d; + } + }; +}); diff --git a/test/unit/specs/vislib/lib/data.js b/test/unit/specs/vislib/lib/data.js index c28437febfa4b..516a5a80e7892 100644 --- a/test/unit/specs/vislib/lib/data.js +++ b/test/unit/specs/vislib/lib/data.js @@ -230,20 +230,68 @@ define(function (require) { colOut = colIn.flatten(); }); - it('should return an array of arrays', function () { - expect(_.isArray(serOut)).to.be(true); + it('should return an array of value objects from every series', function () { + expect(serOut.every(_.isObject)).to.be(true); }); - it('should return array length 3', function () { - expect(serOut[0][0].length).to.be(3); + function testLength(inputData) { + return function () { + var data = new dataFactory(inputData, {}); + var len = _.reduce(data.chartData(), function (sum, chart) { + return sum + chart.series.reduce(function (sum, series) { + return sum + series.values.length; + }, 0); + }, 0); + + expect(data.flatten()).to.have.length(len); + }; + } + + it('should return all points from every series', testLength(seriesData)); + it('should return all points from every series', testLength(rowsData)); + it('should return all points from every series', testLength(colsData)); + }); + + describe('getYMinValue method', function () { + var Data; + var dataSeries; + var stackedDataSeries; + var visData; + var stackedVisData; + var series; + var stackedSeries; + var minValue; + var stackedMinValue; + + beforeEach(function () { + module('DataFactory'); }); - it('should return array length 3', function () { - expect(rowOut[0][0].length).to.be(3); + beforeEach(function () { + inject(function (d3, Private) { + Data = Private(require('components/vislib/lib/data')); + dataSeries = require('vislib_fixtures/mock_data/date_histogram/_series'); + stackedDataSeries = require('vislib_fixtures/mock_data/stacked/_stacked'); + visData = new Data(dataSeries, {}); + stackedVisData = new Data(stackedDataSeries, { type: 'histogram' }); + series = _.pluck(visData.chartData(), 'series'); + stackedSeries = _.pluck(stackedVisData.chartData(), 'series'); + minValue = 4; + stackedMinValue = 15; + }); }); - it('should return array length 3', function () { - expect(colOut[0][0].length).to.be(3); + // The first value in the time series is less than the min date in the + // date range. It also has the largest y value. This value should be excluded + // when calculating the Y max value since it falls outside of the range. + it('should return the Y domain min value', function () { + expect(visData.getYMinValue()).to.be(minValue); + expect(stackedVisData.getYMinValue()).to.be(stackedMinValue); + }); + + it('should have a minimum date value that is greater than the max value within the date range', function () { + expect(_.min(series.values, function (d) { return d.x; })).to.be.greaterThan(minValue); + expect(_.min(stackedSeries.values, function (d) { return d.x; })).to.be.greaterThan(stackedMinValue); }); }); @@ -268,9 +316,9 @@ define(function (require) { dataSeries = require('vislib_fixtures/mock_data/date_histogram/_series'); stackedDataSeries = require('vislib_fixtures/mock_data/stacked/_stacked'); visData = new Data(dataSeries, {}); - stackedVisData = new Data(stackedDataSeries, {}); - series = visData.flatten(); - stackedSeries = stackedVisData.flatten(); + stackedVisData = new Data(stackedDataSeries, { type: 'histogram' }); + series = _.pluck(visData.chartData(), 'series'); + stackedSeries = _.pluck(stackedVisData.chartData(), 'series'); maxValue = 41; stackedMaxValue = 115; }); @@ -279,13 +327,9 @@ define(function (require) { // The first value in the time series is less than the min date in the // date range. It also has the largest y value. This value should be excluded // when calculating the Y max value since it falls outside of the range. - it('should return the Y domain max value', function () { - series.forEach(function (data) { - expect(visData._getYMax(data, visData._getY)).to.be(maxValue); - }); - stackedSeries.forEach(function (data) { - expect(stackedVisData._getYMax(data, visData._getYStack)).to.be(stackedMaxValue); - }); + it('should return the Y domain min value', function () { + expect(visData.getYMaxValue()).to.be(maxValue); + expect(stackedVisData.getYMaxValue()).to.be(stackedMaxValue); }); it('should have a minimum date value that is greater than the max value within the date range', function () { diff --git a/test/unit/specs/vislib/lib/dispatch.js b/test/unit/specs/vislib/lib/dispatch.js index 2be2af0d84081..2e15e21bef952 100644 --- a/test/unit/specs/vislib/lib/dispatch.js +++ b/test/unit/specs/vislib/lib/dispatch.js @@ -20,9 +20,7 @@ define(function (require) { vis = Private(require('vislib_fixtures/_vis_fixture'))(); require('css!components/vislib/styles/main'); - vis.on('brush', function (e) { - console.log(e); - }); + vis.on('brush', _.noop); vis.render(data); }); @@ -36,12 +34,8 @@ define(function (require) { describe('addEvent method', function () { it('should return a function', function () { vis.handler.charts.forEach(function (chart) { - var clickEvent = function (e) { - console.log(e); - }; var addEvent = chart.events.addEvent; - - expect(_.isFunction(addEvent('click', clickEvent))).to.be(true); + expect(_.isFunction(addEvent('click', _.noop))).to.be(true); }); }); }); diff --git a/test/unit/specs/vislib/lib/y_axis.js b/test/unit/specs/vislib/lib/y_axis.js index ae89c3b9a2033..82e919bbd4f21 100644 --- a/test/unit/specs/vislib/lib/y_axis.js +++ b/test/unit/specs/vislib/lib/y_axis.js @@ -1,18 +1,42 @@ define(function (require) { - var angular = require('angular'); var _ = require('lodash'); var d3 = require('d3'); var $ = require('jquery'); - angular.module('YAxisFactory', ['kibana']); + var YAxis; + var Data; + var el; + var yAxis; + var yAxisDiv; - describe('Vislib yAxis Class Test Suite', function () { - var YAxis; - var Data; - var yAxis; - var el; - var yAxisDiv; - var dataObj; + var timeSeries = [ + 1408734060000, + 1408734090000, + 1408734120000, + 1408734150000, + 1408734180000, + 1408734210000, + 1408734240000, + 1408734270000, + 1408734300000, + 1408734330000 + ]; + + var defaultGraphData = [ + [ 8, 23, 30, 28, 36, 30, 26, 22, 29, 24 ], + [ 2, 13, 20, 18, 26, 20, 16, 12, 19, 14 ] + ]; + + function makeSeriesData(data) { + return timeSeries.map(function (timestamp, i) { + return { + x: timestamp, + y: data[i] || 0 + }; + }); + } + + function createData(seriesData) { var data = { hits: 621, label: 'test', @@ -22,129 +46,51 @@ define(function (require) { max: 1408734982458, min: 1408734082458 }, - series: [ - { - values: [ - { - x: 1408734060000, - y: 8 - }, - { - x: 1408734090000, - y: 23 - }, - { - x: 1408734120000, - y: 30 - }, - { - x: 1408734150000, - y: 28 - }, - { - x: 1408734180000, - y: 36 - }, - { - x: 1408734210000, - y: 30 - }, - { - x: 1408734240000, - y: 26 - }, - { - x: 1408734270000, - y: 22 - }, - { - x: 1408734300000, - y: 29 - }, - { - x: 1408734330000, - y: 24 - } - ] - }, - { - values: [ - { - x: 1408734060000, - y: 8 - }, - { - x: 1408734090000, - y: 23 - }, - { - x: 1408734120000, - y: 30 - }, - { - x: 1408734150000, - y: 28 - }, - { - x: 1408734180000, - y: 36 - }, - { - x: 1408734210000, - y: 30 - }, - { - x: 1408734240000, - y: 26 - }, - { - x: 1408734270000, - y: 22 - }, - { - x: 1408734300000, - y: 29 - }, - { - x: 1408734330000, - y: 24 - } - ] - } - ], + series: seriesData.map(function (series) { + return { values: makeSeriesData(series) }; + }), xAxisLabel: 'Date Histogram', yAxisLabel: 'Count' }; - beforeEach(module('YAxisFactory')); - beforeEach(inject(function (d3, Private) { - Data = Private(require('components/vislib/lib/data')); - YAxis = Private(require('components/vislib/lib/y_axis')); + var node = $('
').css({ + height: 40, + width: 40 + }) + .appendTo('body') + .addClass('y-axis-wrapper') + .get(0); - expect($('.y-axis-wrapper')).to.have.length(0); + el = d3.select(node).datum(data); - var $node = $('
').css({ - height: 40, - width: 40 - }) - .appendTo('body') - .addClass('y-axis-wrapper'); + yAxisDiv = el.append('div') + .attr('class', 'y-axis-div'); - var node = $node.get(0); + var dataObj = new Data(data, { + defaultYMin: true + }); - el = d3.select(node).datum(data); + yAxis = new YAxis({ + el: node, + yMin: dataObj.getYMinValue(), + yMax: dataObj.getYMaxValue(), + _attr: { + margin: { top: 0, right: 0, bottom: 0, left: 0 } + } + }); + } - yAxisDiv = el.append('div') - .attr('class', 'y-axis-div'); + describe('Vislib yAxis Class Test Suite', function () { + var d3Provider; - dataObj = new Data(data, {}); - yAxis = new YAxis({ - el: node, - yMax: dataObj.getYMaxValue(), - _attr: { - margin: { top: 0, right: 0, bottom: 0, left: 0 } - } - }); + beforeEach(module('kibana')); + + beforeEach(inject(function (Private, _d3_) { + d3Provider = _d3_; + Data = Private(require('components/vislib/lib/data')); + YAxis = Private(require('components/vislib/lib/y_axis')); + + expect($('.y-axis-wrapper')).to.have.length(0); })); afterEach(function () { @@ -154,6 +100,7 @@ define(function (require) { describe('render Method', function () { beforeEach(function () { + createData(defaultGraphData); expect(d3.select(yAxis.el).selectAll('.y-axis-div')).to.have.length(1); yAxis.render(); }); @@ -173,26 +120,89 @@ define(function (require) { describe('getYScale Method', function () { var yScale; + var graphData; var height = 50; - beforeEach(function () { - yScale = yAxis.getYScale(height); + function checkDomain(min, max) { + var domain = yScale.domain(); + expect(domain[0]).to.be.lessThan(min + 1); + expect(domain[1]).to.be.greaterThan(max - 1); + return domain; + } + + function checkRange() { + expect(yScale.range()[0]).to.be(height); + expect(yScale.range()[1]).to.be(0); + } + + describe('API', function () { + beforeEach(function () { + createData(defaultGraphData); + yScale = yAxis.getYScale(height); + }); + + it('should return a function', function () { + expect(_.isFunction(yScale)).to.be(true); + }); }); - it('should return a function', function () { - expect(_.isFunction(yScale)).to.be(true); + describe('positive values', function () { + beforeEach(function () { + graphData = defaultGraphData; + createData(graphData); + yScale = yAxis.getYScale(height); + }); + + + it('should have domain between 0 and max value', function () { + var min = 0; + var max = _.max(_.flatten(graphData)); + var domain = checkDomain(min, max); + expect(domain[1]).to.be.greaterThan(0); + checkRange(); + }); }); - it('should return the correct domain', function () { - expect(yScale.domain()[0]).to.be(0); - // Should be greater than 36 since we are using .nice() - expect(yScale.domain()[1]).to.be.greaterThan(36); + describe('negative values', function () { + beforeEach(function () { + graphData = [ + [ -8, -23, -30, -28, -36, -30, -26, -22, -29, -24 ], + [ -22, -8, -30, -4, 0, 0, -3, -22, -14, -24 ] + ]; + createData(graphData); + yScale = yAxis.getYScale(height); + }); + + + it('should have domain between min value and 0', function () { + var min = _.min(_.flatten(graphData)); + var max = 0; + var domain = checkDomain(min, max); + expect(domain[0]).to.be.lessThan(0); + checkRange(); + }); + }); - it('should return the correct range', function () { - expect(yScale.range()[0]).to.be(height); - // The yScale range should always start from 0 - expect(yScale.range()[1]).to.be(0); + describe('positive and negative values', function () { + beforeEach(function () { + graphData = [ + [ 8, 23, 30, 28, 36, 30, 26, 22, 29, 24 ], + [ 22, 8, -30, -4, 0, 0, 3, -22, 14, 24 ] + ]; + createData(graphData); + yScale = yAxis.getYScale(height); + }); + + + it('should have domain between min and max values', function () { + var min = _.min(_.flatten(graphData)); + var max = _.max(_.flatten(graphData)); + var domain = checkDomain(min, max); + expect(domain[0]).to.be.lessThan(0); + expect(domain[1]).to.be.greaterThan(0); + checkRange(); + }); }); }); @@ -201,6 +211,7 @@ define(function (require) { var val; beforeEach(function () { + createData(defaultGraphData); val = yAxis.formatAxisLabel(num); }); @@ -212,6 +223,7 @@ define(function (require) { describe('getYAxis method', function () { var mode, yMax, yScale; beforeEach(function () { + createData(defaultGraphData); mode = yAxis._attr.mode; yMax = yAxis.yMax; yScale = yAxis.getYScale; @@ -244,12 +256,20 @@ define(function (require) { }); describe('draw Method', function () { + beforeEach(function () { + createData(defaultGraphData); + }); + it('should be a function', function () { expect(_.isFunction(yAxis.draw())).to.be(true); }); }); describe('tickScale Method', function () { + beforeEach(function () { + createData(defaultGraphData); + }); + it('should return the correct number of ticks', function () { expect(yAxis.tickScale(1000)).to.be(11); expect(yAxis.tickScale(40)).to.be(3); @@ -257,4 +277,4 @@ define(function (require) { }); }); }); -}); +}); \ No newline at end of file diff --git a/test/unit/specs/vislib/vis.js b/test/unit/specs/vislib/vis.js index 3c55376bf94b5..ca4b37878ea3f 100644 --- a/test/unit/specs/vislib/vis.js +++ b/test/unit/specs/vislib/vis.js @@ -130,12 +130,8 @@ define(function (require) { beforeEach(function () { listeners = []; - listener1 = function (e) { - console.log(e, 'listener1'); - }; - listener2 = function (e) { - console.log(e, 'listener2'); - }; + listener1 = function () {}; + listener2 = function () {}; listeners.push(listener1); listeners.push(listener2); @@ -198,12 +194,8 @@ define(function (require) { beforeEach(function () { listeners = []; - listener1 = function (e) { - console.log(e, 'listener1'); - }; - listener2 = function (e) { - console.log(e, 'listener2'); - }; + listener1 = function () {}; + listener2 = function () {}; listeners.push(listener1); listeners.push(listener2); diff --git a/test/unit/specs/vislib/visualizations/area_chart.js b/test/unit/specs/vislib/visualizations/area_chart.js index 810009b1bd307..3d2cc60eb2cf7 100644 --- a/test/unit/specs/vislib/visualizations/area_chart.js +++ b/test/unit/specs/vislib/visualizations/area_chart.js @@ -4,20 +4,26 @@ define(function (require) { var $ = require('jquery'); // Data - var series = require('vislib_fixtures/mock_data/date_histogram/_series'); + var seriesPos = require('vislib_fixtures/mock_data/date_histogram/_series'); + var seriesPosNeg = require('vislib_fixtures/mock_data/date_histogram/_series_pos_neg'); + var seriesNeg = require('vislib_fixtures/mock_data/date_histogram/_series_neg'); var termColumns = require('vislib_fixtures/mock_data/terms/_columns'); var rangeRows = require('vislib_fixtures/mock_data/range/_rows'); var stackedSeries = require('vislib_fixtures/mock_data/date_histogram/_stacked_series'); var dataArray = [ - series, + seriesPos, + seriesPosNeg, + seriesNeg, termColumns, rangeRows, stackedSeries, ]; var names = [ - 'series', + 'series pos', + 'series pos neg', + 'series neg', 'term columns', 'range rows', 'stackedSeries', @@ -26,7 +32,8 @@ define(function (require) { var visLibParams = { type: 'area', addLegend: true, - addTooltip: true + addTooltip: true, + defaultYExtents: false }; angular.module('AreaChartFactory', ['kibana']); @@ -44,9 +51,7 @@ define(function (require) { vis = Private(require('vislib_fixtures/_vis_fixture'))(visLibParams); require('css!components/vislib/styles/main'); - vis.on('brush', function (e) { - console.log(e); - }); + vis.on('brush', _.noop); vis.render(data); }); @@ -218,6 +223,25 @@ define(function (require) { expect(_.isFunction(chart.draw())).to.be(true); }); }); + + it('should return a yMin and yMax', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + + expect(yAxis.yMin).to.not.be(undefined); + expect(yAxis.yMax).to.not.be(undefined); + }); + }); + + it('should render a zero axis line', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + + if (yAxis.yMin < 0 && yAxis.yMax > 0) { + expect($(chart.chartEl).find('line.zero-line').length).to.be(1); + } + }); + }); }); describe('containerTooSmall error', function () { @@ -234,6 +258,23 @@ define(function (require) { }); }); }); + + describe('defaultYExtents is true', function () { + beforeEach(function () { + vis._attr.defaultYExtents = true; + vis.render(data); + }); + + it('should return yAxis extents equal to data extents', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + var yVals = [vis.handler.data.getYMinValue(), vis.handler.data.getYMaxValue()]; + + expect(yAxis.yMin).to.equal(yVals[0]); + expect(yAxis.yMax).to.equal(yVals[1]); + }); + }); + }); }); }); }); diff --git a/test/unit/specs/vislib/visualizations/column_chart.js b/test/unit/specs/vislib/visualizations/column_chart.js index bfb1b15a07052..49330f3c39547 100644 --- a/test/unit/specs/vislib/visualizations/column_chart.js +++ b/test/unit/specs/vislib/visualizations/column_chart.js @@ -6,22 +6,30 @@ define(function (require) { // Data var series = require('vislib_fixtures/mock_data/date_histogram/_series'); + var seriesPosNeg = require('vislib_fixtures/mock_data/date_histogram/_series_pos_neg'); + var seriesNeg = require('vislib_fixtures/mock_data/date_histogram/_series_neg'); var termsColumns = require('vislib_fixtures/mock_data/terms/_columns'); //var histogramRows = require('vislib_fixtures/mock_data/histogram/_rows'); var stackedSeries = require('vislib_fixtures/mock_data/date_histogram/_stacked_series'); var dataArray = [ series, + seriesPosNeg, + seriesNeg, termsColumns, //histogramRows, stackedSeries ]; var names = [ 'series', + 'series with positive and negative values', + 'series with negative values', 'terms columns', //'histogram rows', 'stackedSeries' ]; var modes = [ + 'stacked', + 'stacked', 'stacked', 'grouped', //'percentage', @@ -153,6 +161,25 @@ define(function (require) { expect(_.isFunction(chart.draw())).to.be(true); }); }); + + it('should return a yMin and yMax', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + + expect(yAxis.yMin).to.not.be(undefined); + expect(yAxis.yMax).to.not.be(undefined); + }); + }); + + it('should render a zero axis line', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + + if (yAxis.yMin < 0 && yAxis.yMax > 0) { + expect($(chart.chartEl).find('line.zero-line').length).to.be(1); + } + }); + }); }); describe('containerTooSmall error', function () { @@ -169,6 +196,23 @@ define(function (require) { }); }); }); + + describe('defaultYExtents is true', function () { + beforeEach(function () { + vis._attr.defaultYExtents = true; + vis.render(data); + }); + + it('should return yAxis extents equal to data extents', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + var yVals = [vis.handler.data.getYMinValue(), vis.handler.data.getYMaxValue()]; + + expect(yAxis.yMin).to.equal(yVals[0]); + expect(yAxis.yMax).to.equal(yVals[1]); + }); + }); + }); }); }); }); diff --git a/test/unit/specs/vislib/visualizations/line_chart.js b/test/unit/specs/vislib/visualizations/line_chart.js index 4c3f504fdc933..79edddeb5b9cd 100644 --- a/test/unit/specs/vislib/visualizations/line_chart.js +++ b/test/unit/specs/vislib/visualizations/line_chart.js @@ -4,18 +4,24 @@ define(function (require) { var $ = require('jquery'); // Data - var series = require('vislib_fixtures/mock_data/date_histogram/_series'); + var seriesPos = require('vislib_fixtures/mock_data/date_histogram/_series'); + var seriesPosNeg = require('vislib_fixtures/mock_data/date_histogram/_series_pos_neg'); + var seriesNeg = require('vislib_fixtures/mock_data/date_histogram/_series_neg'); var histogramColumns = require('vislib_fixtures/mock_data/histogram/_columns'); var rangeRows = require('vislib_fixtures/mock_data/range/_rows'); var termSeries = require('vislib_fixtures/mock_data/terms/_series'); var dateHistogramArray = [ - series, + seriesPos, + seriesPosNeg, + seriesNeg, histogramColumns, rangeRows, termSeries, ]; var names = [ - 'series', + 'series pos', + 'series pos neg', + 'series neg', 'histogram columns', 'range rows', 'term series', @@ -41,9 +47,7 @@ define(function (require) { vis = Private(require('vislib_fixtures/_vis_fixture'))(visLibParams); require('css!components/vislib/styles/main'); - vis.on('brush', function (e) { - console.log(e); - }); + vis.on('brush', _.noop); vis.render(data); }); @@ -133,6 +137,25 @@ define(function (require) { expect(chart.draw()).to.be.a(Function); }); }); + + it('should return a yMin and yMax', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + + expect(yAxis.yMin).to.not.be(undefined); + expect(yAxis.yMax).to.not.be(undefined); + }); + }); + + it('should render a zero axis line', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + + if (yAxis.yMin < 0 && yAxis.yMax > 0) { + expect($(chart.chartEl).find('line.zero-line').length).to.be(1); + } + }); + }); }); describe('containerTooSmall error', function () { @@ -150,6 +173,22 @@ define(function (require) { }); }); + describe('defaultYExtents is true', function () { + beforeEach(function () { + vis._attr.defaultYExtents = true; + vis.render(data); + }); + + it('should return yAxis extents equal to data extents', function () { + vis.handler.charts.forEach(function (chart) { + var yAxis = chart.handler.yAxis; + var yVals = [vis.handler.data.getYMinValue(), vis.handler.data.getYMaxValue()]; + + expect(yAxis.yMin).to.equal(yVals[0]); + expect(yAxis.yMax).to.equal(yVals[1]); + }); + }); + }); }); }); }); diff --git a/test/utils/agg_param_writer.js b/test/utils/agg_param_writer.js index fd01ed95aacd7..89e5102c46c97 100644 --- a/test/utils/agg_param_writer.js +++ b/test/utils/agg_param_writer.js @@ -91,6 +91,12 @@ define(function (require) { return aggConfig.type === self.aggType; }); + aggConfig.type.params.forEach(function (param) { + if (param.onRequest) { + param.onRequest(aggConfig); + } + }); + return aggConfig.type.params.write(aggConfig); };