diff --git a/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx b/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
index 40702c39deaf4..047de1bdb0708 100644
--- a/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_interval.tsx
@@ -126,6 +126,7 @@ function TimelionInterval({ value, setValue, setValidity }: TimelionIntervalProp
placeholder={i18n.translate('timelion.vis.selectIntervalPlaceholder', {
defaultMessage: 'Select an interval',
})}
+ data-test-subj="timelionIntervalComboBox"
/>
);
diff --git a/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx b/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx
index 4690f4fe11e45..7f422b2a00710 100644
--- a/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx
+++ b/src/plugins/vis_type_timelion/public/components/timelion_vis_component.tsx
@@ -43,6 +43,15 @@ import type { Series } from '../helpers/timelion_request_handler';
import './timelion_vis.scss';
+declare global {
+ interface Window {
+ /**
+ * Flag used to enable debugState on elastic charts
+ */
+ _echDebugStateFlag?: boolean;
+ }
+}
+
interface TimelionVisComponentProps {
interval: string;
seriesList: Sheet;
@@ -174,7 +183,7 @@ const TimelionVisComponent = ({
}, [chart]);
return (
-
+
{title && (
{title}
@@ -182,6 +191,7 @@ const TimelionVisComponent = ({
)}
{
+ before(async () => {
+ await kibanaServer.uiSettings.update({
+ 'timelion:legacyChartsLibrary': false,
+ });
+ await visualize.initTests(true);
+ await visualize.navigateToNewAggBasedVisualization();
+ await visualize.clickTimelion();
+ await timePicker.setDefaultAbsoluteRange();
+ });
+
+ const initVisualization = async (expression: string, interval: string = '12h') => {
+ await visEditor.setTimelionInterval(interval);
+ await monacoEditor.setCodeEditorValue(expression);
+ await visEditor.clickGo();
+ };
+
+ it('should display correct data for specified index pattern and timefield', async () => {
+ await initVisualization('.es(index=long-window-logstash-*,timefield=@timestamp)');
+
+ const chartData = await visChart.getAreaChartData('q:* > count');
+ expect(chartData).to.eql([3, 5, 2, 6, 1, 6, 1, 7, 0, 0]);
+ });
+
+ it('should display correct chart colors for multiple expressions', async () => {
+ const expectedColors = ['#01A4A4', '#FFCFDF', '#BFA7DA', '#AD7DE6'];
+ await initVisualization(
+ '.es(*), .es(*).color("#FFCFDF"), .es(*).color("#BFA7DA"), .es(*).color("#AD7DE6")'
+ );
+
+ const areas = (await elasticChart.getChartDebugData())?.areas;
+ expect(areas?.map(({ color }) => color)).to.eql(expectedColors);
+ });
+
+ it('should display correct chart data for average, min, max and cardinality aggregations', async () => {
+ await initVisualization(
+ '.es(index=logstash-*,metric=avg:bytes), .es(index=logstash-*,metric=min:bytes),' +
+ '.es(index=logstash-*,metric=max:bytes,), .es(index=logstash-*,metric=cardinality:bytes)',
+ '36h'
+ );
+
+ const firstAreaChartData = await visChart.getAreaChartData('q:* > avg(bytes)');
+ const secondAreaChartData = await visChart.getAreaChartData('q:* > min(bytes)');
+ const thirdAreaChartData = await visChart.getAreaChartData('q:* > max(bytes)');
+ const forthAreaChartData = await visChart.getAreaChartData('q:* > cardinality(bytes)');
+
+ expect(firstAreaChartData).to.eql([5732.783676366217, 5721.775973559419]);
+ expect(secondAreaChartData).to.eql([0, 0]);
+ expect(thirdAreaChartData).to.eql([19985, 19986]);
+ expect(forthAreaChartData).to.eql([5019, 4958, 0, 0]);
+ });
+
+ it('should display correct chart data for expressions using functions', async () => {
+ const firstAreaExpectedChartData = [3, 2421, 2343, 2294, 2327, 2328, 2312, 7, 0, 0];
+ const thirdAreaExpectedChartData = [200, 167, 199, 200, 200, 198, 108, 200, 200];
+ const forthAreaExpectedChartData = [150, 50, 50, 50, 50, 50, 50, 150, 150, 150];
+ await initVisualization(
+ '.es(*).label("initial"),' +
+ '.es(*).add(term=.es(*).multiply(-1).abs()).divide(2).label("add multiply abs divide"),' +
+ '.es(q="bytes<100").derivative().sum(200).min(200).label("query derivative min sum"),' +
+ '.es(*).if(operator=gt,if=200,then=50,else=150).label("condition")'
+ );
+
+ const firstAreaChartData = await visChart.getAreaChartData('initial');
+ const secondAreaChartData = await visChart.getAreaChartData('add multiply abs divide');
+ const thirdAreaChartData = await visChart.getAreaChartData('query derivative min sum');
+ const forthAreaChartData = await visChart.getAreaChartData('condition');
+
+ expect(firstAreaChartData).to.eql(firstAreaExpectedChartData);
+ expect(secondAreaChartData).to.eql(firstAreaExpectedChartData);
+ expect(thirdAreaChartData).to.eql(thirdAreaExpectedChartData);
+ expect(forthAreaChartData).to.eql(forthAreaExpectedChartData);
+ });
+
+ it('should display correct chart title, data and labels for expressions with custom labels, yaxis and offset', async () => {
+ const firstAreaExpectedChartData = [13112352443.375292, 13095637741.055172];
+ const secondAreaExpectedChartData = [
+ [1442642400000, 5732.783676366217],
+ [1442772000000, 5721.775973559419],
+ ];
+ const thirdAreaExpectedChartData = [
+ [1442772000000, 5732.783676366217],
+ [1442901600000, 5721.775973559419],
+ ];
+ await initVisualization(
+ '.es(index=logstash*,timefield="@timestamp",metric=avg:machine.ram).label("Average Machine RAM amount").yaxis(2,units=bytes,position=right),' +
+ '.es(index=logstash*,timefield="@timestamp",metric=avg:bytes).label("Average Bytes for request").yaxis(1,units=bytes,position=left),' +
+ '.es(index=logstash*,timefield="@timestamp",metric=avg:bytes, offset=-12h).label("Average Bytes for request with offset").yaxis(3,units=custom:BYTES_,position=right)',
+ '36h'
+ );
+
+ const leftAxesCount = await visChart.getAxesCountByPosition('left');
+ const rightAxesCount = await visChart.getAxesCountByPosition('right');
+ const firstAxesLabels = await visChart.getYAxisLabels();
+ const secondAxesLabels = await visChart.getYAxisLabels(1);
+ const thirdAxesLabels = await visChart.getYAxisLabels(2);
+ const firstAreaChartData = await visChart.getAreaChartData('Average Machine RAM amount');
+ const secondAreaChartData = await visChart.getAreaChartData(
+ 'Average Bytes for request',
+ undefined,
+ true
+ );
+ const thirdAreaChartData = await visChart.getAreaChartData(
+ 'Average Bytes for request with offset',
+ undefined,
+ true
+ );
+
+ expect(leftAxesCount).to.be(1);
+ expect(rightAxesCount).to.be(2);
+ expect(firstAreaChartData).to.eql(firstAreaExpectedChartData);
+ expect(secondAreaChartData).to.eql(secondAreaExpectedChartData);
+ expect(thirdAreaChartData).to.eql(thirdAreaExpectedChartData);
+ expect(firstAxesLabels).to.eql(['12.19GB', '12.2GB', '12.21GB']);
+ expect(secondAxesLabels).to.eql(['5.59KB', '5.6KB']);
+ expect(thirdAxesLabels.toString()).to.be(
+ 'BYTES_5721,BYTES_5722,BYTES_5723,BYTES_5724,BYTES_5725,BYTES_5726,BYTES_5727,BYTES_5728,BYTES_5729,BYTES_5730,BYTES_5731,BYTES_5732,BYTES_5733'
+ );
+ });
+
+ it('should display correct chart data for split expression', async () => {
+ await initVisualization('.es(index=logstash-*, split=geo.dest:3)', '1 day');
+
+ const firstAreaChartData = await visChart.getAreaChartData('q:* > geo.dest:CN > count');
+ const secondAreaChartData = await visChart.getAreaChartData('q:* > geo.dest:IN > count');
+ const thirdAreaChartData = await visChart.getAreaChartData('q:* > geo.dest:US > count');
+
+ expect(firstAreaChartData).to.eql([0, 905, 910, 850, 0]);
+ expect(secondAreaChartData).to.eql([0, 763, 699, 825, 0]);
+ expect(thirdAreaChartData).to.eql([0, 423, 386, 389, 0]);
+ });
+
+ it('should display two areas and one bar chart items', async () => {
+ await initVisualization('.es(*), .es(*), .es(*).bars(stack=true)');
+
+ const areasChartsCount = await visChart.getAreaSeriesCount();
+ const barsChartsCount = await visChart.getHistogramSeriesCount();
+
+ expect(areasChartsCount).to.be(2);
+ expect(barsChartsCount).to.be(1);
+ });
+
+ describe('Legend', () => {
+ it('should correctly display the legend items names and position', async () => {
+ await initVisualization('.es(*).label("first series"), .es(*).label("second series")');
+
+ const legendNames = await visChart.getLegendEntries();
+ const legendElement = await find.byClassName('echLegend');
+ const isLegendTopPositioned = await legendElement.elementHasClass('echLegend--top');
+ const isLegendLeftPositioned = await legendElement.elementHasClass('echLegend--left');
+
+ expect(legendNames).to.eql(['first series', 'second series']);
+ expect(isLegendTopPositioned).to.be(true);
+ expect(isLegendLeftPositioned).to.be(true);
+ });
+
+ it('should correctly display the legend position', async () => {
+ await initVisualization('.es(*).legend(position=se)');
+
+ const legendElement = await find.byClassName('echLegend');
+ const isLegendBottomPositioned = await legendElement.elementHasClass('echLegend--bottom');
+ const isLegendRightPositioned = await legendElement.elementHasClass('echLegend--right');
+
+ expect(isLegendBottomPositioned).to.be(true);
+ expect(isLegendRightPositioned).to.be(true);
+ });
+
+ it('should not display the legend', async () => {
+ await initVisualization('.es(*), .es(*).label("second series").legend(position=false)');
+
+ const isLegendElementExists = await find.existsByCssSelector('.echLegend');
+ expect(isLegendElementExists).to.be(false);
+ });
+ });
+
+ after(
+ async () =>
+ await kibanaServer.uiSettings.update({
+ 'timelion:legacyChartsLibrary': true,
+ })
+ );
+ });
+}
diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts
index bc6160eba3846..4af871bd9347d 100644
--- a/test/functional/apps/visualize/index.ts
+++ b/test/functional/apps/visualize/index.ts
@@ -52,6 +52,7 @@ export default function ({ getService, loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./_vertical_bar_chart'));
loadTestFile(require.resolve('./_vertical_bar_chart_nontimeindex'));
loadTestFile(require.resolve('./_pie_chart'));
+ loadTestFile(require.resolve('./_timelion'));
});
describe('visualize ciGroup9', function () {
diff --git a/test/functional/page_objects/visualize_chart_page.ts b/test/functional/page_objects/visualize_chart_page.ts
index 64b8c363fa6c2..bcee77a21c0b0 100644
--- a/test/functional/page_objects/visualize_chart_page.ts
+++ b/test/functional/page_objects/visualize_chart_page.ts
@@ -115,10 +115,10 @@ export class VisualizeChartPageObject extends FtrService {
.map((tick) => $(tick).text().trim());
}
- public async getYAxisLabels() {
+ public async getYAxisLabels(nth = 0) {
if (await this.isNewLibraryChart(xyChartSelector)) {
- const [yAxis] = (await this.getEsChartDebugState(xyChartSelector))?.axes?.y ?? [];
- return yAxis?.labels;
+ const yAxis = (await this.getEsChartDebugState(xyChartSelector))?.axes?.y ?? [];
+ return yAxis[nth]?.labels;
}
const yAxis = await this.find.byCssSelector('.visAxis__column--y.visAxis__column--left');
@@ -141,14 +141,19 @@ export class VisualizeChartPageObject extends FtrService {
* Gets the chart data and scales it based on chart height and label.
* @param dataLabel data-label value
* @param axis axis value, 'ValueAxis-1' by default
+ * @param shouldContainXAxisData boolean value for mapping points, false by default
*
* Returns an array of height values
*/
- public async getAreaChartData(dataLabel: string, axis = 'ValueAxis-1') {
+ public async getAreaChartData(
+ dataLabel: string,
+ axis = 'ValueAxis-1',
+ shouldContainXAxisData = false
+ ) {
if (await this.isNewLibraryChart(xyChartSelector)) {
const areas = (await this.getEsChartDebugState(xyChartSelector))?.areas ?? [];
const points = areas.find(({ name }) => name === dataLabel)?.lines.y1.points ?? [];
- return points.map(({ y }) => y);
+ return shouldContainXAxisData ? points.map(({ x, y }) => [x, y]) : points.map(({ y }) => y);
}
const yAxisRatio = await this.getChartYAxisRatio(axis);
@@ -556,12 +561,12 @@ export class VisualizeChartPageObject extends FtrService {
return values.filter((item) => item.length > 0);
}
- public async getRightValueAxesCount() {
+ public async getAxesCountByPosition(axesPosition: typeof Position[keyof typeof Position]) {
if (await this.isNewLibraryChart(xyChartSelector)) {
const yAxes = (await this.getEsChartDebugState(xyChartSelector))?.axes?.y ?? [];
- return yAxes.filter(({ position }) => position === Position.Right).length;
+ return yAxes.filter(({ position }) => position === axesPosition).length;
}
- const axes = await this.find.allByCssSelector('.visAxis__column--right g.axis');
+ const axes = await this.find.allByCssSelector(`.visAxis__column--${axesPosition} g.axis`);
return axes.length;
}
@@ -576,6 +581,16 @@ export class VisualizeChartPageObject extends FtrService {
await gauge.clickMouseButton({ xOffset: 0, yOffset });
}
+ public async getAreaSeriesCount() {
+ if (await this.isNewLibraryChart(xyChartSelector)) {
+ const areas = (await this.getEsChartDebugState(xyChartSelector))?.areas ?? [];
+ return areas.filter((area) => area.lines.y1.visible).length;
+ }
+
+ const series = await this.find.allByCssSelector('.points.area');
+ return series.length;
+ }
+
public async getHistogramSeriesCount() {
if (await this.isNewLibraryChart(xyChartSelector)) {
const bars = (await this.getEsChartDebugState(xyChartSelector))?.bars ?? [];
diff --git a/test/functional/page_objects/visualize_editor_page.ts b/test/functional/page_objects/visualize_editor_page.ts
index ab458c2c0fdc1..90fc320da3cda 100644
--- a/test/functional/page_objects/visualize_editor_page.ts
+++ b/test/functional/page_objects/visualize_editor_page.ts
@@ -532,4 +532,10 @@ export class VisualizeEditorPageObject extends FtrService {
public async setSeriesType(seriesNth: number, type: string) {
await this.find.selectValue(`select#seriesType${seriesNth}`, type);
}
+
+ public async setTimelionInterval(interval: string) {
+ const timelionIntervalComboBoxSelector = 'timelionIntervalComboBox';
+ await this.comboBox.clearInputField(timelionIntervalComboBoxSelector);
+ await this.comboBox.setCustom(timelionIntervalComboBoxSelector, interval);
+ }
}
diff --git a/test/functional/page_objects/visualize_page.ts b/test/functional/page_objects/visualize_page.ts
index 7e87312a70910..617cd42c06546 100644
--- a/test/functional/page_objects/visualize_page.ts
+++ b/test/functional/page_objects/visualize_page.ts
@@ -197,6 +197,10 @@ export class VisualizePageObject extends FtrService {
return await this.hasVisType('tile_map');
}
+ public async clickTimelion() {
+ await this.clickVisType('timelion');
+ }
+
public async clickTagCloud() {
await this.clickVisType('tagcloud');
}
diff --git a/test/functional/services/monaco_editor.ts b/test/functional/services/monaco_editor.ts
index d2afa1e3b6e92..90674e101fc4e 100644
--- a/test/functional/services/monaco_editor.ts
+++ b/test/functional/services/monaco_editor.ts
@@ -27,13 +27,13 @@ export class MonacoEditorService extends FtrService {
return values[nthIndex] as string;
}
- public async setCodeEditorValue(nthIndex: number, value: string) {
+ public async setCodeEditorValue(value: string, nthIndex = 0) {
await this.retry.try(async () => {
await this.browser.execute(
(editorIndex, codeEditorValue) => {
const editor = (window as any).MonacoEnvironment.monaco.editor;
const instance = editor.getModels()[editorIndex];
- instance.setValue(JSON.parse(codeEditorValue));
+ instance.setValue(codeEditorValue);
},
nthIndex,
value
diff --git a/x-pack/test/functional/page_objects/security_page.ts b/x-pack/test/functional/page_objects/security_page.ts
index 074ecafddf381..37fe0eccea31a 100644
--- a/x-pack/test/functional/page_objects/security_page.ts
+++ b/x-pack/test/functional/page_objects/security_page.ts
@@ -485,10 +485,7 @@ export class SecurityPageObject extends FtrService {
if (roleObj.elasticsearch.indices[0].query) {
await this.testSubjects.click('restrictDocumentsQuery0');
- await this.monacoEditor.setCodeEditorValue(
- 0,
- JSON.stringify(roleObj.elasticsearch.indices[0].query)
- );
+ await this.monacoEditor.setCodeEditorValue(roleObj.elasticsearch.indices[0].query);
}
const globalPrivileges = (roleObj.kibana as any).global;