mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 18:27:59 -04:00
We need to share `@kbn/datemath` with `@elastic/eui`, and rather than making them rely on Kibana for their dependencies we've decided to republish `@kbn/datemath` as `@elastic/datemath`. This isn't something we want to do often, so please check with the platform team if you'd like to do this for another module.
394 lines
13 KiB
JavaScript
394 lines
13 KiB
JavaScript
/*
|
|
* Licensed to Elasticsearch B.V. under one or more contributor
|
|
* license agreements. See the NOTICE file distributed with
|
|
* this work for additional information regarding copyright
|
|
* ownership. Elasticsearch B.V. licenses this file to you under
|
|
* the Apache License, Version 2.0 (the "License"); you may
|
|
* not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
import dateMath from '../src/index';
|
|
import moment from 'moment';
|
|
import sinon from 'sinon';
|
|
import expect from 'expect.js';
|
|
|
|
/**
|
|
* Require a new instance of the moment library, bypassing the require cache.
|
|
* This is needed, since we are trying to test whether or not this library works
|
|
* when passing in a different configured moment instance. If we would change
|
|
* the locales on the imported moment, it would automatically apply
|
|
* to the source code, even without passing it in to the method, since they share
|
|
* the same global state. This method avoids this, by loading a separate instance
|
|
* of moment, by deleting the require cache and require the library again.
|
|
*/
|
|
function momentClone() {
|
|
delete require.cache[require.resolve('moment')];
|
|
return require('moment');
|
|
}
|
|
|
|
describe('dateMath', function() {
|
|
// Test each of these intervals when testing relative time
|
|
const spans = ['s', 'm', 'h', 'd', 'w', 'M', 'y', 'ms'];
|
|
const anchor = '2014-01-01T06:06:06.666Z';
|
|
const anchoredDate = new Date(Date.parse(anchor));
|
|
const unix = moment(anchor).valueOf();
|
|
const format = 'YYYY-MM-DDTHH:mm:ss.SSSZ';
|
|
let clock;
|
|
|
|
describe('errors', function() {
|
|
it('should return undefined if passed something falsy', function() {
|
|
expect(dateMath.parse()).to.be(undefined);
|
|
});
|
|
|
|
it('should return undefined if I pass an operator besides [+-/]', function() {
|
|
expect(dateMath.parse('now&1d')).to.be(undefined);
|
|
});
|
|
|
|
it('should return undefined if I pass a unit besides' + spans.toString(), function() {
|
|
expect(dateMath.parse('now+5f')).to.be(undefined);
|
|
});
|
|
|
|
it('should return undefined if rounding unit is not 1', function() {
|
|
expect(dateMath.parse('now/2y')).to.be(undefined);
|
|
expect(dateMath.parse('now/0.5y')).to.be(undefined);
|
|
});
|
|
|
|
it('should not go into an infinite loop when missing a unit', function() {
|
|
expect(dateMath.parse('now-0')).to.be(undefined);
|
|
expect(dateMath.parse('now-00')).to.be(undefined);
|
|
expect(dateMath.parse('now-000')).to.be(undefined);
|
|
});
|
|
|
|
describe('forceNow', function() {
|
|
it('should throw an Error if passed a string', function() {
|
|
const fn = () => dateMath.parse('now', { forceNow: '2000-01-01T00:00:00.000Z' });
|
|
expect(fn).to.throwError();
|
|
});
|
|
|
|
it('should throw an Error if passed a moment', function() {
|
|
expect(() => dateMath.parse('now', { forceNow: moment() })).to.throwError();
|
|
});
|
|
|
|
it('should throw an Error if passed an invalid date', function() {
|
|
expect(() => dateMath.parse('now', { forceNow: new Date('foobar') })).to.throwError();
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('objects and strings', function() {
|
|
let mmnt;
|
|
let date;
|
|
let string;
|
|
let now;
|
|
|
|
beforeEach(function() {
|
|
clock = sinon.useFakeTimers(unix);
|
|
now = moment();
|
|
mmnt = moment(anchor);
|
|
date = mmnt.toDate();
|
|
string = mmnt.format(format);
|
|
});
|
|
|
|
afterEach(function() {
|
|
clock.restore();
|
|
});
|
|
|
|
it('should return the same moment if passed a moment', function() {
|
|
expect(dateMath.parse(mmnt)).to.eql(mmnt);
|
|
});
|
|
|
|
it('should return a moment if passed a date', function() {
|
|
expect(dateMath.parse(date).format(format)).to.eql(mmnt.format(format));
|
|
});
|
|
|
|
it('should return a moment if passed an ISO8601 string', function() {
|
|
expect(dateMath.parse(string).format(format)).to.eql(mmnt.format(format));
|
|
});
|
|
|
|
it('should return the current time when parsing now', function() {
|
|
expect(dateMath.parse('now').format(format)).to.eql(now.format(format));
|
|
});
|
|
|
|
it('should use the forceNow parameter when parsing now', function() {
|
|
expect(dateMath.parse('now', { forceNow: anchoredDate }).valueOf()).to.eql(unix);
|
|
});
|
|
});
|
|
|
|
describe('subtraction', function() {
|
|
let now;
|
|
let anchored;
|
|
|
|
beforeEach(function() {
|
|
clock = sinon.useFakeTimers(unix);
|
|
now = moment();
|
|
anchored = moment(anchor);
|
|
});
|
|
|
|
afterEach(function() {
|
|
clock.restore();
|
|
});
|
|
|
|
[5, 12, 247].forEach(len => {
|
|
spans.forEach(span => {
|
|
const nowEx = `now-${len}${span}`;
|
|
const thenEx = `${anchor}||-${len}${span}`;
|
|
|
|
it('should return ' + len + span + ' ago', function() {
|
|
const parsed = dateMath.parse(nowEx).format(format);
|
|
expect(parsed).to.eql(now.subtract(len, span).format(format));
|
|
});
|
|
|
|
it('should return ' + len + span + ' before ' + anchor, function() {
|
|
const parsed = dateMath.parse(thenEx).format(format);
|
|
expect(parsed).to.eql(anchored.subtract(len, span).format(format));
|
|
});
|
|
|
|
it('should return ' + len + span + ' before forceNow', function() {
|
|
const parsed = dateMath.parse(nowEx, { forceNow: anchoredDate }).valueOf();
|
|
expect(parsed).to.eql(anchored.subtract(len, span).valueOf());
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('addition', function() {
|
|
let now;
|
|
let anchored;
|
|
|
|
beforeEach(function() {
|
|
clock = sinon.useFakeTimers(unix);
|
|
now = moment();
|
|
anchored = moment(anchor);
|
|
});
|
|
|
|
afterEach(function() {
|
|
clock.restore();
|
|
});
|
|
|
|
[5, 12, 247].forEach(len => {
|
|
spans.forEach(span => {
|
|
const nowEx = `now+${len}${span}`;
|
|
const thenEx = `${anchor}||+${len}${span}`;
|
|
|
|
it('should return ' + len + span + ' from now', function() {
|
|
expect(dateMath.parse(nowEx).format(format)).to.eql(now.add(len, span).format(format));
|
|
});
|
|
|
|
it('should return ' + len + span + ' after ' + anchor, function() {
|
|
expect(dateMath.parse(thenEx).format(format)).to.eql(
|
|
anchored.add(len, span).format(format)
|
|
);
|
|
});
|
|
|
|
it('should return ' + len + span + ' after forceNow', function() {
|
|
expect(dateMath.parse(nowEx, { forceNow: anchoredDate }).valueOf()).to.eql(
|
|
anchored.add(len, span).valueOf()
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('rounding', function() {
|
|
let now;
|
|
let anchored;
|
|
|
|
beforeEach(function() {
|
|
clock = sinon.useFakeTimers(unix);
|
|
now = moment();
|
|
anchored = moment(anchor);
|
|
});
|
|
|
|
afterEach(function() {
|
|
clock.restore();
|
|
});
|
|
|
|
spans.forEach(span => {
|
|
it(`should round now to the beginning of the ${span}`, function() {
|
|
expect(dateMath.parse('now/' + span).format(format)).to.eql(
|
|
now.startOf(span).format(format)
|
|
);
|
|
});
|
|
|
|
it(`should round now to the beginning of forceNow's ${span}`, function() {
|
|
expect(dateMath.parse('now/' + span, { forceNow: anchoredDate }).valueOf()).to.eql(
|
|
anchored.startOf(span).valueOf()
|
|
);
|
|
});
|
|
|
|
it(`should round now to the end of the ${span}`, function() {
|
|
expect(dateMath.parse('now/' + span, { roundUp: true }).format(format)).to.eql(
|
|
now.endOf(span).format(format)
|
|
);
|
|
});
|
|
|
|
it(`should round now to the end of forceNow's ${span}`, function() {
|
|
expect(
|
|
dateMath.parse('now/' + span, { roundUp: true, forceNow: anchoredDate }).valueOf()
|
|
).to.eql(anchored.endOf(span).valueOf());
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('math and rounding', function() {
|
|
let now;
|
|
let anchored;
|
|
|
|
beforeEach(function() {
|
|
clock = sinon.useFakeTimers(unix);
|
|
now = moment();
|
|
anchored = moment(anchor);
|
|
});
|
|
|
|
afterEach(function() {
|
|
clock.restore();
|
|
});
|
|
|
|
it('should round to the nearest second with 0 value', function() {
|
|
const val = dateMath.parse('now-0s/s').format(format);
|
|
expect(val).to.eql(now.startOf('s').format(format));
|
|
});
|
|
|
|
it('should subtract 17s, rounded to the nearest second', function() {
|
|
const val = dateMath.parse('now-17s/s').format(format);
|
|
expect(val).to.eql(
|
|
now
|
|
.startOf('s')
|
|
.subtract(17, 's')
|
|
.format(format)
|
|
);
|
|
});
|
|
|
|
it('should add 555ms, rounded to the nearest millisecond', function() {
|
|
const val = dateMath.parse('now+555ms/ms').format(format);
|
|
expect(val).to.eql(
|
|
now
|
|
.add(555, 'ms')
|
|
.startOf('ms')
|
|
.format(format)
|
|
);
|
|
});
|
|
|
|
it('should subtract 555ms, rounded to the nearest second', function() {
|
|
const val = dateMath.parse('now-555ms/s').format(format);
|
|
expect(val).to.eql(
|
|
now
|
|
.subtract(555, 'ms')
|
|
.startOf('s')
|
|
.format(format)
|
|
);
|
|
});
|
|
|
|
it('should round weeks to Sunday by default', function() {
|
|
const val = dateMath.parse('now-1w/w');
|
|
expect(val.isoWeekday()).to.eql(7);
|
|
});
|
|
|
|
it('should round weeks based on the passed moment locale start of week setting', function() {
|
|
const m = momentClone();
|
|
// Define a locale, that has Tuesday as beginning of the week
|
|
m.defineLocale('x-test', {
|
|
week: { dow: 2 },
|
|
});
|
|
const val = dateMath.parse('now-1w/w', { momentInstance: m });
|
|
expect(val.isoWeekday()).to.eql(2);
|
|
});
|
|
|
|
it('should round up weeks based on the passed moment locale start of week setting', function() {
|
|
const m = momentClone();
|
|
// Define a locale, that has Tuesday as beginning of the week
|
|
m.defineLocale('x-test', {
|
|
week: { dow: 3 },
|
|
});
|
|
const val = dateMath.parse('now-1w/w', {
|
|
roundUp: true,
|
|
momentInstance: m,
|
|
});
|
|
// The end of the range (rounding up) should be the last day of the week (so one day before)
|
|
// our start of the week, that's why 3 - 1
|
|
expect(val.isoWeekday()).to.eql(3 - 1);
|
|
});
|
|
|
|
it('should round relative to forceNow', function() {
|
|
const val = dateMath.parse('now-0s/s', { forceNow: anchoredDate }).valueOf();
|
|
expect(val).to.eql(anchored.startOf('s').valueOf());
|
|
});
|
|
|
|
it('should parse long expressions', () => {
|
|
expect(dateMath.parse('now-1d/d+8h+50m')).to.be.ok();
|
|
});
|
|
});
|
|
|
|
describe('used momentjs instance', function() {
|
|
it('should use the default moment instance if parameter not specified', function() {
|
|
const momentSpy = sinon.spy(moment, 'isMoment');
|
|
dateMath.parse('now');
|
|
expect(momentSpy.called).to.be(true);
|
|
momentSpy.restore();
|
|
});
|
|
|
|
it('should not use default moment instance if parameter is specified', function() {
|
|
const m = momentClone();
|
|
const momentSpy = sinon.spy(moment, 'isMoment');
|
|
const cloneSpy = sinon.spy(m, 'isMoment');
|
|
dateMath.parse('now', { momentInstance: m });
|
|
expect(momentSpy.called).to.be(false);
|
|
expect(cloneSpy.called).to.be(true);
|
|
momentSpy.restore();
|
|
cloneSpy.restore();
|
|
});
|
|
|
|
it('should work with multiple different instances', function() {
|
|
const m1 = momentClone();
|
|
const m2 = momentClone();
|
|
const m1Spy = sinon.spy(m1, 'isMoment');
|
|
const m2Spy = sinon.spy(m2, 'isMoment');
|
|
dateMath.parse('now', { momentInstance: m1 });
|
|
expect(m1Spy.called).to.be(true);
|
|
expect(m2Spy.called).to.be(false);
|
|
m1Spy.resetHistory();
|
|
m2Spy.resetHistory();
|
|
dateMath.parse('now', { momentInstance: m2 });
|
|
expect(m1Spy.called).to.be(false);
|
|
expect(m2Spy.called).to.be(true);
|
|
m1Spy.restore();
|
|
m2Spy.restore();
|
|
});
|
|
|
|
it('should use global instance after passing an instance', function() {
|
|
const m = momentClone();
|
|
const momentSpy = sinon.spy(moment, 'isMoment');
|
|
const cloneSpy = sinon.spy(m, 'isMoment');
|
|
dateMath.parse('now', { momentInstance: m });
|
|
expect(momentSpy.called).to.be(false);
|
|
expect(cloneSpy.called).to.be(true);
|
|
momentSpy.resetHistory();
|
|
cloneSpy.resetHistory();
|
|
dateMath.parse('now');
|
|
expect(momentSpy.called).to.be(true);
|
|
expect(cloneSpy.called).to.be(false);
|
|
momentSpy.restore();
|
|
cloneSpy.restore();
|
|
});
|
|
});
|
|
|
|
describe('units', function() {
|
|
it('should have units descending for unitsDesc', function() {
|
|
expect(dateMath.unitsDesc).to.eql(['y', 'M', 'w', 'd', 'h', 'm', 's', 'ms']);
|
|
});
|
|
|
|
it('should have units ascending for unitsAsc', function() {
|
|
expect(dateMath.unitsAsc).to.eql(['ms', 's', 'm', 'h', 'd', 'w', 'M', 'y']);
|
|
});
|
|
});
|
|
});
|