[8.9] [FTR] use xpath locator to find particular element in group by index (#161202) (#163291)

# Backport

This will backport the following commits from `main` to `8.9`:
- [[FTR] use xpath locator to find particular element in group by index
(#161202)](https://github.com/elastic/kibana/pull/161202)

<!--- Backport version: 8.9.7 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Dzmitry
Lemechko","email":"dzmitry.lemechko@elastic.co"},"sourceCommit":{"committedDate":"2023-08-07T11:39:29Z","message":"[FTR]
use xpath locator to find particular element in group by index
(#161202)\n\nRelated to #158972\r\n\r\nI was able to reproduce the
failure locally on the latest main with a\r\nsmall code
change:\r\n\r\n\r\nb3d7b71076/test/functional/page_objects/visual_builder_page.ts (L867-L869)\r\n\r\n```\r\n
| debg comboBox.isOptionSelected, value: machine.os.raw\r\n │ info
Taking screenshot
\"/Users/dmle/github/kibana/test/functional/screenshots/failure/visualize
app visual builder Time Series basics Clicking on the chart should
cre-a71516dab48cdb296c45e2b439ed3965cfd400204827bba7ce3cf4719afb093b.png\"\r\n
│ info Current URL is:
http://localhost:5620/app/visualize#/create?type=metrics&_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:%272015-09-19T06:31:44.000Z%27,to:%272015-09-22T18:31:44.000Z%27))&_a=(filters:!(),linked:!f,query:(language:kuery,query:%27%27),uiState:(),vis:(aggs:!(),params:(axis_formatter:number,axis_position:left,axis_scale:normal,drop_last_bucket:1,id:b6ee5181-addb-436f-8190-4f2a178fee7b,index_pattern:(id:%27logstash-*%27),interval:%27%27,isModelInvalid:!f,max_lines_legend:1,series:!((axis_position:right,chart_type:line,color:%2368BC00,fill:0.5,formatter:default,id:%2724f639d7-d16e-44d1-ac0d-f2b590e94e8e%27,line_width:1,metrics:!((id:d4f68ace-b844-4e00-933f-8c3b49e19067,type:count)),override_index_pattern:0,palette:(name:default,type:palette),point_size:1,separate_axis:0,series_drop_last_bucket:0,split_mode:terms,stacked:none,terms_field:!(bytes,machine.os.raw),time_range_mode:entire_time_range)),show_grid:1,show_legend:1,time_field:%27%27,time_range_mode:entire_time_range,tooltip_mode:show_all,truncate_legend:1,type:timeseries,use_kibana_indexes:!t),title:%27%27,type:metrics))\r\n
│ info Saving page source to:
/Users/dmle/github/kibana/test/functional/failure_debug/html/visualize
app visual builder Time Series basics Clicking on the chart should
cre-a71516dab48cdb296c45e2b439ed3965cfd400204827bba7ce3cf4719afb093b.html\r\n
└- ✖ fail: visualize app visual builder Time Series basics Clicking on
the chart should create a filter for series with multiple split by terms
fields one of which has formatting\r\n │ StaleElementReferenceError:
stale element reference: stale element not found\r\n │ (Session info:
chrome=114.0.5735.198)\r\n │ at Object.throwDecodedError
(node_modules/selenium-webdriver/lib/error.js:524:15)\r\n │ at
parseHttpResponse
(node_modules/selenium-webdriver/lib/http.js:601:13)\r\n │ at
Executor.execute
(node_modules/selenium-webdriver/lib/http.js:529:28)\r\n │ at
runMicrotasks (<anonymous>)\r\n │ at processTicksAndRejections
(node:internal/process/task_queues:96:5)\r\n │ at Task.exec
(prevent_parallel_calls.ts:28:20)\r\n```\r\n\r\n### Do we have any
service to help with StaleElementReferenceError\r\nhandling in
FTR?\r\n\r\nWe do. `WebElementWrapper` object have internal mechanism to
handle it:\r\nwe wrap most of the actions (click, type, getAttribute,
etc.) with\r\n`retryCall` function, which execute command up to
RETRY_MAX_ATTEMPTS\r\ntimes (3 by default) and check for errors. So if
we try to click element\r\nthat is no longer in the DOM, this mechanism
will try to find the\r\nelement in the DOM again, assuming it is still
there but referenceId was\r\nchanged due to page updates. And repeat the
click.\r\n\r\nhttps://github.com/elastic/kibana/blob/main/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts#L107-L140\r\n\r\nYou
might notice a warning during timePicker dates selection\r\n```\r\n │
debg
Find.findByCssSelector('[data-test-subj=\"superDatePickerAbsoluteTab\"]')
with timeout=10000\r\n │ warn WebElementWrapper.click:
StaleElementReferenceError: stale element reference: stale element not
found\r\n │ (Session info: chrome=114.0.5735.198)\r\n │ debg Searching
again for the element 'By(css selector,
[data-test-subj=\"superDatePickerAbsoluteTab\"])',\r\n | 2 attempts
left\r\n```\r\nIt is helping a lot to minimize the flakiness.\r\n\r\n###
Why does FTR still fails with StaleElementReferenceError in\r\nVisualize
tests?\r\nBecause most methods use the same pattern for searching
elements in the\r\nDOM: get all elements and then pick one by
index.\r\n\r\n```\r\nconst byFields = await
this.testSubjects.findAll('fieldSelectItem');\r\nconst selectedByField =
byFields[byFields.length - 1];\r\nawait
this.comboBox.setElement(selectedByField, field);\r\n```\r\n\r\nThe
problem is that WebElementWrapper retry mechanism relies on
having\r\n`locator` property defined and it is _only_ defined if you
search for a\r\nsingle element, e.g. `testSubjects.find` or
`by.byCssSelector`. This\r\nproperty is set to `null` for each
`WebElementWrapper` object in array\r\nyou get from `await
this.testSubjects.findAll('fieldSelectItem')`;\r\nSo when we pass this
object in `this.comboBox.setElement` and go through\r\na list of
actions, it will fail on the first
StaleElementReferenceError\r\noccurrence.\r\n\r\nThe devil is in the
detail: usually searching for multiple elements and\r\ndoing actions on
some of it is totally fine. But comboBox selection has\r\nquite many
actions with different child elements, that can be updated in\r\nDOM and
lead to the error. Wrapping things with classical `retry`\r\nservice
only hides the issue, but does not actually solve it.\r\n\r\n###
Proposed solution\r\nAfaik CSS locators does not support searching for
elements and picking\r\nup one by index (I looked at `:nth-child()\r\n`,
but it is not what we need).\r\nBut we can use xpath locators for this
purpose. \r\n```\r\nconst selectedByField = await
this.find.byXPath(`(//*[@data-test-subj='fieldSelectItem'])[last()]`);\r\nawait
this.comboBox.setElement(selectedByField, field);\r\n```\r\n\r\nThis way
`selectedByField` has locator property defined and internal\r\nmechanism
will be able to retry actions.\r\n\r\n#### Why don't we store `locator`
for multiple elements search.\r\nWe can, but it means every element in
array has the same locator and\r\nwhile retrying to find it WebDriver
will return the first one in the DOM\r\nignoring the element index.
Using xpath locator is basically the simpler\r\nversion of storing
element index in WebElementWrapper object.\r\n\r\nFlaky test runner: 50x
for
visualize/group1..5/config.ts\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2554\r\n\r\n100x
for
group5\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2596#0189403c-ccd2-4186-a83e-af21fe88018c\r\n\r\n100x
flaky test
runner\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2794","sha":"8e1d66fab6d09ec01946672911e874dc8a9d2050","branchLabelMapping":{"^v8.10.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["test_ui_functional","release_note:skip","v8.10.0","v8.9.1"],"number":161202,"url":"https://github.com/elastic/kibana/pull/161202","mergeCommit":{"message":"[FTR]
use xpath locator to find particular element in group by index
(#161202)\n\nRelated to #158972\r\n\r\nI was able to reproduce the
failure locally on the latest main with a\r\nsmall code
change:\r\n\r\n\r\nb3d7b71076/test/functional/page_objects/visual_builder_page.ts (L867-L869)\r\n\r\n```\r\n
| debg comboBox.isOptionSelected, value: machine.os.raw\r\n │ info
Taking screenshot
\"/Users/dmle/github/kibana/test/functional/screenshots/failure/visualize
app visual builder Time Series basics Clicking on the chart should
cre-a71516dab48cdb296c45e2b439ed3965cfd400204827bba7ce3cf4719afb093b.png\"\r\n
│ info Current URL is:
http://localhost:5620/app/visualize#/create?type=metrics&_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:%272015-09-19T06:31:44.000Z%27,to:%272015-09-22T18:31:44.000Z%27))&_a=(filters:!(),linked:!f,query:(language:kuery,query:%27%27),uiState:(),vis:(aggs:!(),params:(axis_formatter:number,axis_position:left,axis_scale:normal,drop_last_bucket:1,id:b6ee5181-addb-436f-8190-4f2a178fee7b,index_pattern:(id:%27logstash-*%27),interval:%27%27,isModelInvalid:!f,max_lines_legend:1,series:!((axis_position:right,chart_type:line,color:%2368BC00,fill:0.5,formatter:default,id:%2724f639d7-d16e-44d1-ac0d-f2b590e94e8e%27,line_width:1,metrics:!((id:d4f68ace-b844-4e00-933f-8c3b49e19067,type:count)),override_index_pattern:0,palette:(name:default,type:palette),point_size:1,separate_axis:0,series_drop_last_bucket:0,split_mode:terms,stacked:none,terms_field:!(bytes,machine.os.raw),time_range_mode:entire_time_range)),show_grid:1,show_legend:1,time_field:%27%27,time_range_mode:entire_time_range,tooltip_mode:show_all,truncate_legend:1,type:timeseries,use_kibana_indexes:!t),title:%27%27,type:metrics))\r\n
│ info Saving page source to:
/Users/dmle/github/kibana/test/functional/failure_debug/html/visualize
app visual builder Time Series basics Clicking on the chart should
cre-a71516dab48cdb296c45e2b439ed3965cfd400204827bba7ce3cf4719afb093b.html\r\n
└- ✖ fail: visualize app visual builder Time Series basics Clicking on
the chart should create a filter for series with multiple split by terms
fields one of which has formatting\r\n │ StaleElementReferenceError:
stale element reference: stale element not found\r\n │ (Session info:
chrome=114.0.5735.198)\r\n │ at Object.throwDecodedError
(node_modules/selenium-webdriver/lib/error.js:524:15)\r\n │ at
parseHttpResponse
(node_modules/selenium-webdriver/lib/http.js:601:13)\r\n │ at
Executor.execute
(node_modules/selenium-webdriver/lib/http.js:529:28)\r\n │ at
runMicrotasks (<anonymous>)\r\n │ at processTicksAndRejections
(node:internal/process/task_queues:96:5)\r\n │ at Task.exec
(prevent_parallel_calls.ts:28:20)\r\n```\r\n\r\n### Do we have any
service to help with StaleElementReferenceError\r\nhandling in
FTR?\r\n\r\nWe do. `WebElementWrapper` object have internal mechanism to
handle it:\r\nwe wrap most of the actions (click, type, getAttribute,
etc.) with\r\n`retryCall` function, which execute command up to
RETRY_MAX_ATTEMPTS\r\ntimes (3 by default) and check for errors. So if
we try to click element\r\nthat is no longer in the DOM, this mechanism
will try to find the\r\nelement in the DOM again, assuming it is still
there but referenceId was\r\nchanged due to page updates. And repeat the
click.\r\n\r\nhttps://github.com/elastic/kibana/blob/main/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts#L107-L140\r\n\r\nYou
might notice a warning during timePicker dates selection\r\n```\r\n │
debg
Find.findByCssSelector('[data-test-subj=\"superDatePickerAbsoluteTab\"]')
with timeout=10000\r\n │ warn WebElementWrapper.click:
StaleElementReferenceError: stale element reference: stale element not
found\r\n │ (Session info: chrome=114.0.5735.198)\r\n │ debg Searching
again for the element 'By(css selector,
[data-test-subj=\"superDatePickerAbsoluteTab\"])',\r\n | 2 attempts
left\r\n```\r\nIt is helping a lot to minimize the flakiness.\r\n\r\n###
Why does FTR still fails with StaleElementReferenceError in\r\nVisualize
tests?\r\nBecause most methods use the same pattern for searching
elements in the\r\nDOM: get all elements and then pick one by
index.\r\n\r\n```\r\nconst byFields = await
this.testSubjects.findAll('fieldSelectItem');\r\nconst selectedByField =
byFields[byFields.length - 1];\r\nawait
this.comboBox.setElement(selectedByField, field);\r\n```\r\n\r\nThe
problem is that WebElementWrapper retry mechanism relies on
having\r\n`locator` property defined and it is _only_ defined if you
search for a\r\nsingle element, e.g. `testSubjects.find` or
`by.byCssSelector`. This\r\nproperty is set to `null` for each
`WebElementWrapper` object in array\r\nyou get from `await
this.testSubjects.findAll('fieldSelectItem')`;\r\nSo when we pass this
object in `this.comboBox.setElement` and go through\r\na list of
actions, it will fail on the first
StaleElementReferenceError\r\noccurrence.\r\n\r\nThe devil is in the
detail: usually searching for multiple elements and\r\ndoing actions on
some of it is totally fine. But comboBox selection has\r\nquite many
actions with different child elements, that can be updated in\r\nDOM and
lead to the error. Wrapping things with classical `retry`\r\nservice
only hides the issue, but does not actually solve it.\r\n\r\n###
Proposed solution\r\nAfaik CSS locators does not support searching for
elements and picking\r\nup one by index (I looked at `:nth-child()\r\n`,
but it is not what we need).\r\nBut we can use xpath locators for this
purpose. \r\n```\r\nconst selectedByField = await
this.find.byXPath(`(//*[@data-test-subj='fieldSelectItem'])[last()]`);\r\nawait
this.comboBox.setElement(selectedByField, field);\r\n```\r\n\r\nThis way
`selectedByField` has locator property defined and internal\r\nmechanism
will be able to retry actions.\r\n\r\n#### Why don't we store `locator`
for multiple elements search.\r\nWe can, but it means every element in
array has the same locator and\r\nwhile retrying to find it WebDriver
will return the first one in the DOM\r\nignoring the element index.
Using xpath locator is basically the simpler\r\nversion of storing
element index in WebElementWrapper object.\r\n\r\nFlaky test runner: 50x
for
visualize/group1..5/config.ts\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2554\r\n\r\n100x
for
group5\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2596#0189403c-ccd2-4186-a83e-af21fe88018c\r\n\r\n100x
flaky test
runner\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2794","sha":"8e1d66fab6d09ec01946672911e874dc8a9d2050"}},"sourceBranch":"main","suggestedTargetBranches":["8.9"],"targetPullRequestStates":[{"branch":"main","label":"v8.10.0","labelRegex":"^v8.10.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/161202","number":161202,"mergeCommit":{"message":"[FTR]
use xpath locator to find particular element in group by index
(#161202)\n\nRelated to #158972\r\n\r\nI was able to reproduce the
failure locally on the latest main with a\r\nsmall code
change:\r\n\r\n\r\nb3d7b71076/test/functional/page_objects/visual_builder_page.ts (L867-L869)\r\n\r\n```\r\n
| debg comboBox.isOptionSelected, value: machine.os.raw\r\n │ info
Taking screenshot
\"/Users/dmle/github/kibana/test/functional/screenshots/failure/visualize
app visual builder Time Series basics Clicking on the chart should
cre-a71516dab48cdb296c45e2b439ed3965cfd400204827bba7ce3cf4719afb093b.png\"\r\n
│ info Current URL is:
http://localhost:5620/app/visualize#/create?type=metrics&_g=(filters:!(),refreshInterval:(pause:!t,value:60000),time:(from:%272015-09-19T06:31:44.000Z%27,to:%272015-09-22T18:31:44.000Z%27))&_a=(filters:!(),linked:!f,query:(language:kuery,query:%27%27),uiState:(),vis:(aggs:!(),params:(axis_formatter:number,axis_position:left,axis_scale:normal,drop_last_bucket:1,id:b6ee5181-addb-436f-8190-4f2a178fee7b,index_pattern:(id:%27logstash-*%27),interval:%27%27,isModelInvalid:!f,max_lines_legend:1,series:!((axis_position:right,chart_type:line,color:%2368BC00,fill:0.5,formatter:default,id:%2724f639d7-d16e-44d1-ac0d-f2b590e94e8e%27,line_width:1,metrics:!((id:d4f68ace-b844-4e00-933f-8c3b49e19067,type:count)),override_index_pattern:0,palette:(name:default,type:palette),point_size:1,separate_axis:0,series_drop_last_bucket:0,split_mode:terms,stacked:none,terms_field:!(bytes,machine.os.raw),time_range_mode:entire_time_range)),show_grid:1,show_legend:1,time_field:%27%27,time_range_mode:entire_time_range,tooltip_mode:show_all,truncate_legend:1,type:timeseries,use_kibana_indexes:!t),title:%27%27,type:metrics))\r\n
│ info Saving page source to:
/Users/dmle/github/kibana/test/functional/failure_debug/html/visualize
app visual builder Time Series basics Clicking on the chart should
cre-a71516dab48cdb296c45e2b439ed3965cfd400204827bba7ce3cf4719afb093b.html\r\n
└- ✖ fail: visualize app visual builder Time Series basics Clicking on
the chart should create a filter for series with multiple split by terms
fields one of which has formatting\r\n │ StaleElementReferenceError:
stale element reference: stale element not found\r\n │ (Session info:
chrome=114.0.5735.198)\r\n │ at Object.throwDecodedError
(node_modules/selenium-webdriver/lib/error.js:524:15)\r\n │ at
parseHttpResponse
(node_modules/selenium-webdriver/lib/http.js:601:13)\r\n │ at
Executor.execute
(node_modules/selenium-webdriver/lib/http.js:529:28)\r\n │ at
runMicrotasks (<anonymous>)\r\n │ at processTicksAndRejections
(node:internal/process/task_queues:96:5)\r\n │ at Task.exec
(prevent_parallel_calls.ts:28:20)\r\n```\r\n\r\n### Do we have any
service to help with StaleElementReferenceError\r\nhandling in
FTR?\r\n\r\nWe do. `WebElementWrapper` object have internal mechanism to
handle it:\r\nwe wrap most of the actions (click, type, getAttribute,
etc.) with\r\n`retryCall` function, which execute command up to
RETRY_MAX_ATTEMPTS\r\ntimes (3 by default) and check for errors. So if
we try to click element\r\nthat is no longer in the DOM, this mechanism
will try to find the\r\nelement in the DOM again, assuming it is still
there but referenceId was\r\nchanged due to page updates. And repeat the
click.\r\n\r\nhttps://github.com/elastic/kibana/blob/main/test/functional/services/lib/web_element_wrapper/web_element_wrapper.ts#L107-L140\r\n\r\nYou
might notice a warning during timePicker dates selection\r\n```\r\n │
debg
Find.findByCssSelector('[data-test-subj=\"superDatePickerAbsoluteTab\"]')
with timeout=10000\r\n │ warn WebElementWrapper.click:
StaleElementReferenceError: stale element reference: stale element not
found\r\n │ (Session info: chrome=114.0.5735.198)\r\n │ debg Searching
again for the element 'By(css selector,
[data-test-subj=\"superDatePickerAbsoluteTab\"])',\r\n | 2 attempts
left\r\n```\r\nIt is helping a lot to minimize the flakiness.\r\n\r\n###
Why does FTR still fails with StaleElementReferenceError in\r\nVisualize
tests?\r\nBecause most methods use the same pattern for searching
elements in the\r\nDOM: get all elements and then pick one by
index.\r\n\r\n```\r\nconst byFields = await
this.testSubjects.findAll('fieldSelectItem');\r\nconst selectedByField =
byFields[byFields.length - 1];\r\nawait
this.comboBox.setElement(selectedByField, field);\r\n```\r\n\r\nThe
problem is that WebElementWrapper retry mechanism relies on
having\r\n`locator` property defined and it is _only_ defined if you
search for a\r\nsingle element, e.g. `testSubjects.find` or
`by.byCssSelector`. This\r\nproperty is set to `null` for each
`WebElementWrapper` object in array\r\nyou get from `await
this.testSubjects.findAll('fieldSelectItem')`;\r\nSo when we pass this
object in `this.comboBox.setElement` and go through\r\na list of
actions, it will fail on the first
StaleElementReferenceError\r\noccurrence.\r\n\r\nThe devil is in the
detail: usually searching for multiple elements and\r\ndoing actions on
some of it is totally fine. But comboBox selection has\r\nquite many
actions with different child elements, that can be updated in\r\nDOM and
lead to the error. Wrapping things with classical `retry`\r\nservice
only hides the issue, but does not actually solve it.\r\n\r\n###
Proposed solution\r\nAfaik CSS locators does not support searching for
elements and picking\r\nup one by index (I looked at `:nth-child()\r\n`,
but it is not what we need).\r\nBut we can use xpath locators for this
purpose. \r\n```\r\nconst selectedByField = await
this.find.byXPath(`(//*[@data-test-subj='fieldSelectItem'])[last()]`);\r\nawait
this.comboBox.setElement(selectedByField, field);\r\n```\r\n\r\nThis way
`selectedByField` has locator property defined and internal\r\nmechanism
will be able to retry actions.\r\n\r\n#### Why don't we store `locator`
for multiple elements search.\r\nWe can, but it means every element in
array has the same locator and\r\nwhile retrying to find it WebDriver
will return the first one in the DOM\r\nignoring the element index.
Using xpath locator is basically the simpler\r\nversion of storing
element index in WebElementWrapper object.\r\n\r\nFlaky test runner: 50x
for
visualize/group1..5/config.ts\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2554\r\n\r\n100x
for
group5\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2596#0189403c-ccd2-4186-a83e-af21fe88018c\r\n\r\n100x
flaky test
runner\r\nhttps://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/2794","sha":"8e1d66fab6d09ec01946672911e874dc8a9d2050"}},{"branch":"8.9","label":"v8.9.1","labelRegex":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Dzmitry Lemechko <dzmitry.lemechko@elastic.co>
This commit is contained in:
Kibana Machine 2023-08-07 08:53:47 -04:00 committed by GitHub
parent 1906969726
commit e1a00f0377
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 69 deletions

View file

@ -44,6 +44,7 @@ export class CalculationVars extends Component {
<EuiFlexItem>
<EuiFieldText
className="tvbAggs__varName"
data-test-subj="tvbAggsVarNameInput"
aria-label={i18n.translate('visTypeTimeseries.vars.variableNameAriaLabel', {
defaultMessage: 'Variable name',
})}
@ -54,7 +55,10 @@ export class CalculationVars extends Component {
value={row.name}
/>
</EuiFlexItem>
<EuiFlexItem className="tvbAggs__varMetricWrapper">
<EuiFlexItem
className="tvbAggs__varMetricWrapper"
data-test-subj="tvbAggsVarMetricWrapper"
>
<MetricSelect
onChange={this.handleChange(row, 'field')}
metrics={this.props.metrics}

View file

@ -11,16 +11,9 @@ import expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const { visualize, visualBuilder, timeToVisualize, dashboard, header, common, visChart } =
getPageObjects([
'visualBuilder',
'visualize',
'timeToVisualize',
'dashboard',
'header',
'common',
'visChart',
]);
const { visualize, visualBuilder, timeToVisualize, dashboard, common, visChart } = getPageObjects(
['visualBuilder', 'visualize', 'timeToVisualize', 'dashboard', 'header', 'common', 'visChart']
);
const security = getService('security');
const testSubjects = getService('testSubjects');
const retry = getService('retry');
@ -225,14 +218,11 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const expectedFilterPills = ['0, win 7'];
await visualBuilder.setMetricsGroupByTerms('bytes');
await visChart.waitForVisualizationRenderingStabilized();
await header.waitUntilLoadingHasFinished();
await visualBuilder.setAnotherGroupByTermsField('machine.os.raw');
await visChart.waitForVisualizationRenderingStabilized();
await header.waitUntilLoadingHasFinished();
await visualBuilder.clickSeriesOption();
await visualBuilder.setChartType('Bar');
await visChart.waitForVisualizationRenderingStabilized();
await header.waitUntilLoadingHasFinished();
await visualBuilder.clickPanelOptions('timeSeries');
await visualBuilder.setIntervalValue('1w');

View file

@ -7,6 +7,7 @@
*/
import type { DebugState } from '@elastic/charts';
import expect from '@kbn/expect';
import { FtrService } from '../ftr_provider_context';
import { WebElementWrapper } from '../services/lib/web_element_wrapper';
@ -260,8 +261,10 @@ export class VisualBuilderPageObject extends FtrService {
}
public async clickSeriesOption(nth = 0) {
const el = await this.testSubjects.findAll('seriesOptions');
await el[nth].click();
const button = await this.find.byXPath(
`(//button[@data-test-subj='seriesOptions'])[${nth + 1}]`
);
await button.click();
}
public async clearOffsetSeries() {
@ -325,8 +328,7 @@ export class VisualBuilderPageObject extends FtrService {
});
}
if (decimalPlaces) {
const decimalPlacesInput = await this.testSubjects.find('dataFormatPickerDurationDecimal');
await decimalPlacesInput.type(decimalPlaces);
await this.testSubjects.setValue('dataFormatPickerDurationDecimal', decimalPlaces);
}
}
@ -437,22 +439,30 @@ export class VisualBuilderPageObject extends FtrService {
}
public async selectAggType(value: string, nth = 0) {
const elements = await this.testSubjects.findAll('aggSelector');
await this.comboBox.setElement(elements[nth], value);
const element = await this.find.byXPath(`(//div[@data-test-subj='aggSelector'])[${nth + 1}]`);
await this.comboBox.setElement(element, value);
return await this.header.waitUntilLoadingHasFinished();
}
public async fillInExpression(expression: string, nth = 0) {
const expressions = await this.testSubjects.findAll('mathExpression');
await expressions[nth].type(expression);
const element = await this.find.byXPath(
`(//textarea[@data-test-subj='mathExpression'])[${nth + 1}]`
);
await element.type(expression);
return await this.header.waitUntilLoadingHasFinished();
}
public async fillInVariable(name = 'test', metric = 'Count', nth = 0) {
const elements = await this.testSubjects.findAll('varRow');
const varNameInput = await elements[nth].findByCssSelector('.tvbAggs__varName');
const varNameInput = await this.find.byXPath(
`(//div[@data-test-subj="varRow"])[${nth + 1}]//input[@data-test-subj='tvbAggsVarNameInput']`
);
await varNameInput.type(name);
const metricSelectWrapper = await elements[nth].findByCssSelector('.tvbAggs__varMetricWrapper');
const metricSelectWrapper = await this.find.byXPath(
`(//div[@data-test-subj="varRow"])[${
nth + 1
}]//div[@data-test-subj='tvbAggsVarMetricWrapper']`
);
await this.comboBox.setElement(metricSelectWrapper, metric);
return await this.header.waitUntilLoadingHasFinished();
}
@ -510,19 +520,16 @@ export class VisualBuilderPageObject extends FtrService {
}
public async setAnnotationFilter(query: string) {
const annotationQueryBar = await this.testSubjects.find('annotationQueryBar');
await annotationQueryBar.type(query);
await this.testSubjects.setValue('annotationQueryBar', query);
await this.header.waitUntilLoadingHasFinished();
}
public async setAnnotationFields(fields: string) {
const annotationFieldsInput = await this.testSubjects.find('annotationFieldsInput');
await annotationFieldsInput.type(fields);
await this.testSubjects.setValue('annotationFieldsInput', fields);
}
public async setAnnotationRowTemplate(template: string) {
const annotationRowTemplateInput = await this.testSubjects.find('annotationRowTemplateInput');
await annotationRowTemplateInput.type(template);
await this.testSubjects.setValue('annotationRowTemplateInput', template);
}
public async toggleIndexPatternSelectionModePopover(shouldOpen: boolean) {
@ -644,7 +651,7 @@ export class VisualBuilderPageObject extends FtrService {
}
public async setStaticValue(value: number, nth: number = 0): Promise<void> {
const input = (await this.testSubjects.findAll('staticValue'))[nth];
const input = await this.find.byXPath(`(//input[@data-test-subj='staticValue'])[${nth + 1}]`);
await input.type(value.toString());
}
@ -691,16 +698,17 @@ export class VisualBuilderPageObject extends FtrService {
}
public async getFieldForAggregation(aggNth: number = 0): Promise<WebElementWrapper> {
const labels = await this.testSubjects.findAll('aggRow');
const label = labels[aggNth];
return (await label.findAllByTestSubject('comboBoxInput'))[1];
// Aggregation has 2 comboBox elements: Aggregation Type and Field
// Locator picks the aggregation by index (aggNth) and its Field comboBox child by index (2)
return await this.find.byXPath(
`((//div[@data-test-subj='aggRow'])[${aggNth + 1}]//div[@data-test-subj='comboBoxInput'])[2]`
);
}
public async clickColorPicker(nth: number = 0): Promise<void> {
const picker = (await this.find.allByCssSelector('[data-test-subj="tvbColorPicker"] button'))[
nth
];
const picker = await this.find.byXPath(
`(//button[@data-test-subj='euiColorPickerAnchor'])[${nth + 1}]`
);
await picker.clickMouseButton();
}
@ -838,22 +846,27 @@ export class VisualBuilderPageObject extends FtrService {
await this.setMetricsGroupBy('terms');
await this.common.sleep(1000);
const byField = await this.testSubjects.find('groupByField');
await this.retry.try(async () => {
await this.comboBox.setElement(byField, field);
});
await this.comboBox.setElement(byField, field);
const isSelected = await this.comboBox.isOptionSelected(byField, field);
expect(isSelected).to.be(true);
await this.setMetricsGroupByFiltering(filtering.include, filtering.exclude);
}
public async setAnotherGroupByTermsField(field: string) {
const fieldSelectAddButtons = await this.testSubjects.findAll('fieldSelectItemAddBtn');
await fieldSelectAddButtons[fieldSelectAddButtons.length - 1].click();
// Using xpath locator to find the last element
const fieldSelectAddButtonLast = await this.find.byXPath(
`(//*[@data-test-subj='fieldSelectItemAddBtn'])[last()]`
);
// In case of StaleElementReferenceError 'browser' service will try to find element again
await fieldSelectAddButtonLast.click();
await this.common.sleep(2000);
const byFields = await this.testSubjects.findAll('fieldSelectItem');
const selectedByField = byFields[byFields.length - 1];
await this.retry.try(async () => {
await this.comboBox.setElement(selectedByField, field);
});
const selectedByField = await this.find.byXPath(
`(//*[@data-test-subj='fieldSelectItem'])[last()]`
);
await this.comboBox.setElement(selectedByField, field);
const isSelected = await this.comboBox.isOptionSelected(selectedByField, field);
expect(isSelected).to.be(true);
}
public async setMetricsGroupByFiltering(include?: string, exclude?: string) {
@ -881,34 +894,40 @@ export class VisualBuilderPageObject extends FtrService {
}
public async setGroupByFilterQuery(query: string, nth: number = 0) {
const filterQueryInput = await this.testSubjects.findAll('filterItemsQueryBar');
await filterQueryInput[nth].type(query);
const filterQueryInput = await this.find.byXPath(
`(//textarea[@data-test-subj='filterItemsQueryBar'])[${nth + 1}]`
);
await filterQueryInput.type(query);
}
public async setGroupByFilterLabel(label: string, nth: number = 0) {
const filterLabelInput = await this.testSubjects.findAll('filterItemsLabel');
await filterLabelInput[nth].type(label);
const filterLabelInput = await this.find.byXPath(
`(//input[@data-test-subj='filterItemsLabel'])[${nth + 1}]`
);
await filterLabelInput.type(label);
}
public async setChartType(type: 'Bar' | 'Line', nth: number = 0) {
const seriesChartTypeComboBoxes = await this.testSubjects.findAll('seriesChartTypeComboBox');
return await this.comboBox.setElement(seriesChartTypeComboBoxes[nth], type);
const seriesChartTypeComboBox = await this.find.byXPath(
`(//div[@data-test-subj='seriesChartTypeComboBox'])[${nth + 1}]`
);
return await this.comboBox.setElement(seriesChartTypeComboBox, type);
}
public async setStackedType(stackedType: string, nth: number = 0) {
const seriesChartTypeComboBoxes = await this.testSubjects.findAll('seriesStackedComboBox');
return await this.comboBox.setElement(seriesChartTypeComboBoxes[nth], stackedType);
const seriesStackedComboBox = await this.find.byXPath(
`(//div[@data-test-subj='seriesStackedComboBox'])[${nth + 1}]`
);
return await this.comboBox.setElement(seriesStackedComboBox, stackedType);
}
public async setSeriesFilter(query: string) {
const seriesFilterQueryInput = await this.testSubjects.find('seriesConfigQueryBar');
await seriesFilterQueryInput.type(query);
await this.testSubjects.setValue('seriesConfigQueryBar', query);
await this.header.waitUntilLoadingHasFinished();
}
public async setPanelFilter(query: string) {
const panelFilterQueryInput = await this.testSubjects.find('panelFilterQueryBar');
await panelFilterQueryInput.type(query);
await this.testSubjects.setValue('panelFilterQueryBar', query);
await this.header.waitUntilLoadingHasFinished();
}
@ -938,8 +957,7 @@ export class VisualBuilderPageObject extends FtrService {
}
public async setFilterRatioOption(optionType: 'Numerator' | 'Denominator', query: string) {
const optionInput = await this.testSubjects.find(`filterRatio${optionType}Input`);
await optionInput.type(query);
await this.testSubjects.setValue(`filterRatio${optionType}Input`, query);
}
public async clickSeriesLegendItem(name: string) {

View file

@ -38,8 +38,9 @@ export class ComboBoxService extends FtrService {
public async setForLastInput(comboBoxSelector: string, value: string): Promise<void> {
this.log.debug(`comboBox.set, comboBoxSelector: ${comboBoxSelector}`);
const comboBoxes = await this.testSubjects.findAll(comboBoxSelector);
const comboBox = comboBoxes[comboBoxes.length - 1];
const comboBox = await this.find.byXPath(
`(//*[@data-test-subj='${comboBoxSelector}'])[last()]`
);
await this.setElement(comboBox, value);
}
@ -354,9 +355,10 @@ export class ComboBoxService extends FtrService {
public async clearLastInputField(comboBoxSelector: string): Promise<void> {
this.log.debug(`comboBox.clearInputField, comboBoxSelector:${comboBoxSelector}`);
const comboBoxElements = await this.testSubjects.findAll(comboBoxSelector);
const comboBoxElement = comboBoxElements[comboBoxElements.length - 1];
const input = await comboBoxElement.findByTagName('input');
const comboBox = await this.find.byXPath(
`(//*[@data-test-subj='${comboBoxSelector}'])[last()]`
);
const input = await comboBox.findByTagName('input');
await input.clearValueWithKeyboard();
}