Fix bug where negative time offsets in visual builder will shift x-axis range (#15554) (#15650)

* Shift negative series before drawing, fix #15553

* Add tests fot TSVB markdown

* Use waitUntilLoadingHasFinished

* Remove forgotten skip

* Switch test order

* Use generic seriesOption

* Rename test from chart to metric

* Use test subj to find metric tabs

* Improve TSVB tests
This commit is contained in:
Tim Roes 2017-12-16 11:59:51 +01:00 committed by GitHub
parent 5d9f5a6dd9
commit e5eb9d0713
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 153 additions and 25 deletions

View file

@ -148,6 +148,7 @@ class MarkdownPanelConfig extends Component {
>Markdown
</button>
<button
data-test-subj="markdownDataBtn"
role="tab"
aria-selected={selectedTab === 'data'}
className={`kbnTabs__tab${selectedTab === 'data' && '-active' || ''}`}

View file

@ -36,6 +36,7 @@ export const SeriesConfig = props => {
Offset series time by (1m, 1h, 1w, 1d)
</label>
<input
data-test-subj="offsetTimeSeries"
style={{ width: 100 }}
id={htmlId('offsetSeries')}
className="vis_editor__input-grows"

View file

@ -12,7 +12,12 @@ function VisPickerItem(props) {
labelClassName += ' selected';
}
return (
<button role="tab" className={itemClassName} onClick={() => props.onClick(type)}>
<button
role="tab"
className={itemClassName}
onClick={() => props.onClick(type)}
data-test-subj={`${type}TsvbTypeBtn`}
>
<div className={iconClassName}>
<i className={`fa ${icon}`} />
</div>

View file

@ -88,6 +88,7 @@ function GaugeSeries(props) {
</button>
<button
role="tab"
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'options'}
className={optionsClassname}
onClick={() => props.switchTab('options')}

View file

@ -85,6 +85,7 @@ function MarkdownSeries(props) {
</button>
<button
role="tab"
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'metrics'}
className={optionsClassname}
onClick={() => props.switchTab('options')}

View file

@ -88,6 +88,7 @@ function MetricSeries(props) {
</button>
<button
role="tab"
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'options'}
className={optionsClassname}
onClick={() => props.switchTab('options')}

View file

@ -71,6 +71,7 @@ function TopNSeries(props) {
Metrics
</div>
<div
data-test-subj="seriesOptions"
className={optionsClassname}
onClick={() => props.switchTab('options')}
>

View file

@ -195,6 +195,7 @@ function TimeseriesConfig(props) {
</label>
<input
id={htmlId('offset')}
data-test-subj="offsetTimeSeries"
className="vis_editor__input-grows"
type="text"
onChange={handleTextChange('offset_time')}

View file

@ -88,6 +88,7 @@ function TimeseriesSeries(props) {
</button>
<button
role="tab"
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'options'}
className={optionsClassname}
onClick={() => props.switchTab('options')}

View file

@ -86,6 +86,7 @@ function TopNSeries(props) {
</button>
<button
role="tab"
data-test-subj="seriesOptions"
aria-selected={selectedTab === 'options'}
className={optionsClassname}
onClick={() => props.switchTab('options')}

View file

@ -2,8 +2,8 @@ import _ from 'lodash';
import moment from 'moment';
export default function timeShift(resp, panel, series) {
return next => results => {
if (/^([\d]+)([shmdwMy]|ms)$/.test(series.offset_time)) {
const matches = series.offset_time.match(/^([\d]+)([shmdwMy]|ms)$/);
if (/^([+-]?[\d]+)([shmdwMy]|ms)$/.test(series.offset_time)) {
const matches = series.offset_time.match(/^([+-]?[\d]+)([shmdwMy]|ms)$/);
if (matches) {
const offsetValue = matches[1];
const offsetUnit = matches[2];

View file

@ -22,26 +22,50 @@ export default function ({ getService, getPageObjects }) {
.then(function () {
return PageObjects.header.waitUntilLoadingHasFinished();
})
.then(function sleep() {
return PageObjects.common.sleep(1003);
})
.then(function clickMetric() {
return PageObjects.visualBuilder.clickMetric();
})
.then(function sleep() {
return PageObjects.common.sleep(1003);
});
});
describe('Visual Builder Time Series', function () {
describe('Visual Builder chart', function indexPatternCreation() {
it('should show the correct count in the legend', async function () {
const actualCount = await PageObjects.visualBuilder.getRhythmChartLegendValue();
expect(actualCount).to.be('156');
});
it('should show the correct count in the legend with 2h offset', async function () {
await PageObjects.visualBuilder.clickSeriesOption();
await PageObjects.visualBuilder.enterOffsetSeries('2h');
const actualCount = await PageObjects.visualBuilder.getRhythmChartLegendValue();
expect(actualCount).to.be('293');
});
it('should show the correct count in the legend with -2h offset', async function () {
await PageObjects.visualBuilder.enterOffsetSeries('-2h');
const actualCount = await PageObjects.visualBuilder.getRhythmChartLegendValue();
expect(actualCount).to.be('53');
});
after(async () => {
// set back to no offset for the next test, an empty string didn't seem to work here
await PageObjects.visualBuilder.enterOffsetSeries('0h');
});
});
describe('Visual Builder metric', () => {
before(async () => {
await PageObjects.visualBuilder.clickMetric();
await PageObjects.common.sleep(1003);
});
it('should not display spy panel toggle button', async function () {
const spyToggleExists = await PageObjects.visualize.getSpyToggleExists();
expect(spyToggleExists).to.be(false);
});
it('should show correct data', function () {
it('should show correct data', async function () {
const expectedMetricValue = '156';
return PageObjects.visualBuilder.getMetricValue()
@ -51,5 +75,51 @@ export default function ({ getService, getPageObjects }) {
});
});
});
describe('Visual Builder markdown', () => {
before(async () => {
await PageObjects.visualBuilder.clickMarkdown();
await PageObjects.common.sleep(1003);
await PageObjects.header.setAbsoluteRange('2015-09-22 06:00:00.000', '2015-09-22 11:00:00.000');
});
it('should allow printing raw timestamp of data', async () => {
await PageObjects.visualBuilder.enterMarkdown('{{ count.data.raw.[0].[0] }}');
const text = await PageObjects.visualBuilder.getMarkdownText();
expect(text).to.be('1442901600000');
});
it('should allow printing raw value of data', async () => {
await PageObjects.visualBuilder.enterMarkdown('{{ count.data.raw.[0].[1] }}');
const text = await PageObjects.visualBuilder.getMarkdownText();
expect(text).to.be('6');
});
describe('allow time offsets', () => {
before(async () => {
await PageObjects.visualBuilder.enterMarkdown('{{ count.data.raw.[0].[0] }}#{{ count.data.raw.[0].[1] }}');
await PageObjects.visualBuilder.clickMarkdownData();
await PageObjects.visualBuilder.clickSeriesOption();
});
it('allow positive time offsets', async () => {
await PageObjects.visualBuilder.enterOffsetSeries('2h');
const text = await PageObjects.visualBuilder.getMarkdownText();
const [timestamp, value] = text.split('#');
expect(timestamp).to.be('1442901600000');
expect(value).to.be('3');
});
it('allow negative time offsets', async () => {
await PageObjects.visualBuilder.enterOffsetSeries('-2h');
const text = await PageObjects.visualBuilder.getMarkdownText();
const [timestamp, value] = text.split('#');
expect(timestamp).to.be('1442901600000');
expect(value).to.be('23');
});
});
});
});
}

View file

@ -1,27 +1,71 @@
export function VisualBuilderPageProvider({ getService }) {
const config = getService('config');
const find = getService('find');
const log = getService('log');
import Keys from 'leadfoot/keys';
const defaultFindTimeout = config.get('timeouts.find');
export function VisualBuilderPageProvider({ getService, getPageObjects }) {
const find = getService('find');
const testSubjects = getService('testSubjects');
const PageObjects = getPageObjects(['common', 'header']);
class VisualBuilderPage {
async clickMetric() {
const nav = await find.allByCssSelector('.vis_editor__vis_picker-label', defaultFindTimeout);
log.debug('found navigation items: ' + nav.length);
await Promise.all(nav.map(async function (link) {
const text = await link.getVisibleText();
log.debug('nav text:' + text);
if (text === 'Metric') {
await link.click();
}
}));
const button = await testSubjects.find('metricTsvbTypeBtn');
await button.click();
}
async clickMarkdown() {
const button = await testSubjects.find('markdownTsvbTypeBtn');
await button.click();
}
async getMetricValue() {
const metricValue = await find.byCssSelector('.rhythm_metric__primary-value');
return metricValue.getVisibleText();
}
async enterMarkdown(markdown) {
const input = await find.byCssSelector('.vis_editor__markdown-editor textarea');
// Since we use ACE editor and that isn't really storing its value inside
// a textarea we must really select all text and remove it, and cannot use
// clearValue().
await input.session.pressKeys([Keys.CONTROL, 'a']); // Select all text
await input.session.pressKeys(Keys.NULL); // Release modifier keys
await input.session.pressKeys(Keys.BACKSPACE); // Delete all content
await input.type(markdown);
await PageObjects.header.waitUntilLoadingHasFinished();
}
async getMarkdownText() {
const el = await find.byCssSelector('.vis_editor__visualization');
return await el.getVisibleText();
}
async clickMarkdownData() {
await testSubjects.click('markdownDataBtn');
}
async clickSeriesOption(nth = 0) {
const el = await testSubjects.findAll('seriesOptions');
await el[nth].click();
await PageObjects.common.sleep(300);
}
async clearOffsetSeries() {
const el = await testSubjects.find('offsetTimeSeries');
await el.clearValue();
await PageObjects.header.waitUntilLoadingHasFinished();
}
async enterOffsetSeries(value) {
const el = await testSubjects.find('offsetTimeSeries');
await el.clearValue();
await el.type(value);
await PageObjects.header.waitUntilLoadingHasFinished();
}
async getRhythmChartLegendValue() {
const metricValue = await find.byCssSelector('.rhythm_chart__legend_value');
await metricValue.session.moveMouseTo(metricValue);
return await metricValue.getVisibleText();
}
}
return new VisualBuilderPage();