mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 09:19:04 -04:00
* get wiring done for timelion application functional tests * add tests for expression type ahead functions, arguments, and argument values * provide argument suggestions when argument name not provided * updates from cjcenizal review
This commit is contained in:
parent
048e7a922f
commit
375c0764c3
11 changed files with 535 additions and 14 deletions
|
@ -54,11 +54,21 @@ argument
|
|||
currentArgs.push(arg);
|
||||
return arg;
|
||||
}
|
||||
/ name:argument_name space? '=' {
|
||||
/ space? '=' space? value:arg_type? {
|
||||
var exception = {
|
||||
type: 'incompleteArgument',
|
||||
currentArgs: currentArgs,
|
||||
currentFunction: currentFunction,
|
||||
location: simpleLocation(location()),
|
||||
text: text()
|
||||
}
|
||||
error(JSON.stringify(exception));
|
||||
}
|
||||
/ name:argument_name space? '=' {
|
||||
var exception = {
|
||||
type: 'incompleteArgumentValue',
|
||||
currentArgs: currentArgs,
|
||||
currentFunction: currentFunction,
|
||||
name: name,
|
||||
location: simpleLocation(location()),
|
||||
text: text()
|
||||
|
|
|
@ -75,7 +75,79 @@ describe('Timelion expression suggestions', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('incompleteArgument', () => {
|
||||
describe('no argument name provided', () => {
|
||||
it('should return no argument suggestions when none provided by help', async () => {
|
||||
const expression = '.otherFunc(=)';
|
||||
const cursorPosition = 0;
|
||||
const suggestions = await suggest(expression, functionList, Parser, cursorPosition, argValueSuggestions);
|
||||
expect(suggestions).to.eql({
|
||||
'list': [],
|
||||
'location': {
|
||||
'min': 11,
|
||||
'max': 12
|
||||
},
|
||||
'type': 'arguments'
|
||||
});
|
||||
});
|
||||
|
||||
it('should return argument suggestions when provided by help', async () => {
|
||||
const expression = '.myFunc2(=)';
|
||||
const cursorPosition = 0;
|
||||
const suggestions = await suggest(expression, functionList, Parser, cursorPosition, argValueSuggestions);
|
||||
expect(suggestions).to.eql({
|
||||
'list': myFunc2.args,
|
||||
'location': {
|
||||
'min': 9,
|
||||
'max': 10
|
||||
},
|
||||
'type': 'arguments'
|
||||
});
|
||||
});
|
||||
|
||||
it('should return argument suggestions when argument value provided', async () => {
|
||||
const expression = '.myFunc2(=whatArgumentAmI)';
|
||||
const cursorPosition = 0;
|
||||
const suggestions = await suggest(expression, functionList, Parser, cursorPosition, argValueSuggestions);
|
||||
expect(suggestions).to.eql({
|
||||
'list': myFunc2.args,
|
||||
'location': {
|
||||
'min': 9,
|
||||
'max': 25
|
||||
},
|
||||
'type': 'arguments'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not show first argument for chainable functions', async () => {
|
||||
const expression = '.func1(=)';
|
||||
const cursorPosition = 0;
|
||||
const suggestions = await suggest(expression, functionList, Parser, cursorPosition, argValueSuggestions);
|
||||
expect(suggestions).to.eql({
|
||||
'list': [{ name: 'argA' }, { name: 'argAB', suggestions: [{ name: 'value1' }] }],
|
||||
'location': {
|
||||
'min': 7,
|
||||
'max': 8
|
||||
},
|
||||
'type': 'arguments'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not provide argument suggestions for argument that is all ready set in function def', async () => {
|
||||
const expression = '.myFunc2(argAB=provided,=)';
|
||||
const cursorPosition = 0;
|
||||
const suggestions = await suggest(expression, functionList, Parser, cursorPosition, argValueSuggestions);
|
||||
expect(suggestions).to.eql({
|
||||
'list': [{ name: 'argA' }, { name: 'argABC' }],
|
||||
'location': {
|
||||
'min': 24,
|
||||
'max': 25
|
||||
},
|
||||
'type': 'arguments'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('no argument value provided', () => {
|
||||
it('should return no argument value suggestions when not provided by help', async () => {
|
||||
const expression = '.func1(argA=)';
|
||||
const cursorPosition = 11;
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
For some reasons it doesn't work without it (even though the default role of
|
||||
the element is textbox anyway). -->
|
||||
<textarea
|
||||
id="timelionExpressionTextArea"
|
||||
data-expression-input
|
||||
role="textbox"
|
||||
rows="{{ rows }}"
|
||||
|
@ -28,6 +27,7 @@
|
|||
aria-autocomplete="list"
|
||||
aria-controls="timelionSuggestionList"
|
||||
aria-activedescendant="{{ getActiveSuggestionId() }}"
|
||||
data-test-subj="timelionExpressionTextArea"
|
||||
></textarea>
|
||||
|
||||
<timelion-expression-suggestions
|
||||
|
|
|
@ -73,6 +73,23 @@ function inLocation(cursorPosition, location) {
|
|||
return cursorPosition >= location.min && cursorPosition <= location.max;
|
||||
}
|
||||
|
||||
function getArgumentsHelp(functionHelp, functionArgs = []) {
|
||||
if (!functionHelp) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Do not provide 'inputSeries' as argument suggestion for chainable functions
|
||||
const argsHelp = functionHelp.chainable ? functionHelp.args.slice(1) : functionHelp.args.slice(0);
|
||||
|
||||
// ignore arguments that are already provided in function declaration
|
||||
const functionArgNames = functionArgs.map((arg) => {
|
||||
return arg.name;
|
||||
});
|
||||
return argsHelp.filter(arg => {
|
||||
return !functionArgNames.includes(arg.name);
|
||||
});
|
||||
}
|
||||
|
||||
async function extractSuggestionsFromParsedResult(result, cursorPosition, functionList, argValueSuggestions) {
|
||||
const activeFunc = result.functions.find((func) => {
|
||||
return cursorPosition >= func.location.min && cursorPosition < func.location.max;
|
||||
|
@ -123,17 +140,8 @@ async function extractSuggestionsFromParsedResult(result, cursorPosition, functi
|
|||
}
|
||||
|
||||
// return argument suggestions
|
||||
const providedArguments = activeFunc.arguments.map((arg) => {
|
||||
return arg.name;
|
||||
});
|
||||
// Do not provide 'inputSeries' as argument suggestion for chainable functions
|
||||
const args = functionHelp.chainable ? functionHelp.args.slice(1) : functionHelp.args.slice(0);
|
||||
const argumentSuggestions = args.filter(arg => {
|
||||
// ignore arguments that are all ready provided in function declaration
|
||||
if (providedArguments.includes(arg.name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const argsHelp = getArgumentsHelp(functionHelp, activeFunc.arguments);
|
||||
const argumentSuggestions = argsHelp.filter(arg => {
|
||||
if (_.get(activeArg, 'type') === 'namedArg') {
|
||||
return _.startsWith(arg.name, activeArg.name);
|
||||
} else if (activeArg) {
|
||||
|
@ -177,6 +185,18 @@ export async function suggest(expression, functionList, Parser, cursorPosition,
|
|||
return { list, location: message.location, type: SUGGESTION_TYPE.FUNCTIONS };
|
||||
}
|
||||
case 'incompleteArgument': {
|
||||
const {
|
||||
currentFunction: functionName,
|
||||
currentArgs: functionArgs,
|
||||
} = message;
|
||||
const functionHelp = functionList.find(func => func.name === functionName);
|
||||
return {
|
||||
list: getArgumentsHelp(functionHelp, functionArgs),
|
||||
location: message.location,
|
||||
type: SUGGESTION_TYPE.ARGUMENTS
|
||||
};
|
||||
}
|
||||
case 'incompleteArgumentValue': {
|
||||
const {
|
||||
name: argName,
|
||||
currentFunction: functionName,
|
||||
|
|
96
test/functional/apps/timelion/_expression_typeahead.js
Normal file
96
test/functional/apps/timelion/_expression_typeahead.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
import expect from 'expect.js';
|
||||
|
||||
export default function ({ getPageObjects }) {
|
||||
const PageObjects = getPageObjects(['common', 'timelion', 'header', 'settings']);
|
||||
|
||||
describe('expression typeahead', () => {
|
||||
before(async () => {
|
||||
const fromTime = '2015-09-19 06:31:44.000';
|
||||
const toTime = '2015-09-23 18:31:44.000';
|
||||
|
||||
await PageObjects.timelion.initTests();
|
||||
await PageObjects.header.setAbsoluteRange(fromTime, toTime);
|
||||
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
});
|
||||
|
||||
it('should display function suggestions filtered by function name', async () => {
|
||||
await PageObjects.timelion.setExpression('.e');
|
||||
const suggestions = await PageObjects.timelion.getSuggestionItemsText();
|
||||
expect(suggestions.length).to.eql(2);
|
||||
expect(suggestions[0].includes('.elasticsearch()')).to.eql(true);
|
||||
expect(suggestions[1].includes('.es()')).to.eql(true);
|
||||
});
|
||||
|
||||
it('should show argument suggestions when function suggestion is selected', async () => {
|
||||
await PageObjects.timelion.setExpression('.es');
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
const suggestions = await PageObjects.timelion.getSuggestionItemsText();
|
||||
expect(suggestions.length).to.eql(9);
|
||||
expect(suggestions[0].includes('fit=')).to.eql(true);
|
||||
});
|
||||
|
||||
it('should show argument value suggestions when argument is selected', async () => {
|
||||
await PageObjects.timelion.setExpression('.legend');
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
const argumentSuggestions = await PageObjects.timelion.getSuggestionItemsText();
|
||||
expect(argumentSuggestions.length).to.eql(4);
|
||||
expect(argumentSuggestions[1].includes('position=')).to.eql(true);
|
||||
await PageObjects.timelion.clickSuggestion(1);
|
||||
const valueSuggestions = await PageObjects.timelion.getSuggestionItemsText();
|
||||
expect(valueSuggestions.length).to.eql(5);
|
||||
expect(valueSuggestions[0].includes('disable legend')).to.eql(true);
|
||||
expect(valueSuggestions[1].includes('place legend in north east corner')).to.eql(true);
|
||||
});
|
||||
|
||||
describe('dynamic suggestions for argument values', () => {
|
||||
describe('.es()', () => {
|
||||
before(async () => {
|
||||
await PageObjects.timelion.setExpression('.es');
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
});
|
||||
|
||||
it('should show index pattern suggestions for index argument', async () => {
|
||||
await PageObjects.timelion.updateExpression('index');
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
await PageObjects.common.sleep(500);
|
||||
const suggestions = await PageObjects.timelion.getSuggestionItemsText();
|
||||
expect(suggestions.length).to.eql(1);
|
||||
expect(suggestions[0].includes('logstash-*')).to.eql(true);
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
});
|
||||
|
||||
it('should show field suggestions for timefield argument when index pattern set', async () => {
|
||||
await PageObjects.timelion.updateExpression(',timefield');
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
await PageObjects.common.sleep(500);
|
||||
const suggestions = await PageObjects.timelion.getSuggestionItemsText();
|
||||
expect(suggestions.length).to.eql(4);
|
||||
expect(suggestions[0].includes('@timestamp')).to.eql(true);
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
});
|
||||
|
||||
it('should show field suggestions for split argument when index pattern set', async () => {
|
||||
await PageObjects.timelion.updateExpression(',split');
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
await PageObjects.common.sleep(500);
|
||||
const suggestions = await PageObjects.timelion.getSuggestionItemsText();
|
||||
expect(suggestions.length).to.eql(52);
|
||||
expect(suggestions[0].includes('@message.raw')).to.eql(true);
|
||||
await PageObjects.timelion.clickSuggestion(10);
|
||||
});
|
||||
|
||||
it('should show field suggestions for metric argument when index pattern set', async () => {
|
||||
await PageObjects.timelion.updateExpression(',metric');
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
await PageObjects.common.sleep(500);
|
||||
await PageObjects.timelion.updateExpression('avg:');
|
||||
await PageObjects.timelion.clickSuggestion();
|
||||
const suggestions = await PageObjects.timelion.getSuggestionItemsText();
|
||||
expect(suggestions.length).to.eql(2);
|
||||
expect(suggestions[0].includes('avg:bytes')).to.eql(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
17
test/functional/apps/timelion/index.js
Normal file
17
test/functional/apps/timelion/index.js
Normal file
|
@ -0,0 +1,17 @@
|
|||
export default function ({ getService, loadTestFile }) {
|
||||
const remote = getService('remote');
|
||||
const log = getService('log');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
describe('timelion app', function () {
|
||||
before(async function () {
|
||||
log.debug('Starting timelion before method');
|
||||
remote.setWindowSize(1280, 800);
|
||||
await esArchiver.loadIfNeeded('logstash_functional');
|
||||
await kibanaServer.uiSettings.replace({ 'dateFormat:tz': 'UTC', 'defaultIndex': 'logstash-*' });
|
||||
});
|
||||
|
||||
loadTestFile(require.resolve('./_expression_typeahead'));
|
||||
});
|
||||
}
|
|
@ -11,6 +11,7 @@ import {
|
|||
MonitoringPageProvider,
|
||||
PointSeriesPageProvider,
|
||||
VisualBuilderPageProvider,
|
||||
TimelionPageProvider,
|
||||
} from './page_objects';
|
||||
|
||||
import {
|
||||
|
@ -36,6 +37,7 @@ export default async function ({ readConfigFile }) {
|
|||
require.resolve('./apps/discover'),
|
||||
require.resolve('./apps/management'),
|
||||
require.resolve('./apps/status_page'),
|
||||
require.resolve('./apps/timelion'),
|
||||
require.resolve('./apps/visualize'),
|
||||
require.resolve('./apps/xpack'),
|
||||
],
|
||||
|
@ -52,6 +54,7 @@ export default async function ({ readConfigFile }) {
|
|||
monitoring: MonitoringPageProvider,
|
||||
pointSeries: PointSeriesPageProvider,
|
||||
visualBuilder: VisualBuilderPageProvider,
|
||||
timelion: TimelionPageProvider
|
||||
},
|
||||
services: {
|
||||
es: commonConfig.get('services.es'),
|
||||
|
@ -93,6 +96,9 @@ export default async function ({ readConfigFile }) {
|
|||
pathname: '/app/kibana',
|
||||
hash: '/management',
|
||||
},
|
||||
timelion: {
|
||||
pathname: '/app/timelion',
|
||||
},
|
||||
console: {
|
||||
pathname: '/app/kibana',
|
||||
hash: '/dev_tools/console',
|
||||
|
|
BIN
test/functional/fixtures/es_archiver/timelion/data.json.gz
Normal file
BIN
test/functional/fixtures/es_archiver/timelion/data.json.gz
Normal file
Binary file not shown.
246
test/functional/fixtures/es_archiver/timelion/mappings.json
Normal file
246
test/functional/fixtures/es_archiver/timelion/mappings.json
Normal file
|
@ -0,0 +1,246 @@
|
|||
{
|
||||
"type": "index",
|
||||
"value": {
|
||||
"index": ".kibana",
|
||||
"settings": {
|
||||
"index": {
|
||||
"number_of_shards": "1",
|
||||
"number_of_replicas": "1"
|
||||
}
|
||||
},
|
||||
"mappings": {
|
||||
"doc": {
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"index-pattern": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"fieldFormatMap": {
|
||||
"type": "text"
|
||||
},
|
||||
"fields": {
|
||||
"type": "text"
|
||||
},
|
||||
"intervalName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"notExpandable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"sourceFilters": {
|
||||
"type": "text"
|
||||
},
|
||||
"timeFieldName": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"search": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"columns": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"hits": {
|
||||
"type": "integer"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sort": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timelion-sheet": {
|
||||
"dynamic": "strict",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
},
|
||||
"server": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"uuid": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"config": {
|
||||
"dynamic": "true",
|
||||
"properties": {
|
||||
"buildNum": {
|
||||
"type": "keyword"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dashboard": {
|
||||
"dynamic": "strict",
|
||||
"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"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
}
|
||||
}
|
||||
},
|
||||
"visualization": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"description": {
|
||||
"type": "text"
|
||||
},
|
||||
"kibanaSavedObjectMeta": {
|
||||
"properties": {
|
||||
"searchSourceJSON": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"savedSearchId": {
|
||||
"type": "keyword"
|
||||
},
|
||||
"title": {
|
||||
"type": "text"
|
||||
},
|
||||
"uiStateJSON": {
|
||||
"type": "text"
|
||||
},
|
||||
"version": {
|
||||
"type": "integer"
|
||||
},
|
||||
"visState": {
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"dynamic": "strict",
|
||||
"properties": {
|
||||
"accessCount": {
|
||||
"type": "long"
|
||||
},
|
||||
"accessDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"createDate": {
|
||||
"type": "date"
|
||||
},
|
||||
"url": {
|
||||
"type": "text",
|
||||
"fields": {
|
||||
"keyword": {
|
||||
"type": "keyword",
|
||||
"ignore_above": 2048
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,3 +10,4 @@ export { SettingsPageProvider } from './settings_page';
|
|||
export { MonitoringPageProvider } from './monitoring_page';
|
||||
export { PointSeriesPageProvider } from './point_series_page';
|
||||
export { VisualBuilderPageProvider } from './visual_builder_page';
|
||||
export { TimelionPageProvider } from './timelion_page';
|
||||
|
|
53
test/functional/page_objects/timelion_page.js
Normal file
53
test/functional/page_objects/timelion_page.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
export function TimelionPageProvider({ getService, getPageObjects }) {
|
||||
const testSubjects = getService('testSubjects');
|
||||
const find = getService('find');
|
||||
const log = getService('log');
|
||||
const PageObjects = getPageObjects(['common', 'header']);
|
||||
const esArchiver = getService('esArchiver');
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
class TimelionPage {
|
||||
async initTests() {
|
||||
await kibanaServer.uiSettings.replace({
|
||||
'dateFormat:tz': 'UTC',
|
||||
'defaultIndex': 'logstash-*'
|
||||
});
|
||||
|
||||
log.debug('load kibana index');
|
||||
await esArchiver.load('timelion');
|
||||
|
||||
await PageObjects.common.navigateToApp('timelion');
|
||||
}
|
||||
|
||||
async setExpression(expression) {
|
||||
const input = await testSubjects.find('timelionExpressionTextArea');
|
||||
await input.clearValue();
|
||||
await input.type(expression);
|
||||
}
|
||||
|
||||
async updateExpression(updates) {
|
||||
const input = await testSubjects.find('timelionExpressionTextArea');
|
||||
await input.type(updates);
|
||||
}
|
||||
|
||||
async getExpression() {
|
||||
const input = await testSubjects.find('timelionExpressionTextArea');
|
||||
return input.getVisibleText();
|
||||
}
|
||||
|
||||
async getSuggestionItemsText() {
|
||||
const elements = await find.allByCssSelector('.suggestions .suggestion');
|
||||
return await Promise.all(elements.map(async element => await element.getVisibleText()));
|
||||
}
|
||||
|
||||
async clickSuggestion(suggestionIndex = 0) {
|
||||
const elements = await find.allByCssSelector('.suggestions .suggestion');
|
||||
if (suggestionIndex > elements.length) {
|
||||
throw new Error(`Unable to select suggestion ${suggestionIndex}, only ${elements.length} suggestions available.`);
|
||||
}
|
||||
await elements[suggestionIndex].click();
|
||||
}
|
||||
}
|
||||
|
||||
return new TimelionPage();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue