From 20db7b5736bee3ab644c1f3feab97f75510a1c37 Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Wed, 4 Sep 2019 14:38:30 -0400 Subject: [PATCH 01/16] Add a smoke test for creating a Lens visualization --- .../lens/public/drag_drop/drag_drop.tsx | 4 +- .../editor_frame/editor_frame.test.tsx | 5 +- .../editor_frame/workspace_panel.tsx | 7 ++- .../public/indexpattern_plugin/field_item.tsx | 2 +- .../lens/public/register_vis_type_alias.ts | 4 +- .../xy_config_panel.tsx | 1 + .../apps/lens/indexpattern_datapanel.ts | 62 +++++++++++++------ 7 files changed, 57 insertions(+), 28 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/drag_drop/drag_drop.tsx b/x-pack/legacy/plugins/lens/public/drag_drop/drag_drop.tsx index e1f765ae6e673..bf3f207a1d7d5 100644 --- a/x-pack/legacy/plugins/lens/public/drag_drop/drag_drop.tsx +++ b/x-pack/legacy/plugins/lens/public/drag_drop/drag_drop.tsx @@ -56,7 +56,7 @@ interface Props { /** * The optional test subject associated with this DOM element. */ - dataTestSubj?: string; + 'data-test-subj'?: string; } /** @@ -128,7 +128,7 @@ export function DragDrop(props: Props) { return (
{ instance.update(); act(() => { - instance.find('[data-test-subj="lnsDragDrop"]').simulate('drop'); + instance + .find('[data-test-subj="lnsWorkspace"]') + .last() + .simulate('drop'); }); expect(mockVisualization.renderConfigPanel).toHaveBeenCalledWith( diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx index cc7b7148adae9..4db1997ccfd39 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/workspace_panel.tsx @@ -163,7 +163,12 @@ export function InnerWorkspacePanel({ } return ( - + {renderVisualization()} ); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx index 51d519da21693..05daa51f86adc 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx @@ -42,7 +42,7 @@ export function FieldItem({ field, indexPatternId, highlight }: FieldItemProps) return ( ) { { - beforeEach(async () => { - await PageObjects.common.navigateToApp('lens'); - }); + it('should allow creation of lens visualizations', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + + // Drag the @timestamp field to the workspace + const workspace = await find.byCssSelector('[data-test-subj="lnsWorkspace"]'); + const timestampField = await find.byCssSelector( + `[data-test-subj="lnsFieldListPanelField"] [title="@timestamp"]` + ); + + await browser.dragAndDrop( + { location: timestampField, offset: { x: 0, y: 0 } }, + { location: workspace, offset: { x: 40, y: 40 } } + ); + + // Change the y from count to min of bytes + await find.clickByButtonText('Count of documents'); + await find.clickByCssSelector('[data-test-subj="lns-indexPatternDimensionIncompatible-min"]'); + await find.clickByCssSelector('[data-test-subj="indexPattern-dimension-field"]'); + await find.clickByCssSelector('[data-test-subj="lns-fieldOption-bytes"]'); + + // Change the title to Afancilenstest + await find.setValue('[data-test-subj="lns_ChartTitle"]', 'Afancilenstest'); + + // Save the chart + await find.clickByCssSelector('[data-test-subj="lnsApp_saveButton"]'); + + // Ensure the visualization shows up in the visualize list + await PageObjects.visualize.gotoVisualizationLandingPage(); + await find.clickByCssSelector('[data-test-subj="visListingTitleLink-Afancilenstest"]'); + + // Expect to see the visualization editor for the saved visualization + const title = await find + .byCssSelector('[data-test-subj="lns_ChartTitle"]') + .then(el => el.getAttribute('value')); + expect(title).to.eql('Afancilenstest'); - it('should list the index pattern fields', async () => { - const fields = await find.allByCssSelector('[data-test-subj="lnsFieldListPanelField"]'); - const fieldText = await Promise.all(fields.map(field => field.getVisibleText())); - expect(fieldText).to.eql([ - '_score', - '@timestamp', - 'bytes', - 'id', - 'machine.ram', - 'memory', - 'meta.user.lastname', - 'phpmemory', - 'relatedContent.article:modified_time', - 'relatedContent.article:published_time', - 'utc_time', - ]); + const yDimension = await find + .byCssSelector('[data-test-subj="lnsXY_YDimensionPanel"]') + .then(el => el.getVisibleText()); + expect(yDimension).to.contain('Minimum of bytes'); }); }); } From 9259980507198322058faf44a8297fde80c5c875 Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Wed, 4 Sep 2019 16:51:48 -0400 Subject: [PATCH 02/16] Add remaining Lens smokescreen tests --- .../services/dashboard/add_panel.js | 6 +- .../metric_expression.tsx | 8 +- x-pack/test/functional/apps/lens/index.ts | 4 +- .../apps/lens/indexpattern_datapanel.ts | 60 - .../test/functional/apps/lens/smokescreen.ts | 124 ++ .../es_archives/lens/basic/data.json.gz | Bin 0 -> 2957 bytes .../es_archives/lens/basic/mappings.json | 1155 +++++++++++++++++ 7 files changed, 1292 insertions(+), 65 deletions(-) delete mode 100644 x-pack/test/functional/apps/lens/indexpattern_datapanel.ts create mode 100644 x-pack/test/functional/apps/lens/smokescreen.ts create mode 100644 x-pack/test/functional/es_archives/lens/basic/data.json.gz create mode 100644 x-pack/test/functional/es_archives/lens/basic/mappings.json diff --git a/test/functional/services/dashboard/add_panel.js b/test/functional/services/dashboard/add_panel.js index 89ec7b0a4c3c2..db9925d15c877 100644 --- a/test/functional/services/dashboard/add_panel.js +++ b/test/functional/services/dashboard/add_panel.js @@ -161,7 +161,11 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { log.debug('DashboardAddPanel.addVisualizations'); const vizList = []; for (const vizName of visualizations) { - await this.addVisualization(vizName); + if (typeof vizName === 'string') { + await this.addVisualization(vizName); + } else { + await this.addEmbeddable(vizName.name, vizName.embeddableType); + } vizList.push(vizName); } return vizList; diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.tsx index daff873feb18c..c3ebf096cf3cd 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.tsx +++ b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.tsx @@ -110,8 +110,12 @@ export function MetricChart({ }} > -
{value}
-
{title}
+
+ {value} +
+
+ {title} +
); diff --git a/x-pack/test/functional/apps/lens/index.ts b/x-pack/test/functional/apps/lens/index.ts index 085950b9f5f6b..60c74e9f68e11 100644 --- a/x-pack/test/functional/apps/lens/index.ts +++ b/x-pack/test/functional/apps/lens/index.ts @@ -17,13 +17,13 @@ export default function({ getService, loadTestFile }: FtrProviderContext) { log.debug('Starting lens before method'); browser.setWindowSize(1280, 800); await esArchiver.loadIfNeeded('logstash_functional'); - await esArchiver.loadIfNeeded('visualize/default'); + await esArchiver.loadIfNeeded('lens/basic'); }); describe('', function() { this.tags(['ciGroup4', 'skipFirefox']); - loadTestFile(require.resolve('./indexpattern_datapanel')); + loadTestFile(require.resolve('./smokescreen')); }); }); } diff --git a/x-pack/test/functional/apps/lens/indexpattern_datapanel.ts b/x-pack/test/functional/apps/lens/indexpattern_datapanel.ts deleted file mode 100644 index 09a3cb2b2ba9e..0000000000000 --- a/x-pack/test/functional/apps/lens/indexpattern_datapanel.ts +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import expect from '@kbn/expect'; -import { FtrProviderContext } from '../../ftr_provider_context'; - -// eslint-disable-next-line import/no-default-export -export default function({ getService, getPageObjects }: FtrProviderContext) { - const PageObjects = getPageObjects(['header', 'common', 'visualize', 'header', 'timePicker']); - const find = getService('find'); - const browser = getService('browser'); - - describe('indexpattern_datapanel', () => { - it('should allow creation of lens visualizations', async () => { - await PageObjects.visualize.navigateToNewVisualization(); - await PageObjects.visualize.clickVisType('lens'); - - // Drag the @timestamp field to the workspace - const workspace = await find.byCssSelector('[data-test-subj="lnsWorkspace"]'); - const timestampField = await find.byCssSelector( - `[data-test-subj="lnsFieldListPanelField"] [title="@timestamp"]` - ); - - await browser.dragAndDrop( - { location: timestampField, offset: { x: 0, y: 0 } }, - { location: workspace, offset: { x: 40, y: 40 } } - ); - - // Change the y from count to min of bytes - await find.clickByButtonText('Count of documents'); - await find.clickByCssSelector('[data-test-subj="lns-indexPatternDimensionIncompatible-min"]'); - await find.clickByCssSelector('[data-test-subj="indexPattern-dimension-field"]'); - await find.clickByCssSelector('[data-test-subj="lns-fieldOption-bytes"]'); - - // Change the title to Afancilenstest - await find.setValue('[data-test-subj="lns_ChartTitle"]', 'Afancilenstest'); - - // Save the chart - await find.clickByCssSelector('[data-test-subj="lnsApp_saveButton"]'); - - // Ensure the visualization shows up in the visualize list - await PageObjects.visualize.gotoVisualizationLandingPage(); - await find.clickByCssSelector('[data-test-subj="visListingTitleLink-Afancilenstest"]'); - - // Expect to see the visualization editor for the saved visualization - const title = await find - .byCssSelector('[data-test-subj="lns_ChartTitle"]') - .then(el => el.getAttribute('value')); - expect(title).to.eql('Afancilenstest'); - - const yDimension = await find - .byCssSelector('[data-test-subj="lnsXY_YDimensionPanel"]') - .then(el => el.getVisibleText()); - expect(yDimension).to.contain('Minimum of bytes'); - }); - }); -} diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts new file mode 100644 index 0000000000000..8a08cbb88d691 --- /dev/null +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -0,0 +1,124 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import expect from '@kbn/expect'; +import { FtrProviderContext } from '../../ftr_provider_context'; + +// eslint-disable-next-line import/no-default-export +export default function({ getService, getPageObjects }: FtrProviderContext) { + const PageObjects = getPageObjects([ + 'header', + 'common', + 'visualize', + 'dashboard', + 'header', + 'timePicker', + ]); + const find = getService('find'); + const retry = getService('retry'); + const browser = getService('browser'); + + async function goToValidTimeRange() { + const fromTime = '2015-09-19 06:31:44.000'; + const toTime = '2015-09-23 18:31:44.000'; + await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + } + + async function assertExpectedText(selector: string, test: (value?: string) => boolean) { + let actualText: string | undefined; + + await retry.waitForWithTimeout('assertExpectedText', 1000, async () => { + actualText = await find.byCssSelector(selector).then(el => el.getVisibleText()); + return test(actualText); + }); + + if (!test(actualText)) { + throw new Error(`"${actualText}" did not match expectation.`); + } + } + + async function assertExactText(selector: string, expectedText: string) { + await assertExpectedText(selector, value => value === expectedText); + } + + async function assertExpectedMetric() { + const expectedTitle = 'Maximum of bytes'; + const expectedValue = '19,986'; + await assertExactText('[data-test-subj="lns_metric_title"]', expectedTitle); + await assertExactText('[data-test-subj="lns_metric_value"]', expectedValue); + } + + function clickVisualizeListItem(title: string) { + return find.clickByCssSelector(`[data-test-subj="visListingTitleLink-${title}"]`); + } + + describe('lens smokescreen tests', () => { + it('should allow editing saved visualizations', async () => { + await PageObjects.visualize.gotoVisualizationLandingPage(); + await clickVisualizeListItem('Artistpreviouslyknownaslens'); + await goToValidTimeRange(); + await assertExpectedMetric(); + }); + + it('should be embeddable in dashboards', async () => { + await PageObjects.common.navigateToApp('dashboard'); + await PageObjects.dashboard.clickNewDashboard(); + await goToValidTimeRange(); + await PageObjects.dashboard.addVisualizations([ + { name: 'Artistpreviouslyknownaslens', embeddableType: 'lens' }, + ]); + await assertExpectedMetric(); + }); + + it('should allow creation of lens visualizations', async () => { + await PageObjects.visualize.navigateToNewVisualization(); + await PageObjects.visualize.clickVisType('lens'); + await goToValidTimeRange(); + + // Drag the @timestamp field to the workspace + const workspace = await find.byCssSelector('[data-test-subj="lnsWorkspace"]'); + const timestampField = await find.byCssSelector( + `[data-test-subj="lnsFieldListPanelField"] [title="@timestamp"]` + ); + + await browser.dragAndDrop( + { location: timestampField, offset: { x: 0, y: 0 } }, + { location: workspace, offset: { x: 40, y: 40 } } + ); + + // Change the y from count to average of bytes + await find.clickByButtonText('Count of documents'); + await find.clickByCssSelector('[data-test-subj="lns-indexPatternDimensionIncompatible-avg"]'); + await find.clickByCssSelector('[data-test-subj="indexPattern-dimension-field"]'); + await find.clickByCssSelector('[data-test-subj="lns-fieldOption-bytes"]'); + + // Change the title + await find.setValue('[data-test-subj="lns_ChartTitle"]', 'Afancilenstest'); + + // Save the chart + await find.clickByCssSelector('[data-test-subj="lnsApp_saveButton"]'); + + // Ensure the visualization shows up in the visualize list + await PageObjects.visualize.gotoVisualizationLandingPage(); + await clickVisualizeListItem('Afancilenstest'); + await goToValidTimeRange(); + + // Expect to see the visualization editor for the saved visualization + const title = await find + .byCssSelector('[data-test-subj="lns_ChartTitle"]') + .then(el => el.getAttribute('value')); + + expect(title).to.eql('Afancilenstest'); + + // .echLegendItem__title is the only viable way of getting the xy chart's + // legend item(s), so we're using a class selector here. + await assertExpectedText( + '.echLegendItem__title', + legendText => !!legendText && legendText.includes('Average of bytes') + ); + }); + }); +} diff --git a/x-pack/test/functional/es_archives/lens/basic/data.json.gz b/x-pack/test/functional/es_archives/lens/basic/data.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..992bf7c85e9fd7046b0fe3a3cf63b0297070baea GIT binary patch literal 2957 zcmc)4_dgVl!vOFzLuBuHw(c7C&N}<-Y-g{ckdVD+sE%-Eb~t-vobgFHBhGf5nb9Fz z)^+*5p1_CGSS3+~cmn0+ z$c8Ky;;rDLmA1E+A`&AXd`ALig1-XDr-yd7@tcat$;sJ$_(RCd>o9xum4$P0`ag`1 zI!X0M3=gsF7p&T-92S9uC~`Mb(Wh;jmbT=6Zw{`QCuP|Zse|D_q_8%KR(!M- z!@zt|XIVY>vr9PSccxKq12#Q>m1Bru5S)-8dt;vK=v!BC_#oAgmin9Z`||3}gmOYH z6`Y|w&Px3JOjeMKQ0nt}j%+qzAR2#QoDw4tJH<`qovN&0r)M)rTj46)=mR^*s5E;+ zaZ_R0Rd4sot*LB$WM^yZ@C}GMo`d2O%g-Msr>vzh$c$*!A!lA&*vb%fH?t+}S;s8t#G$5?ucW>#ba6d-gVOGzDzq9WyK=*qDzZCcd4 zQ^%SFQs1Ahl1LH=i$6OL;)~iz6EIrwoK5yl|L@Nuh-L0Y>`zPOJaHSw)TiGzhAa8M zA$2tDUNdUPe61d6g-p$Su0q=wN=j_UYJKH7f?gt_WGVgJ#uis@6DZ_Y8+NmvV`>3Q z1>t6nw~2qlTFCiHbgR~xm(BH}(zI^LAG^fmg-sgsAp38}ZPw`$IWf4cfZ_+j2p(^W zvw*x`3YwhQKoP4h@T;eo0WtL#DZIBVyc)cplFB7Q{5SOk{ zR&a3ELbd&PRM6f{Q4*7XMN2lzenec0VJc~6j8N7`bA|>pN^}>*pc^e*UiDbdgIYrZ z8W;hh=b{;c3gisA@-QS(Gvt04>QStPtE*%(iniU)rFS5`X;Li;Vcpk!7}A(|Tp4h& zgmK#D-1cGsSg?prnw(L6r_wQuqLDz8YyqarXqh1M7?S(3z>ji^enK3h0g;N+d#ZS`-Y zJ*{Ct^EmQI=Dm{-Bz_Fn6n^*i9g++SGsN8X^}_P!;cXOsXyQ_6vvuMXb7$O??J+y; zu0%Av^mN*Ul;~G7@g%(t7%Y5l{|x6U?LB`ekUYM}S>=Jf=-R$!#+BH(;oLn}I@adq zR@RlLmY7RJP(`cDzRH8YG;L^|pK(T~fAx!Wz`IV13D|#h)g3Rf1Zm|pzvH%IEKfM~ z;{E%Gz5K>@aLwg!DYfWYd8BAl>@u2=)wc@@6YH>PC738 z7($D@L|(L0l0f%B@Svy_nkMT58;N|TZsUy%N<>ZR^-B551yT5&Z#$-lB+*Epxc0Hl z7ah9V+77bFV0ftuSMI)Ltz+|16UgcEG6??8)M>ySXNv2NJMOxieWJ0}iIgDZZR&Lr zG=FHAfztj3;<((57myXSkAN>Y8!T^%3#?x&Y$ldjq*1x}{C50ed>x6Nsm-e+xGMX} z^=Afv7D-RA+BWsT=X7R1DRx@3T&I^vpdjVd^|sUZG!@Tw1|mIWL#tuEB1nkqOgRH) z;0C0CUqBjMp&=sp1`6bqn!ajX&o&Rocr(WX@wDfAA$GnxFpb?P6w5Z4g)9>Y%5rbB zUqrR$%~=QIUNp_wI~$RKUO~xz#1Axq@tmtF>2%6eQ1+oi^8DDo1tQZc?cl`ohE0gA-n1M)v4CMnu+}uUqI~h-ajKihd!Sm9_k;53B z$Oplc%?s{tQHq?59E zSZg-IWqhI@p%xT@<2CU-D=uw^7nXm-sx~C8?$zk`bkOt^ghH&OBcsX zhNWM39ygq<8^w10skJPFHx2)*o@>#JYCTMPo`1Z;em7_Mft})B+@tru)g=3`k{NH0 ze9nw!PofJF8v<(n-i)#QWOYb$GcFTZXZsn#=u)P&KpMz!hq7tMVlb6NCT3SjhX=Zo z$=y>b-JZZ>RHxM68J{4gmTyXu<$pwqgnh)k&pr2mqkidghXUi;t&YW}HBQ5$R80qIK&MS1d}GrOFf-~IS{MMh1|>-*EhXb$vAyyuE6k}31`4> zeHGkZ#YjFI4)x5;%Q&NxW!cw$;d-W{)?hq;Nh!lF1@e5V!rW+c&(8`B>Ha!v3MvX! z!$~|27xl=O%+Rj>6)qxB@8+r+qRRpsGP0w)G>6ZmB1IYkdYZc z(ObR$-TMme4w!1r<=%r8;8B@Zb5#xU^0n#OwW?@;E1VPwF2^6ybLMFi<~Xn?UK2Yg zIPUMm6gO(9T|#z{Ku8TY7F=ZG2;=s<@xV`!cgy9G2(?x7o#iRW#qcImCTRE;{P#oV z*SfxJdW)_Phlja0HZhY^)48()Eu%usGkCXmn2Lu=Y=;F4Eab0qDW>MFS$o+nU*X7> zSbD+Mth$-@z{_E*_Y%x+E{kq=katZ|=EK6<<3!)agF(#pSoqG3`po!X$4Z*gJgWY` zOWf6#9*-rSKW$g7<#G?KLDZ#S?+r&5weKEJAbaI!?mSY9aK|lWZP{!cpdqKct;U%&x8H_DEHv0^dwqjCOUE2(W@E8fq|}bZbV2)^Ps6Su9p*& zxFN-giT-j3=w(rrml07TayEIJLu2}fZwwIs!m=r2XDlbS+L)v3UOFvz(+XVr518y* zpr;3}y{Kjv3Tw2UG_oHyYd-bw?(lI5xVMrU0X*ewrfW@MiTuy}(cYj>v%zue{-CaM zB6iJ{BXY^w`DjD`7NLe?V;psdm7;`44m@(4ykb&RH@(+aEdpzVg{lR6qR&@*0uf}& bewYwwv=ToCM%XC2yd^niP>i9=1OWaAFXgN? literal 0 HcmV?d00001 diff --git a/x-pack/test/functional/es_archives/lens/basic/mappings.json b/x-pack/test/functional/es_archives/lens/basic/mappings.json new file mode 100644 index 0000000000000..10a94d305dd5d --- /dev/null +++ b/x-pack/test/functional/es_archives/lens/basic/mappings.json @@ -0,0 +1,1155 @@ +{ + "type": "index", + "value": { + "aliases": { + ".kibana": { + } + }, + "index": ".kibana_1", + "mappings": { + "_meta": { + "migrationMappingPropertyHashes": { + "action": "ecc01e367a369542bc2b15dae1fb1773", + "action_task_params": "a9d49f184ee89641044be0ca2950fa3a", + "alert": "b2d549df61fd5bf8098427ec68a4712d", + "apm-telemetry": "07ee1939fa4302c62ddc052ec03fed90", + "canvas-element": "7390014e1091044523666d97247392fc", + "canvas-workpad": "b0a1706d356228dbdcb4a17e6b9eb231", + "config": "87aca8fdb053154f11383fce3dbf3edf", + "dashboard": "d00f614b29a80360e1190193fd333bab", + "file-upload-telemetry": "0ed4d3e1983d1217a30982630897092e", + "graph-workspace": "cd7ba1330e6682e9cc00b78850874be1", + "index-pattern": "66eccb05066c5a89924f48a9e9736499", + "infrastructure-ui-source": "ddc0ecb18383f6b26101a2fadb2dab0c", + "kql-telemetry": "d12a98a6f19a2d273696597547e064ee", + "lens": "d69713426be87ba23283776aab149b9a", + "map": "23d7aa4a720d4938ccde3983f87bd58d", + "maps-telemetry": "a4229f8b16a6820c6d724b7e0c1f729d", + "migrationVersion": "4a1746014a75ade3a714e1db5763276f", + "ml-telemetry": "257fd1d4b4fdbb9cb4b8a3b27da201e9", + "namespace": "2f4316de49999235636386fe51dc06c1", + "query": "11aaeb7f5f7fa5bb43f25e18ce26e7d9", + "references": "7997cf5a56cc02bdc9c93361bde732b0", + "sample-data-telemetry": "7d3cfeb915303c9641c59681967ffeb4", + "search": "181661168bbadd1eff5902361e2a0d5c", + "server": "ec97f1c5da1a19609a60874e5af1100c", + "siem-ui-timeline": "1f6f0860ad7bc0dba3e42467ca40470d", + "siem-ui-timeline-note": "8874706eedc49059d4cf0f5094559084", + "siem-ui-timeline-pinned-event": "20638091112f0e14f0e443d512301c29", + "space": "25de8c2deec044392922989cfcf24c54", + "telemetry": "e1c8bc94e443aefd9458932cc0697a4d", + "timelion-sheet": "9a2a2748877c7a7b582fef201ab1d4cf", + "type": "2f4316de49999235636386fe51dc06c1", + "ui-metric": "0d409297dc5ebe1e3a1da691c6ee32e3", + "updated_at": "00da57df13e94e9d98437d13ace4bfe0", + "upgrade-assistant-reindex-operation": "a53a20fe086b72c9a86da3cc12dad8a6", + "upgrade-assistant-telemetry": "56702cec857e0a9dacfb696655b4ff7b", + "url": "c7f66a0df8b1b52f17c28c4adb111105", + "visualization": "52d7a13ad68a150c4525b292d23e12cc" + } + }, + "dynamic": "strict", + "properties": { + "action": { + "properties": { + "actionTypeId": { + "type": "keyword" + }, + "config": { + "enabled": false, + "type": "object" + }, + "description": { + "type": "text" + }, + "secrets": { + "type": "binary" + } + } + }, + "action_task_params": { + "properties": { + "actionId": { + "type": "keyword" + }, + "apiKey": { + "type": "binary" + }, + "params": { + "enabled": false, + "type": "object" + } + } + }, + "alert": { + "properties": { + "actions": { + "properties": { + "actionRef": { + "type": "keyword" + }, + "group": { + "type": "keyword" + }, + "params": { + "enabled": false, + "type": "object" + } + }, + "type": "nested" + }, + "alertTypeId": { + "type": "keyword" + }, + "alertTypeParams": { + "enabled": false, + "type": "object" + }, + "apiKey": { + "type": "binary" + }, + "apiKeyOwner": { + "type": "keyword" + }, + "createdBy": { + "type": "keyword" + }, + "enabled": { + "type": "boolean" + }, + "interval": { + "type": "keyword" + }, + "scheduledTaskId": { + "type": "keyword" + }, + "updatedBy": { + "type": "keyword" + } + } + }, + "apm-telemetry": { + "properties": { + "has_any_services": { + "type": "boolean" + }, + "services_per_agent": { + "properties": { + "dotnet": { + "null_value": 0, + "type": "long" + }, + "go": { + "null_value": 0, + "type": "long" + }, + "java": { + "null_value": 0, + "type": "long" + }, + "js-base": { + "null_value": 0, + "type": "long" + }, + "nodejs": { + "null_value": 0, + "type": "long" + }, + "python": { + "null_value": 0, + "type": "long" + }, + "ruby": { + "null_value": 0, + "type": "long" + }, + "rum-js": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "canvas-element": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "content": { + "type": "text" + }, + "help": { + "type": "text" + }, + "image": { + "type": "text" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "canvas-workpad": { + "dynamic": "false", + "properties": { + "@created": { + "type": "date" + }, + "@timestamp": { + "type": "date" + }, + "name": { + "fields": { + "keyword": { + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "config": { + "dynamic": "true", + "properties": { + "accessibility:disableAnimations": { + "type": "boolean" + }, + "buildNum": { + "type": "keyword" + }, + "dateFormat:tz": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "defaultIndex": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "dashboard": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "optionsJSON": { + "type": "text" + }, + "panelsJSON": { + "type": "text" + }, + "refreshInterval": { + "properties": { + "display": { + "type": "keyword" + }, + "pause": { + "type": "boolean" + }, + "section": { + "type": "integer" + }, + "value": { + "type": "integer" + } + } + }, + "timeFrom": { + "type": "keyword" + }, + "timeRestore": { + "type": "boolean" + }, + "timeTo": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "file-upload-telemetry": { + "properties": { + "filesUploadedTotalCount": { + "type": "long" + } + } + }, + "gis-map": { + "properties": { + "bounds": { + "strategy": "recursive", + "type": "geo_shape" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "graph-workspace": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "numLinks": { + "type": "integer" + }, + "numVertices": { + "type": "integer" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "wsState": { + "type": "text" + } + } + }, + "index-pattern": { + "properties": { + "fieldFormatMap": { + "type": "text" + }, + "fields": { + "type": "text" + }, + "intervalName": { + "type": "keyword" + }, + "notExpandable": { + "type": "boolean" + }, + "sourceFilters": { + "type": "text" + }, + "timeFieldName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "type": { + "type": "keyword" + }, + "typeMeta": { + "type": "keyword" + } + } + }, + "infrastructure-ui-source": { + "properties": { + "description": { + "type": "text" + }, + "fields": { + "properties": { + "container": { + "type": "keyword" + }, + "host": { + "type": "keyword" + }, + "pod": { + "type": "keyword" + }, + "tiebreaker": { + "type": "keyword" + }, + "timestamp": { + "type": "keyword" + } + } + }, + "logAlias": { + "type": "keyword" + }, + "logColumns": { + "properties": { + "fieldColumn": { + "properties": { + "field": { + "type": "keyword" + }, + "id": { + "type": "keyword" + } + } + }, + "messageColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + }, + "timestampColumn": { + "properties": { + "id": { + "type": "keyword" + } + } + } + }, + "type": "nested" + }, + "metricAlias": { + "type": "keyword" + }, + "name": { + "type": "text" + } + } + }, + "kql-telemetry": { + "properties": { + "optInCount": { + "type": "long" + }, + "optOutCount": { + "type": "long" + } + } + }, + "lens": { + "properties": { + "expression": { + "index": false, + "type": "keyword" + }, + "state": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + }, + "visualizationType": { + "type": "keyword" + } + } + }, + "map": { + "properties": { + "bounds": { + "type": "geo_shape" + }, + "description": { + "type": "text" + }, + "layerListJSON": { + "type": "text" + }, + "mapStateJSON": { + "type": "text" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "maps-telemetry": { + "properties": { + "attributesPerMap": { + "properties": { + "dataSourcesCount": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + }, + "emsVectorLayersCount": { + "dynamic": "true", + "type": "object" + }, + "layerTypesCount": { + "dynamic": "true", + "type": "object" + }, + "layersCount": { + "properties": { + "avg": { + "type": "long" + }, + "max": { + "type": "long" + }, + "min": { + "type": "long" + } + } + } + } + }, + "mapsTotalCount": { + "type": "long" + }, + "timeCaptured": { + "type": "date" + } + } + }, + "migrationVersion": { + "dynamic": "true", + "properties": { + "index-pattern": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "space": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + }, + "visualization": { + "fields": { + "keyword": { + "ignore_above": 256, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "ml-telemetry": { + "properties": { + "file_data_visualizer": { + "properties": { + "index_creation_count": { + "type": "long" + } + } + } + } + }, + "namespace": { + "type": "keyword" + }, + "query": { + "properties": { + "description": { + "type": "text" + }, + "filters": { + "enabled": false, + "type": "object" + }, + "query": { + "properties": { + "language": { + "type": "keyword" + }, + "query": { + "index": false, + "type": "keyword" + } + } + }, + "timefilter": { + "enabled": false, + "type": "object" + }, + "title": { + "type": "text" + } + } + }, + "references": { + "properties": { + "id": { + "type": "keyword" + }, + "name": { + "type": "keyword" + }, + "type": { + "type": "keyword" + } + }, + "type": "nested" + }, + "sample-data-telemetry": { + "properties": { + "installCount": { + "type": "long" + }, + "unInstallCount": { + "type": "long" + } + } + }, + "search": { + "properties": { + "columns": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "sort": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "server": { + "properties": { + "uuid": { + "type": "keyword" + } + } + }, + "siem-ui-timeline": { + "properties": { + "columns": { + "properties": { + "aggregatable": { + "type": "boolean" + }, + "category": { + "type": "keyword" + }, + "columnHeaderType": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "example": { + "type": "text" + }, + "id": { + "type": "keyword" + }, + "indexes": { + "type": "keyword" + }, + "name": { + "type": "text" + }, + "placeholder": { + "type": "text" + }, + "searchable": { + "type": "boolean" + }, + "type": { + "type": "keyword" + } + } + }, + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "dataProviders": { + "properties": { + "and": { + "properties": { + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + } + } + }, + "enabled": { + "type": "boolean" + }, + "excluded": { + "type": "boolean" + }, + "id": { + "type": "keyword" + }, + "kqlQuery": { + "type": "text" + }, + "name": { + "type": "text" + }, + "queryMatch": { + "properties": { + "displayField": { + "type": "text" + }, + "displayValue": { + "type": "text" + }, + "field": { + "type": "text" + }, + "operator": { + "type": "text" + }, + "value": { + "type": "text" + } + } + } + } + }, + "dateRange": { + "properties": { + "end": { + "type": "date" + }, + "start": { + "type": "date" + } + } + }, + "description": { + "type": "text" + }, + "favorite": { + "properties": { + "favoriteDate": { + "type": "date" + }, + "fullName": { + "type": "text" + }, + "keySearch": { + "type": "text" + }, + "userName": { + "type": "text" + } + } + }, + "kqlMode": { + "type": "keyword" + }, + "kqlQuery": { + "properties": { + "filterQuery": { + "properties": { + "kuery": { + "properties": { + "expression": { + "type": "text" + }, + "kind": { + "type": "keyword" + } + } + }, + "serializedQuery": { + "type": "text" + } + } + } + } + }, + "sort": { + "properties": { + "columnId": { + "type": "keyword" + }, + "sortDirection": { + "type": "keyword" + } + } + }, + "title": { + "type": "text" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-note": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "note": { + "type": "text" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "siem-ui-timeline-pinned-event": { + "properties": { + "created": { + "type": "date" + }, + "createdBy": { + "type": "text" + }, + "eventId": { + "type": "keyword" + }, + "timelineId": { + "type": "keyword" + }, + "updated": { + "type": "date" + }, + "updatedBy": { + "type": "text" + } + } + }, + "space": { + "properties": { + "_reserved": { + "type": "boolean" + }, + "color": { + "type": "keyword" + }, + "description": { + "type": "text" + }, + "disabledFeatures": { + "type": "keyword" + }, + "initials": { + "type": "keyword" + }, + "name": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "spaceId": { + "type": "keyword" + }, + "telemetry": { + "properties": { + "enabled": { + "type": "boolean" + } + } + }, + "timelion-sheet": { + "properties": { + "description": { + "type": "text" + }, + "hits": { + "type": "integer" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "timelion_chart_height": { + "type": "integer" + }, + "timelion_columns": { + "type": "integer" + }, + "timelion_interval": { + "type": "keyword" + }, + "timelion_other_interval": { + "type": "keyword" + }, + "timelion_rows": { + "type": "integer" + }, + "timelion_sheet": { + "type": "text" + }, + "title": { + "type": "text" + }, + "version": { + "type": "integer" + } + } + }, + "type": { + "type": "keyword" + }, + "ui-metric": { + "properties": { + "count": { + "type": "integer" + } + } + }, + "updated_at": { + "type": "date" + }, + "upgrade-assistant-reindex-operation": { + "dynamic": "true", + "properties": { + "indexName": { + "type": "keyword" + }, + "status": { + "type": "integer" + } + } + }, + "upgrade-assistant-telemetry": { + "properties": { + "features": { + "properties": { + "deprecation_logging": { + "properties": { + "enabled": { + "null_value": true, + "type": "boolean" + } + } + } + } + }, + "ui_open": { + "properties": { + "cluster": { + "null_value": 0, + "type": "long" + }, + "indices": { + "null_value": 0, + "type": "long" + }, + "overview": { + "null_value": 0, + "type": "long" + } + } + }, + "ui_reindex": { + "properties": { + "close": { + "null_value": 0, + "type": "long" + }, + "open": { + "null_value": 0, + "type": "long" + }, + "start": { + "null_value": 0, + "type": "long" + }, + "stop": { + "null_value": 0, + "type": "long" + } + } + } + } + }, + "url": { + "properties": { + "accessCount": { + "type": "long" + }, + "accessDate": { + "type": "date" + }, + "createDate": { + "type": "date" + }, + "url": { + "fields": { + "keyword": { + "ignore_above": 2048, + "type": "keyword" + } + }, + "type": "text" + } + } + }, + "visualization": { + "properties": { + "description": { + "type": "text" + }, + "kibanaSavedObjectMeta": { + "properties": { + "searchSourceJSON": { + "type": "text" + } + } + }, + "savedSearchRefName": { + "type": "keyword" + }, + "title": { + "type": "text" + }, + "uiStateJSON": { + "type": "text" + }, + "version": { + "type": "integer" + }, + "visState": { + "type": "text" + } + } + } + } + }, + "settings": { + "index": { + "auto_expand_replicas": "0-1", + "number_of_replicas": "0", + "number_of_shards": "1" + } + } + } +} \ No newline at end of file From 7b6147b59af23a0fc073f0e46a59be9bc42c774f Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Thu, 5 Sep 2019 10:13:36 -0400 Subject: [PATCH 03/16] Fix Jest tests --- .../metric_visualization_plugin/metric_expression.test.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx index 69d91d4c97fe1..dd0f7523f54cf 100644 --- a/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx +++ b/x-pack/legacy/plugins/lens/public/metric_visualization_plugin/metric_expression.test.tsx @@ -65,6 +65,7 @@ describe('metric_expression', () => { >
{ 10110
Date: Thu, 5 Sep 2019 14:07:30 -0400 Subject: [PATCH 04/16] Add smokescreen test for switching to / from data table visualization. Fix a number of small issues related to that scenario. --- .../expression.tsx | 1 + .../editor_frame/chart_switch.tsx | 14 +++- .../indexpattern_suggestions.ts | 84 +++++++++++-------- .../test/functional/apps/lens/smokescreen.ts | 34 +++++++- 4 files changed, 91 insertions(+), 42 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/expression.tsx b/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/expression.tsx index 0e53ee59761f5..80762997a2d37 100644 --- a/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/expression.tsx +++ b/x-pack/legacy/plugins/lens/public/datatable_visualization_plugin/expression.tsx @@ -133,6 +133,7 @@ function DatatableComponent(props: DatatableProps & { formatFactory: FormatFacto return ( { return { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx index 770f402bdf5f8..34f9f67f8b928 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/editor_frame/chart_switch.tsx @@ -236,12 +236,13 @@ export function ChartSwitch(props: Props) {
); } + function getTopSuggestion( props: Props, visualizationId: string, newVisualization: Visualization ): Suggestion | undefined { - return getSuggestions({ + const suggestions = getSuggestions({ datasourceMap: props.datasourceMap, datasourceStates: props.datasourceStates, visualizationMap: { [visualizationId]: newVisualization }, @@ -251,5 +252,14 @@ function getTopSuggestion( // don't use extended versions of current data table on switching between visualizations // to avoid confusing the user. return suggestion.changeType !== 'extended'; - })[0]; + }); + + // We prefer unchanged or reduced suggestions when switching + // charts since that allows you to switch from A to B and back + // to A with the greatest chance of preserving your original state. + return ( + suggestions.find(s => s.changeType === 'unchanged') || + suggestions.find(s => s.changeType === 'reduced') || + suggestions[0] + ); } diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern_suggestions.ts b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern_suggestions.ts index 5ed236ee73933..9073782ec746d 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern_suggestions.ts +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/indexpattern_suggestions.ts @@ -15,7 +15,12 @@ import { IndexPatternPrivateState, IndexPattern, } from './indexpattern'; -import { buildColumn, getOperationTypesForField, operationDefinitionMap } from './operations'; +import { + buildColumn, + getOperationTypesForField, + operationDefinitionMap, + IndexPatternColumn, +} from './operations'; import { hasField } from './utils'; function buildSuggestion({ @@ -33,21 +38,36 @@ function buildSuggestion({ datasourceSuggestionId?: number; label?: string; }): DatasourceSuggestion { - const columnOrder = (updatedLayer || state.layers[layerId]).columnOrder; - const columnMap = (updatedLayer || state.layers[layerId]).columns; + const updatedState = updatedLayer + ? { + ...state, + layers: { + ...state.layers, + [layerId]: updatedLayer, + }, + } + : state; + // It's fairly easy to accidentally introduce a mismatch between + // columnOrder and columns, so this is a safeguard to ensure the + // two match up. + const layers = _.mapValues(updatedState.layers, layer => ({ + ...layer, + columns: _.pick, Record>( + layer.columns, + layer.columnOrder + ), + })); + + const columnOrder = layers[layerId].columnOrder; + const columnMap = layers[layerId].columns; const isMultiRow = Object.values(columnMap).some(column => column.isBucketed); return { - state: updatedLayer - ? { - ...state, - layers: { - ...state.layers, - [layerId]: updatedLayer, - }, - } - : state, + state: { + ...updatedState, + layers, + }, table: { columns: columnOrder.map(columnId => ({ @@ -420,9 +440,11 @@ function createAlternativeMetricSuggestions( field, suggestedPriority: undefined, }); - const updatedLayer = buildLayerByColumnOrder({ ...layer, columns: { [newId]: newColumn } }, [ - newId, - ]); + const updatedLayer = { + ...layer, + columns: { [newId]: newColumn }, + columnOrder: [newId], + }; suggestions.push( buildSuggestion({ state, @@ -451,10 +473,11 @@ function createSuggestionWithDefaultDateHistogram( field: indexPattern.fields.find(({ name }) => name === indexPattern.timeFieldName), suggestedPriority: undefined, }); - const updatedLayer = buildLayerByColumnOrder( - { ...layer, columns: { ...layer.columns, [newId]: timeColumn } }, - [...buckets, newId, ...metrics] - ); + const updatedLayer = { + ...layer, + columns: { ...layer.columns, [newId]: timeColumn }, + columnOrder: [...buckets, newId, ...metrics], + }; return buildSuggestion({ state, layerId, @@ -475,15 +498,15 @@ function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layer availableBucketedColumns.map((_col, index) => { // build suggestions with fewer buckets const bucketedColumns = availableBucketedColumns.slice(0, index + 1); - const allMetricsSuggestion = buildLayerByColumnOrder(layer, [ - ...bucketedColumns, - ...availableMetricColumns, - ]); + const allMetricsSuggestion = { + ...layer, + columnOrder: [...bucketedColumns, ...availableMetricColumns], + }; if (availableMetricColumns.length > 1) { return [ allMetricsSuggestion, - buildLayerByColumnOrder(layer, [...bucketedColumns, availableMetricColumns[0]]), + { ...layer, columnOrder: [...bucketedColumns, availableMetricColumns[0]] }, ]; } else { return allMetricsSuggestion; @@ -493,7 +516,7 @@ function createSimplifiedTableSuggestions(state: IndexPatternPrivateState, layer .concat( availableMetricColumns.map(columnId => { // build suggestions with only metrics - return buildLayerByColumnOrder(layer, [columnId]); + return { ...layer, columnOrder: [columnId] }; }) ) .map(updatedLayer => { @@ -526,14 +549,3 @@ function getMetricSuggestionTitle(layer: IndexPatternLayer, onlyMetric: boolean) function separateBucketColumns(layer: IndexPatternLayer) { return partition(layer.columnOrder, columnId => layer.columns[columnId].isBucketed); } - -function buildLayerByColumnOrder( - layer: IndexPatternLayer, - columnOrder: string[] -): IndexPatternLayer { - return { - ...layer, - columns: _.pick(layer.columns, columnOrder), - columnOrder, - }; -} diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 8a08cbb88d691..06aa058e730e7 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -20,6 +20,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const retry = getService('retry'); const browser = getService('browser'); + const esArchiver = getService('esArchiver'); async function goToValidTimeRange() { const fromTime = '2015-09-19 06:31:44.000'; @@ -45,10 +46,24 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { } async function assertExpectedMetric() { - const expectedTitle = 'Maximum of bytes'; - const expectedValue = '19,986'; - await assertExactText('[data-test-subj="lns_metric_title"]', expectedTitle); - await assertExactText('[data-test-subj="lns_metric_value"]', expectedValue); + await assertExactText('[data-test-subj="lns_metric_title"]', 'Maximum of bytes'); + await assertExactText('[data-test-subj="lns_metric_value"]', '19,986'); + } + + async function assertExpectedTable() { + await assertExactText( + '[data-test-subj="lnsDataTable"] thead .euiTableCellContent__text', + 'Maximum of bytes' + ); + await assertExactText( + '[data-test-subj="lnsDataTable"] tbody .euiTableCellContent__text', + '19,986' + ); + } + + async function switchToVisualization(dataTestSubj: string) { + await find.clickByCssSelector('[data-test-subj="lnsChartSwitchPopover"]'); + await find.clickByCssSelector(`[data-test-subj="${dataTestSubj}"]`); } function clickVisualizeListItem(title: string) { @@ -73,6 +88,17 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { await assertExpectedMetric(); }); + it('should allow seamless transition to and from table view', async () => { + await PageObjects.visualize.gotoVisualizationLandingPage(); + await clickVisualizeListItem('Artistpreviouslyknownaslens'); + await goToValidTimeRange(); + await assertExpectedMetric(); + await switchToVisualization('lnsChartSwitchPopover_lnsDatatable'); + await assertExpectedTable(); + await switchToVisualization('lnsChartSwitchPopover_lnsMetric'); + await assertExpectedMetric(); + }); + it('should allow creation of lens visualizations', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); From 3ae7ae325e41dc4dec9a4eaeda399a66e8bd5090 Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Thu, 5 Sep 2019 14:48:57 -0400 Subject: [PATCH 05/16] Remove unused dependency --- x-pack/test/functional/apps/lens/smokescreen.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 06aa058e730e7..20d49f9e72872 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -20,7 +20,6 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { const find = getService('find'); const retry = getService('retry'); const browser = getService('browser'); - const esArchiver = getService('esArchiver'); async function goToValidTimeRange() { const fromTime = '2015-09-19 06:31:44.000'; From e009355e972ea5a6ad12ee31f470e48950a0502a Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Fri, 6 Sep 2019 12:47:36 -0400 Subject: [PATCH 06/16] Move expressionRenderer up into setup instead of start, fix Lens registration --- .../public/expressions/expressions_service.ts | 4 +--- .../public/editor_frame_plugin/plugin.tsx | 24 +++++++++++-------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/legacy/core_plugins/data/public/expressions/expressions_service.ts b/src/legacy/core_plugins/data/public/expressions/expressions_service.ts index 2aff9cab67e82..380055048685c 100644 --- a/src/legacy/core_plugins/data/public/expressions/expressions_service.ts +++ b/src/legacy/core_plugins/data/public/expressions/expressions_service.ts @@ -52,19 +52,17 @@ export class ExpressionsService { registerType: npSetup.plugins.data.expressions.registerType, registerFunction: npSetup.plugins.data.expressions.registerFunction, registerRenderer: npSetup.plugins.data.expressions.registerRenderer, + ExpressionRenderer: createRenderer(loader), }; } public start({ inspector }: ExpressionsServiceStartDependencies) { - const ExpressionRenderer = createRenderer(loader); setInspector(inspector); return { execute, render, loader, - - ExpressionRenderer, }; } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx index 2b83f50924e8a..a7afb5c87dc09 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx @@ -10,7 +10,10 @@ import { I18nProvider } from '@kbn/i18n/react'; import { CoreSetup, CoreStart } from 'src/core/public'; import chrome, { Chrome } from 'ui/chrome'; import { Plugin as EmbeddablePlugin } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { start as embeddablePlugin } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { + start as embeddablePlugin, + setup as embeddableFactorySetup, +} from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { setup as dataSetup, start as dataStart, @@ -29,6 +32,7 @@ import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; export interface EditorFrameSetupPlugins { data: typeof dataSetup; + embeddables: ReturnType; } export interface EditorFrameStartPlugins { @@ -46,6 +50,15 @@ export class EditorFramePlugin { public setup(_core: CoreSetup | null, plugins: EditorFrameSetupPlugins): EditorFrameSetup { plugins.data.expressions.registerFunction(() => mergeTables); + embeddableFactorySetup.registerEmbeddableFactory( + 'lens', + new EmbeddableFactory( + chrome, + plugins.data.expressions.ExpressionRenderer, + plugins.data.indexPatterns.indexPatterns + ) + ); + return { registerDatasource: (name, datasource) => { this.datasources[name] = datasource as Datasource; @@ -57,15 +70,6 @@ export class EditorFramePlugin { } public start(_core: CoreStart | null, plugins: EditorFrameStartPlugins): EditorFrameStart { - plugins.embeddables.registerEmbeddableFactory( - 'lens', - new EmbeddableFactory( - plugins.chrome, - plugins.data.expressions.ExpressionRenderer, - plugins.data.indexPatterns.indexPatterns - ) - ); - const createInstance = (): EditorFrameInstance => { let domElement: Element; return { From fb6dd0e1064dd82bd3bdc4e6f26531df9383774a Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Fri, 6 Sep 2019 15:19:28 -0400 Subject: [PATCH 07/16] Fix Lens integration tests --- .../lens/public/editor_frame_plugin/mocks.tsx | 8 +- .../public/editor_frame_plugin/plugin.tsx | 19 ++-- .../public/indexpattern_plugin/field_item.tsx | 2 +- .../metric_expression.test.tsx | 88 ++++++++----------- .../test/functional/apps/lens/smokescreen.ts | 7 ++ 5 files changed, 60 insertions(+), 64 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx index 582aa42051aca..2e65d31affd16 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx @@ -105,8 +105,13 @@ export function createMockSetupDependencies() { expressions: { registerFunction: jest.fn(), registerRenderer: jest.fn(), + ExpressionRenderer: jest.fn(() => null), + }, + indexPatterns: { + indexPatterns: {}, }, }, + registerEmbeddableFactory: jest.fn(), chrome: { getSavedObjectsClient: () => {}, }, @@ -123,8 +128,5 @@ export function createMockStartDependencies() { indexPatterns: {}, }, }, - embeddables: { - registerEmbeddableFactory: jest.fn(), - }, } as unknown) as MockedStartDependencies; } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx index a7afb5c87dc09..bd40886f779c4 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx @@ -9,11 +9,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { CoreSetup, CoreStart } from 'src/core/public'; import chrome, { Chrome } from 'ui/chrome'; -import { Plugin as EmbeddablePlugin } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; -import { - start as embeddablePlugin, - setup as embeddableFactorySetup, -} from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { setup as embeddableSetup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { setup as dataSetup, start as dataStart, @@ -32,12 +28,12 @@ import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; export interface EditorFrameSetupPlugins { data: typeof dataSetup; - embeddables: ReturnType; + registerEmbeddableFactory: (id: string, factory: EmbeddableFactory) => void; + chrome: Chrome; } export interface EditorFrameStartPlugins { data: typeof dataStart; - embeddables: ReturnType; chrome: Chrome; } @@ -50,10 +46,10 @@ export class EditorFramePlugin { public setup(_core: CoreSetup | null, plugins: EditorFrameSetupPlugins): EditorFrameSetup { plugins.data.expressions.registerFunction(() => mergeTables); - embeddableFactorySetup.registerEmbeddableFactory( + plugins.registerEmbeddableFactory( 'lens', new EmbeddableFactory( - chrome, + plugins.chrome, plugins.data.expressions.ExpressionRenderer, plugins.data.indexPatterns.indexPatterns ) @@ -121,14 +117,15 @@ const editorFrame = new EditorFramePlugin(); export const editorFrameSetup = () => editorFrame.setup(null, { - data: dataSetup, + data: dataStart, + chrome, + registerEmbeddableFactory: embeddableSetup.registerEmbeddableFactory, }); export const editorFrameStart = () => editorFrame.start(null, { data: dataStart, chrome, - embeddables: embeddablePlugin, }); export const editorFrameStop = () => editorFrame.stop(); diff --git a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx index 403b76ed1c49c..0afc769688218 100644 --- a/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx +++ b/x-pack/legacy/plugins/lens/public/indexpattern_plugin/field_item.tsx @@ -44,7 +44,7 @@ export function FieldItem({ field, indexPattern, highlight, exists }: FieldItemP return ( { expect(shallow( x} />)) .toMatchInlineSnapshot(` -
- -
- 10110 -
-
- My fanci metric chart -
-
-
- `); - }); - - test('it does not render title in reduced mode', () => { - const { data, args } = sampleArgs(); - - expect( - shallow( - x} /> - ) - ).toMatchInlineSnapshot(`
{ } } > - +
{
`); }); + + test('it does not render title in reduced mode', () => { + const { data, args } = sampleArgs(); + + expect( + shallow( + x} /> + ) + ).toMatchInlineSnapshot(` +
+ +
+ 10110 +
+
+
+ `); + }); }); }); diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 20d49f9e72872..299179ed61a11 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -69,6 +69,12 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { return find.clickByCssSelector(`[data-test-subj="visListingTitleLink-${title}"]`); } + async function showHiddenFields() { + await find.clickByCssSelector('[data-test-subj="lnsIndexPatternFiltersToggle"]'); + await find.clickByCssSelector('[data-test-subj="lnsEmptyFilter"]'); + await find.clickByCssSelector('[data-test-subj="lnsIndexPatternFiltersToggle"]'); + } + describe('lens smokescreen tests', () => { it('should allow editing saved visualizations', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); @@ -101,6 +107,7 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { it('should allow creation of lens visualizations', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); + await showHiddenFields(); await goToValidTimeRange(); // Drag the @timestamp field to the workspace From a803e692cdde86d321b8e20741a1154f48e83fbf Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 9 Sep 2019 15:28:37 +0200 Subject: [PATCH 08/16] kickoff shimmed start phase in OSS kibana --- .../lens/public/editor_frame_plugin/mocks.tsx | 4 +++- .../public/editor_frame_plugin/plugin.tsx | 24 +++++++++---------- .../lens/public/register_embeddable.ts | 3 ++- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx index 2e65d31affd16..a2de1a646f26a 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx @@ -111,7 +111,6 @@ export function createMockSetupDependencies() { indexPatterns: {}, }, }, - registerEmbeddableFactory: jest.fn(), chrome: { getSavedObjectsClient: () => {}, }, @@ -128,5 +127,8 @@ export function createMockStartDependencies() { indexPatterns: {}, }, }, + embeddables: { + registerEmbeddableFactory: jest.fn(), + }, } as unknown) as MockedStartDependencies; } diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx index bd40886f779c4..51a87ed44a6e2 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx @@ -9,7 +9,7 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { CoreSetup, CoreStart } from 'src/core/public'; import chrome, { Chrome } from 'ui/chrome'; -import { setup as embeddableSetup } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { start as embeddableStart } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { setup as dataSetup, start as dataStart, @@ -28,12 +28,12 @@ import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; export interface EditorFrameSetupPlugins { data: typeof dataSetup; - registerEmbeddableFactory: (id: string, factory: EmbeddableFactory) => void; chrome: Chrome; } export interface EditorFrameStartPlugins { data: typeof dataStart; + embeddables: typeof embeddableStart; chrome: Chrome; } @@ -46,15 +46,6 @@ export class EditorFramePlugin { public setup(_core: CoreSetup | null, plugins: EditorFrameSetupPlugins): EditorFrameSetup { plugins.data.expressions.registerFunction(() => mergeTables); - plugins.registerEmbeddableFactory( - 'lens', - new EmbeddableFactory( - plugins.chrome, - plugins.data.expressions.ExpressionRenderer, - plugins.data.indexPatterns.indexPatterns - ) - ); - return { registerDatasource: (name, datasource) => { this.datasources[name] = datasource as Datasource; @@ -66,6 +57,15 @@ export class EditorFramePlugin { } public start(_core: CoreStart | null, plugins: EditorFrameStartPlugins): EditorFrameStart { + plugins.embeddables.registerEmbeddableFactory( + 'lens', + new EmbeddableFactory( + plugins.chrome, + plugins.data.expressions.ExpressionRenderer, + plugins.data.indexPatterns.indexPatterns + ) + ); + const createInstance = (): EditorFrameInstance => { let domElement: Element; return { @@ -119,13 +119,13 @@ export const editorFrameSetup = () => editorFrame.setup(null, { data: dataStart, chrome, - registerEmbeddableFactory: embeddableSetup.registerEmbeddableFactory, }); export const editorFrameStart = () => editorFrame.start(null, { data: dataStart, chrome, + embeddables: embeddableStart, }); export const editorFrameStop = () => editorFrame.stop(); diff --git a/x-pack/legacy/plugins/lens/public/register_embeddable.ts b/x-pack/legacy/plugins/lens/public/register_embeddable.ts index e488f8e3d9aa3..f86cb91a0029e 100644 --- a/x-pack/legacy/plugins/lens/public/register_embeddable.ts +++ b/x-pack/legacy/plugins/lens/public/register_embeddable.ts @@ -6,7 +6,7 @@ import { indexPatternDatasourceSetup } from './indexpattern_plugin'; import { xyVisualizationSetup } from './xy_visualization_plugin'; -import { editorFrameSetup } from './editor_frame_plugin'; +import { editorFrameSetup, editorFrameStart } from './editor_frame_plugin'; import { datatableVisualizationSetup } from './datatable_visualization_plugin'; import { metricVisualizationSetup } from './metric_visualization_plugin'; @@ -17,3 +17,4 @@ datatableVisualizationSetup(); xyVisualizationSetup(); metricVisualizationSetup(); editorFrameSetup(); +editorFrameStart(); From b050460445fcb50fcde1b6f4d83ab99c385cbb29 Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Mon, 9 Sep 2019 11:44:23 -0400 Subject: [PATCH 09/16] Move helpers into Lens page object --- .../test/functional/apps/lens/smokescreen.ts | 108 ++++----------- x-pack/test/functional/page_objects/lens.ts | 131 +++++++++++++++++- 2 files changed, 159 insertions(+), 80 deletions(-) diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 299179ed61a11..ffe08cb63df15 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; // eslint-disable-next-line import/no-default-export -export default function({ getService, getPageObjects }: FtrProviderContext) { +export default function({ getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ 'header', 'common', @@ -16,77 +16,40 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { 'dashboard', 'header', 'timePicker', + 'lens', ]); - const find = getService('find'); - const retry = getService('retry'); - const browser = getService('browser'); - - async function goToValidTimeRange() { - const fromTime = '2015-09-19 06:31:44.000'; - const toTime = '2015-09-23 18:31:44.000'; - await PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); - } - - async function assertExpectedText(selector: string, test: (value?: string) => boolean) { - let actualText: string | undefined; - - await retry.waitForWithTimeout('assertExpectedText', 1000, async () => { - actualText = await find.byCssSelector(selector).then(el => el.getVisibleText()); - return test(actualText); - }); - - if (!test(actualText)) { - throw new Error(`"${actualText}" did not match expectation.`); - } - } - - async function assertExactText(selector: string, expectedText: string) { - await assertExpectedText(selector, value => value === expectedText); - } async function assertExpectedMetric() { - await assertExactText('[data-test-subj="lns_metric_title"]', 'Maximum of bytes'); - await assertExactText('[data-test-subj="lns_metric_value"]', '19,986'); + await PageObjects.lens.assertExactText( + '[data-test-subj="lns_metric_title"]', + 'Maximum of bytes' + ); + await PageObjects.lens.assertExactText('[data-test-subj="lns_metric_value"]', '19,986'); } async function assertExpectedTable() { - await assertExactText( + await PageObjects.lens.assertExactText( '[data-test-subj="lnsDataTable"] thead .euiTableCellContent__text', 'Maximum of bytes' ); - await assertExactText( + await PageObjects.lens.assertExactText( '[data-test-subj="lnsDataTable"] tbody .euiTableCellContent__text', '19,986' ); } - async function switchToVisualization(dataTestSubj: string) { - await find.clickByCssSelector('[data-test-subj="lnsChartSwitchPopover"]'); - await find.clickByCssSelector(`[data-test-subj="${dataTestSubj}"]`); - } - - function clickVisualizeListItem(title: string) { - return find.clickByCssSelector(`[data-test-subj="visListingTitleLink-${title}"]`); - } - - async function showHiddenFields() { - await find.clickByCssSelector('[data-test-subj="lnsIndexPatternFiltersToggle"]'); - await find.clickByCssSelector('[data-test-subj="lnsEmptyFilter"]'); - await find.clickByCssSelector('[data-test-subj="lnsIndexPatternFiltersToggle"]'); - } - describe('lens smokescreen tests', () => { it('should allow editing saved visualizations', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); - await clickVisualizeListItem('Artistpreviouslyknownaslens'); - await goToValidTimeRange(); + await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens'); + await PageObjects.lens.goToTimeRange(); await assertExpectedMetric(); }); it('should be embeddable in dashboards', async () => { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); - await goToValidTimeRange(); + await PageObjects.lens.goToTimeRange(); await PageObjects.dashboard.addVisualizations([ { name: 'Artistpreviouslyknownaslens', embeddableType: 'lens' }, ]); @@ -95,59 +58,48 @@ export default function({ getService, getPageObjects }: FtrProviderContext) { it('should allow seamless transition to and from table view', async () => { await PageObjects.visualize.gotoVisualizationLandingPage(); - await clickVisualizeListItem('Artistpreviouslyknownaslens'); - await goToValidTimeRange(); + await PageObjects.lens.clickVisualizeListItemTitle('Artistpreviouslyknownaslens'); + await PageObjects.lens.goToTimeRange(); await assertExpectedMetric(); - await switchToVisualization('lnsChartSwitchPopover_lnsDatatable'); + await PageObjects.lens.switchToVisualization('lnsChartSwitchPopover_lnsDatatable'); await assertExpectedTable(); - await switchToVisualization('lnsChartSwitchPopover_lnsMetric'); + await PageObjects.lens.switchToVisualization('lnsChartSwitchPopover_lnsMetric'); await assertExpectedMetric(); }); it('should allow creation of lens visualizations', async () => { await PageObjects.visualize.navigateToNewVisualization(); await PageObjects.visualize.clickVisType('lens'); - await showHiddenFields(); - await goToValidTimeRange(); + await PageObjects.lens.toggleExistenceFilter(); + await PageObjects.lens.goToTimeRange(); // Drag the @timestamp field to the workspace - const workspace = await find.byCssSelector('[data-test-subj="lnsWorkspace"]'); - const timestampField = await find.byCssSelector( - `[data-test-subj="lnsFieldListPanelField"] [title="@timestamp"]` - ); - - await browser.dragAndDrop( - { location: timestampField, offset: { x: 0, y: 0 } }, - { location: workspace, offset: { x: 40, y: 40 } } - ); + await PageObjects.lens.dragToWorkspace('@timestamp'); // Change the y from count to average of bytes - await find.clickByButtonText('Count of documents'); - await find.clickByCssSelector('[data-test-subj="lns-indexPatternDimensionIncompatible-avg"]'); - await find.clickByCssSelector('[data-test-subj="indexPattern-dimension-field"]'); - await find.clickByCssSelector('[data-test-subj="lns-fieldOption-bytes"]'); + await PageObjects.lens.changeDimension({ + from: 'Count of documents', + to: 'avg', + field: 'bytes', + }); // Change the title - await find.setValue('[data-test-subj="lns_ChartTitle"]', 'Afancilenstest'); + await PageObjects.lens.setTitle('Afancilenstest'); // Save the chart - await find.clickByCssSelector('[data-test-subj="lnsApp_saveButton"]'); + await PageObjects.lens.save(); // Ensure the visualization shows up in the visualize list await PageObjects.visualize.gotoVisualizationLandingPage(); - await clickVisualizeListItem('Afancilenstest'); - await goToValidTimeRange(); + await PageObjects.lens.clickVisualizeListItemTitle('Afancilenstest'); + await PageObjects.lens.goToTimeRange(); // Expect to see the visualization editor for the saved visualization - const title = await find - .byCssSelector('[data-test-subj="lns_ChartTitle"]') - .then(el => el.getAttribute('value')); - - expect(title).to.eql('Afancilenstest'); + expect(await PageObjects.lens.getTitle()).to.eql('Afancilenstest'); // .echLegendItem__title is the only viable way of getting the xy chart's // legend item(s), so we're using a class selector here. - await assertExpectedText( + await PageObjects.lens.assertExpectedText( '.echLegendItem__title', legendText => !!legendText && legendText.includes('Average of bytes') ); diff --git a/x-pack/test/functional/page_objects/lens.ts b/x-pack/test/functional/page_objects/lens.ts index fa762dc86fd8d..669a8ebd7386a 100644 --- a/x-pack/test/functional/page_objects/lens.ts +++ b/x-pack/test/functional/page_objects/lens.ts @@ -6,20 +6,147 @@ import { FtrProviderContext } from '../ftr_provider_context'; -export function LensPageProvider({ getService }: FtrProviderContext) { +export function LensPageProvider({ getService, getPageObjects }: FtrProviderContext) { const testSubjects = getService('testSubjects'); + const retry = getService('retry'); + const find = getService('find'); + const browser = getService('browser'); + const PageObjects = getPageObjects([ + 'header', + 'common', + 'visualize', + 'dashboard', + 'header', + 'timePicker', + ]); return { - async openIndexPatternFiltersPopover() { + /** + * Clicks the index pattern filters toggle. + */ + async toggleIndexPatternFiltersPopover() { await testSubjects.click('lnsIndexPatternFiltersToggle'); }, + /** + * Toggles the field existence checkbox. + */ async toggleExistenceFilter() { + await this.toggleIndexPatternFiltersPopover(); await testSubjects.click('lnsEmptyFilter'); + await this.toggleIndexPatternFiltersPopover(); }, async findAllFields() { return await testSubjects.findAll('lnsFieldListPanelField'); }, + + /** + * Move the date filter to the specified time range, defaults to + * a range that has data in our dataset. + */ + goToTimeRange(fromTime = '2015-09-19 06:31:44.000', toTime = '2015-09-23 18:31:44.000') { + return PageObjects.timePicker.setAbsoluteRange(fromTime, toTime); + }, + + /** + * Wait for the specified element to have text that passes the specified test. + * + * @param selector - the element selector + * @param test - the test function to run on the element's text + */ + async assertExpectedText(selector: string, test: (value?: string) => boolean) { + let actualText: string | undefined; + + await retry.waitForWithTimeout('assertExpectedText', 1000, async () => { + actualText = await find.byCssSelector(selector).then(el => el.getVisibleText()); + return test(actualText); + }); + + if (!test(actualText)) { + throw new Error(`"${actualText}" did not match expectation.`); + } + }, + + /** + * Asserts that the specified element has the expected inner text. + * + * @param selector - the element selector + * @param expectedText - the expected text + */ + assertExactText(selector: string, expectedText: string) { + return this.assertExpectedText(selector, value => value === expectedText); + }, + + /** + * Uses the Lens visualization switcher to switch visualizations. + * + * @param dataTestSubj - the data-test-subj of the visualization to switch to + */ + async switchToVisualization(dataTestSubj: string) { + await testSubjects.click('lnsChartSwitchPopover'); + await testSubjects.click(dataTestSubj); + }, + + /** + * Clicks a visualize list item's title (in the visualize app). + * + * @param title - the title of the list item to be clicked + */ + clickVisualizeListItemTitle(title: string) { + return testSubjects.click(`visListingTitleLink-${title}`); + }, + + /** + * Drags and drops a field into the Lens workspace. + * + * @param fieldName - the name of the field to be dropped in the workspace + */ + async dragToWorkspace(fieldName: string) { + const workspace = await testSubjects.find('lnsWorkspace'); + const timestampField = await find.byCssSelector( + `[data-test-subj="lnsFieldListPanelField"] [title="${fieldName}"]` + ); + + await browser.dragAndDrop( + { location: timestampField, offset: { x: 0, y: 0 } }, + { location: workspace, offset: { x: 40, y: 40 } } + ); + }, + + /** + * Changes the specified dimension to the specified operation and (optinally) field. + * + * @param opts.from - the text of the dimension being changed + * @param opts.to - the desired operation for the dimension + * @param opts.field - the desired field for the dimension + */ + async changeDimension(opts: { from: string; to: string; field?: string }) { + await find.clickByButtonText(opts.from); + await find.clickByCssSelector( + `[data-test-subj="lns-indexPatternDimensionIncompatible-${opts.to}"], + [data-test-subj="lns-indexPatternDimension-${opts.to}"]` + ); + + if (opts.field) { + await testSubjects.click('indexPattern-dimension-field'); + await testSubjects.click(`lns-fieldOption-${opts.field}`); + } + }, + + /** + * Save the current Lens visualization. + */ + save() { + return testSubjects.click('lnsApp_saveButton'); + }, + + setTitle(title: string) { + return testSubjects.setValue('lns_ChartTitle', title); + }, + + getTitle() { + return testSubjects.getAttribute('lns_ChartTitle', 'value'); + }, }; } From a793ba31de4282d5cb7857bc459b60f1fe7a1071 Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Mon, 9 Sep 2019 11:45:40 -0400 Subject: [PATCH 10/16] Attempt to fix dashboard embeddable timing issue in integration tests --- x-pack/test/functional/apps/lens/smokescreen.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index ffe08cb63df15..06c5caf659629 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -49,10 +49,10 @@ export default function({ getPageObjects }: FtrProviderContext) { it('should be embeddable in dashboards', async () => { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.lens.goToTimeRange(); await PageObjects.dashboard.addVisualizations([ { name: 'Artistpreviouslyknownaslens', embeddableType: 'lens' }, ]); + await PageObjects.lens.goToTimeRange(); await assertExpectedMetric(); }); From 05b31fecb52a328a3c4abcb94457afa7dbbdf125 Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Mon, 9 Sep 2019 12:33:47 -0400 Subject: [PATCH 11/16] Revert unecessary add_panel changes Another attempt to fix dashboard Lens test --- test/functional/services/dashboard/add_panel.js | 6 +----- x-pack/test/functional/apps/lens/smokescreen.ts | 10 ++++++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/test/functional/services/dashboard/add_panel.js b/test/functional/services/dashboard/add_panel.js index db9925d15c877..89ec7b0a4c3c2 100644 --- a/test/functional/services/dashboard/add_panel.js +++ b/test/functional/services/dashboard/add_panel.js @@ -161,11 +161,7 @@ export function DashboardAddPanelProvider({ getService, getPageObjects }) { log.debug('DashboardAddPanel.addVisualizations'); const vizList = []; for (const vizName of visualizations) { - if (typeof vizName === 'string') { - await this.addVisualization(vizName); - } else { - await this.addEmbeddable(vizName.name, vizName.embeddableType); - } + await this.addVisualization(vizName); vizList.push(vizName); } return vizList; diff --git a/x-pack/test/functional/apps/lens/smokescreen.ts b/x-pack/test/functional/apps/lens/smokescreen.ts index 06c5caf659629..d8898f0c207f3 100644 --- a/x-pack/test/functional/apps/lens/smokescreen.ts +++ b/x-pack/test/functional/apps/lens/smokescreen.ts @@ -8,7 +8,7 @@ import expect from '@kbn/expect'; import { FtrProviderContext } from '../../ftr_provider_context'; // eslint-disable-next-line import/no-default-export -export default function({ getPageObjects }: FtrProviderContext) { +export default function({ getService, getPageObjects }: FtrProviderContext) { const PageObjects = getPageObjects([ 'header', 'common', @@ -18,6 +18,8 @@ export default function({ getPageObjects }: FtrProviderContext) { 'timePicker', 'lens', ]); + const find = getService('find'); + const dashboardAddPanel = getService('dashboardAddPanel'); async function assertExpectedMetric() { await PageObjects.lens.assertExactText( @@ -49,9 +51,9 @@ export default function({ getPageObjects }: FtrProviderContext) { it('should be embeddable in dashboards', async () => { await PageObjects.common.navigateToApp('dashboard'); await PageObjects.dashboard.clickNewDashboard(); - await PageObjects.dashboard.addVisualizations([ - { name: 'Artistpreviouslyknownaslens', embeddableType: 'lens' }, - ]); + await dashboardAddPanel.clickOpenAddPanel(); + await find.clickByButtonText('Artistpreviouslyknownaslens'); + await dashboardAddPanel.closeAddPanel(); await PageObjects.lens.goToTimeRange(); await assertExpectedMetric(); }); From 6bbb1a74338ba18d15b16e108b86e1e14919e33a Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Mon, 9 Sep 2019 14:38:02 -0400 Subject: [PATCH 12/16] Last attempt to fix Lens drag drop functional tests before reverting to alternatives --- test/functional/services/browser.ts | 22 ++++++++++++++++++--- x-pack/test/functional/page_objects/lens.ts | 13 ++++-------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index d52be4b90c043..89764b161167c 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/browser.ts @@ -31,7 +31,7 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { const log = getService('log'); const config = getService('config'); const lifecycle = getService('lifecycle'); - const { driver, Key, LegacyActionSequence, browserType } = await getService( + const { driver, Key, LegacyActionSequence, browserType, By } = await getService( '__webdriver__' ).init(); @@ -201,6 +201,22 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { } } + /** + * Does a drag-and-drop action from one point to another + * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#dragAndDrop + * + * @param opts - from and to selectors + * @return {Promise} + */ + public async dragAndDropSelector(opts: { from: string; to: string }) { + const draggedEl = await driver.findElement(By.css(opts.from)); + const dropTarget = await driver.findElement(By.css(opts.to)); + + return this.getActions() + .dragAndDrop(draggedEl, dropTarget) + .perform(); + } + /** * Does a drag-and-drop action from one point to another * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#dragAndDrop @@ -210,8 +226,8 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { * @return {Promise} */ public async dragAndDrop( - from: { offset: { x: any; y: any }; location: any }, - to: { offset: { x: any; y: any }; location: any } + from: { offset?: { x: any; y: any }; location: any }, + to: { offset?: { x: any; y: any }; location: any } ) { // tslint:disable-next-line:variable-name let _from; diff --git a/x-pack/test/functional/page_objects/lens.ts b/x-pack/test/functional/page_objects/lens.ts index 669a8ebd7386a..9ab650a09381a 100644 --- a/x-pack/test/functional/page_objects/lens.ts +++ b/x-pack/test/functional/page_objects/lens.ts @@ -103,15 +103,10 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * @param fieldName - the name of the field to be dropped in the workspace */ async dragToWorkspace(fieldName: string) { - const workspace = await testSubjects.find('lnsWorkspace'); - const timestampField = await find.byCssSelector( - `[data-test-subj="lnsFieldListPanelField"] [title="${fieldName}"]` - ); - - await browser.dragAndDrop( - { location: timestampField, offset: { x: 0, y: 0 } }, - { location: workspace, offset: { x: 40, y: 40 } } - ); + await browser.dragAndDropSelector({ + from: `[data-test-subj="lnsFieldListPanelField"] [title="${fieldName}"]`, + to: '[data-test-subj="lnsWorkspace"]', + }); }, /** From 93406d5adb13e4bf60759b9254265b0a746c0fe6 Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Mon, 9 Sep 2019 15:46:08 -0400 Subject: [PATCH 13/16] Revert "Last attempt to fix Lens drag drop functional" This reverts commit 6bbb1a74338ba18d15b16e108b86e1e14919e33a. --- test/functional/services/browser.ts | 22 +++------------------ x-pack/test/functional/page_objects/lens.ts | 13 ++++++++---- 2 files changed, 12 insertions(+), 23 deletions(-) diff --git a/test/functional/services/browser.ts b/test/functional/services/browser.ts index 89764b161167c..d52be4b90c043 100644 --- a/test/functional/services/browser.ts +++ b/test/functional/services/browser.ts @@ -31,7 +31,7 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { const log = getService('log'); const config = getService('config'); const lifecycle = getService('lifecycle'); - const { driver, Key, LegacyActionSequence, browserType, By } = await getService( + const { driver, Key, LegacyActionSequence, browserType } = await getService( '__webdriver__' ).init(); @@ -201,22 +201,6 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { } } - /** - * Does a drag-and-drop action from one point to another - * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#dragAndDrop - * - * @param opts - from and to selectors - * @return {Promise} - */ - public async dragAndDropSelector(opts: { from: string; to: string }) { - const draggedEl = await driver.findElement(By.css(opts.from)); - const dropTarget = await driver.findElement(By.css(opts.to)); - - return this.getActions() - .dragAndDrop(draggedEl, dropTarget) - .perform(); - } - /** * Does a drag-and-drop action from one point to another * https://seleniumhq.github.io/selenium/docs/api/javascript/module/selenium-webdriver/lib/input_exports_Actions.html#dragAndDrop @@ -226,8 +210,8 @@ export async function BrowserProvider({ getService }: FtrProviderContext) { * @return {Promise} */ public async dragAndDrop( - from: { offset?: { x: any; y: any }; location: any }, - to: { offset?: { x: any; y: any }; location: any } + from: { offset: { x: any; y: any }; location: any }, + to: { offset: { x: any; y: any }; location: any } ) { // tslint:disable-next-line:variable-name let _from; diff --git a/x-pack/test/functional/page_objects/lens.ts b/x-pack/test/functional/page_objects/lens.ts index 9ab650a09381a..669a8ebd7386a 100644 --- a/x-pack/test/functional/page_objects/lens.ts +++ b/x-pack/test/functional/page_objects/lens.ts @@ -103,10 +103,15 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont * @param fieldName - the name of the field to be dropped in the workspace */ async dragToWorkspace(fieldName: string) { - await browser.dragAndDropSelector({ - from: `[data-test-subj="lnsFieldListPanelField"] [title="${fieldName}"]`, - to: '[data-test-subj="lnsWorkspace"]', - }); + const workspace = await testSubjects.find('lnsWorkspace'); + const timestampField = await find.byCssSelector( + `[data-test-subj="lnsFieldListPanelField"] [title="${fieldName}"]` + ); + + await browser.dragAndDrop( + { location: timestampField, offset: { x: 0, y: 0 } }, + { location: workspace, offset: { x: 40, y: 40 } } + ); }, /** From f01444f0942916d7d8bb10ea321d0f87551e5fbe Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Mon, 9 Sep 2019 16:04:48 -0400 Subject: [PATCH 14/16] Get rid of drag drop test from Lens functional tests --- .../xy_config_panel.tsx | 2 +- .../test/functional/apps/lens/smokescreen.ts | 21 ++++++------ x-pack/test/functional/page_objects/lens.ts | 33 +++++-------------- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.tsx b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.tsx index 6675df850cc1a..4d0c7b7044163 100644 --- a/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.tsx +++ b/x-pack/legacy/plugins/lens/public/xy_visualization_plugin/xy_config_panel.tsx @@ -214,7 +214,7 @@ export function XYConfigPanel(props: VisualizationProps) { Date: Tue, 10 Sep 2019 10:34:53 -0400 Subject: [PATCH 15/16] Undo expressions_service change --- .../data/public/expressions/expressions_service.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/legacy/core_plugins/data/public/expressions/expressions_service.ts b/src/legacy/core_plugins/data/public/expressions/expressions_service.ts index 380055048685c..2aff9cab67e82 100644 --- a/src/legacy/core_plugins/data/public/expressions/expressions_service.ts +++ b/src/legacy/core_plugins/data/public/expressions/expressions_service.ts @@ -52,17 +52,19 @@ export class ExpressionsService { registerType: npSetup.plugins.data.expressions.registerType, registerFunction: npSetup.plugins.data.expressions.registerFunction, registerRenderer: npSetup.plugins.data.expressions.registerRenderer, - ExpressionRenderer: createRenderer(loader), }; } public start({ inspector }: ExpressionsServiceStartDependencies) { + const ExpressionRenderer = createRenderer(loader); setInspector(inspector); return { execute, render, loader, + + ExpressionRenderer, }; } From bb34f57523ad00ab8f9aed84d4dc7d98344ae8f2 Mon Sep 17 00:00:00 2001 From: Christopher Davies Date: Tue, 10 Sep 2019 11:26:14 -0400 Subject: [PATCH 16/16] Add debug logs to Lens functional tests, Remove unnecessary expression changes --- .../lens/public/editor_frame_plugin/mocks.tsx | 4 --- .../public/editor_frame_plugin/plugin.tsx | 11 +++--- x-pack/test/functional/page_objects/index.ts | 2 +- .../page_objects/{lens.ts => lens_page.ts} | 6 ++-- .../functional/page_objects/log_wrapper.ts | 34 +++++++++++++++++++ 5 files changed, 44 insertions(+), 13 deletions(-) rename x-pack/test/functional/page_objects/{lens.ts => lens_page.ts} (97%) create mode 100644 x-pack/test/functional/page_objects/log_wrapper.ts diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx index a2de1a646f26a..582aa42051aca 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/mocks.tsx @@ -105,10 +105,6 @@ export function createMockSetupDependencies() { expressions: { registerFunction: jest.fn(), registerRenderer: jest.fn(), - ExpressionRenderer: jest.fn(() => null), - }, - indexPatterns: { - indexPatterns: {}, }, }, chrome: { diff --git a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx index 51a87ed44a6e2..2b83f50924e8a 100644 --- a/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx +++ b/x-pack/legacy/plugins/lens/public/editor_frame_plugin/plugin.tsx @@ -9,7 +9,8 @@ import { render, unmountComponentAtNode } from 'react-dom'; import { I18nProvider } from '@kbn/i18n/react'; import { CoreSetup, CoreStart } from 'src/core/public'; import chrome, { Chrome } from 'ui/chrome'; -import { start as embeddableStart } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; +import { Plugin as EmbeddablePlugin } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public'; +import { start as embeddablePlugin } from '../../../../../../src/legacy/core_plugins/embeddable_api/public/np_ready/public/legacy'; import { setup as dataSetup, start as dataStart, @@ -28,12 +29,11 @@ import { getActiveDatasourceIdFromDoc } from './editor_frame/state_management'; export interface EditorFrameSetupPlugins { data: typeof dataSetup; - chrome: Chrome; } export interface EditorFrameStartPlugins { data: typeof dataStart; - embeddables: typeof embeddableStart; + embeddables: ReturnType; chrome: Chrome; } @@ -117,15 +117,14 @@ const editorFrame = new EditorFramePlugin(); export const editorFrameSetup = () => editorFrame.setup(null, { - data: dataStart, - chrome, + data: dataSetup, }); export const editorFrameStart = () => editorFrame.start(null, { data: dataStart, chrome, - embeddables: embeddableStart, + embeddables: embeddablePlugin, }); export const editorFrameStop = () => editorFrame.stop(); diff --git a/x-pack/test/functional/page_objects/index.ts b/x-pack/test/functional/page_objects/index.ts index e22e36fcd73aa..4163e77911934 100644 --- a/x-pack/test/functional/page_objects/index.ts +++ b/x-pack/test/functional/page_objects/index.ts @@ -44,7 +44,7 @@ import { SnapshotRestorePageProvider } from './snapshot_restore_page'; import { CrossClusterReplicationPageProvider } from './cross_cluster_replication_page'; import { RemoteClustersPageProvider } from './remote_clusters_page'; import { CopySavedObjectsToSpacePageProvider } from './copy_saved_objects_to_space_page'; -import { LensPageProvider } from './lens'; +import { LensPageProvider } from './lens_page'; // just like services, PageObjects are defined as a map of // names to Providers. Merge in Kibana's or pick specific ones diff --git a/x-pack/test/functional/page_objects/lens.ts b/x-pack/test/functional/page_objects/lens_page.ts similarity index 97% rename from x-pack/test/functional/page_objects/lens.ts rename to x-pack/test/functional/page_objects/lens_page.ts index 47636edab375f..1825876782d15 100644 --- a/x-pack/test/functional/page_objects/lens.ts +++ b/x-pack/test/functional/page_objects/lens_page.ts @@ -5,8 +5,10 @@ */ import { FtrProviderContext } from '../ftr_provider_context'; +import { logWrapper } from './log_wrapper'; export function LensPageProvider({ getService, getPageObjects }: FtrProviderContext) { + const log = getService('log'); const testSubjects = getService('testSubjects'); const retry = getService('retry'); const find = getService('find'); @@ -19,7 +21,7 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont 'timePicker', ]); - return { + return logWrapper('lensPage', log, { /** * Clicks the index pattern filters toggle. */ @@ -133,5 +135,5 @@ export function LensPageProvider({ getService, getPageObjects }: FtrProviderCont getTitle() { return testSubjects.getAttribute('lns_ChartTitle', 'value'); }, - }; + }); } diff --git a/x-pack/test/functional/page_objects/log_wrapper.ts b/x-pack/test/functional/page_objects/log_wrapper.ts new file mode 100644 index 0000000000000..56ab7be81caba --- /dev/null +++ b/x-pack/test/functional/page_objects/log_wrapper.ts @@ -0,0 +1,34 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { ToolingLog } from '@kbn/dev-utils'; + +/** + * Wraps the specified object instance with debug log statements of all method calls. + * + * @param prefix - The string to prefix to all log messages + * @param log - The logger to use + * @param instance - The object being wrapped + */ +export function logWrapper>( + prefix: string, + log: ToolingLog, + instance: T +): T { + return Object.keys(instance).reduce((acc, prop) => { + const baseFn = acc[prop]; + (acc as Record)[prop] = (...args: unknown[]) => { + logMethodCall(log, prefix, prop, args); + return baseFn.apply(instance, args); + }; + return acc; + }, instance); +} + +function logMethodCall(log: ToolingLog, prefix: string, prop: string, args: unknown[]) { + const argsStr = args.map(arg => (typeof arg === 'string' ? `'${arg}'` : arg)).join(', '); + log.debug(`${prefix}.${prop}(${argsStr})`); +}