From 8b7fa0d3f8be933ea391ba4b2a862d4276dca9f1 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Mon, 20 May 2024 11:49:22 +0200 Subject: [PATCH] [SLO] Synthetics based SLO e2e tests (#183637) ## Summary Setting up Elastic/Synthetics based slo e2e tests !! --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .buildkite/ftr_configs.yml | 1 + .../pipelines/pull_request/slo_plugin_e2e.yml | 17 ++++ .../pipelines/pull_request/pipeline.ts | 4 + .../steps/functional/slo_plugin_e2e.sh | 16 ++++ x-pack/packages/kbn-data-forge/index.ts | 1 + x-pack/packages/kbn-data-forge/src/cli.ts | 5 +- .../packages/kbn-data-forge/src/constants.ts | 2 +- .../src/lib/cli_to_partial_config.ts | 2 +- .../kbn-data-forge/src/lib/create_config.ts | 2 +- .../kbn-data-forge/src/lib/index_schedule.ts | 2 +- .../kbn-data-forge/src/types/index.ts | 5 +- .../slo/e2e/journeys/index.ts | 8 ++ .../slo/e2e/journeys/slos_overview.journey.ts | 50 ++++++++++ .../slo/e2e/page_objects/slo_app.tsx | 33 +++++++ .../slo/e2e/services/slo_data_service.ts | 94 +++++++++++++++++++ .../slo/e2e/synthetics_run.ts | 35 +++++++ .../slo/e2e/tsconfig.json | 18 ++++ .../components/card_view/slo_card_item.tsx | 1 + .../observability_solution/slo/scripts/e2e.js | 13 +++ .../e2e/helpers/synthetics_runner.ts | 6 +- .../synthetics/e2e/index.ts | 11 +++ 21 files changed, 317 insertions(+), 9 deletions(-) create mode 100644 .buildkite/pipelines/pull_request/slo_plugin_e2e.yml create mode 100755 .buildkite/scripts/steps/functional/slo_plugin_e2e.sh create mode 100644 x-pack/plugins/observability_solution/slo/e2e/journeys/index.ts create mode 100644 x-pack/plugins/observability_solution/slo/e2e/journeys/slos_overview.journey.ts create mode 100644 x-pack/plugins/observability_solution/slo/e2e/page_objects/slo_app.tsx create mode 100644 x-pack/plugins/observability_solution/slo/e2e/services/slo_data_service.ts create mode 100644 x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts create mode 100644 x-pack/plugins/observability_solution/slo/e2e/tsconfig.json create mode 100644 x-pack/plugins/observability_solution/slo/scripts/e2e.js create mode 100644 x-pack/plugins/observability_solution/synthetics/e2e/index.ts diff --git a/.buildkite/ftr_configs.yml b/.buildkite/ftr_configs.yml index 5b1734613c0f6..9a73a5a00e8d6 100644 --- a/.buildkite/ftr_configs.yml +++ b/.buildkite/ftr_configs.yml @@ -63,6 +63,7 @@ disabled: - x-pack/plugins/observability_solution/synthetics/e2e/synthetics/synthetics_run.ts - x-pack/plugins/observability_solution/exploratory_view/e2e/synthetics_run.ts - x-pack/plugins/observability_solution/ux/e2e/synthetics_run.ts + - x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts # Configs that exist but weren't running in CI when this file was introduced - x-pack/test/alerting_api_integration/security_and_spaces/group2/tests/actions/config.ts diff --git a/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml b/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml new file mode 100644 index 0000000000000..83332811bcc20 --- /dev/null +++ b/.buildkite/pipelines/pull_request/slo_plugin_e2e.yml @@ -0,0 +1,17 @@ +steps: + - command: .buildkite/scripts/steps/functional/slo_plugin_e2e.sh + label: 'SLO Plugin @elastic/synthetics Tests' + agents: + queue: n2-4-spot + depends_on: + - build + - quick_checks + timeout_in_minutes: 30 + artifact_paths: + - 'x-pack/plugins/observability_solution/slo/e2e/.journeys/**/*' + retry: + automatic: + - exit_status: '-1' + limit: 3 + - exit_status: '*' + limit: 1 diff --git a/.buildkite/scripts/pipelines/pull_request/pipeline.ts b/.buildkite/scripts/pipelines/pull_request/pipeline.ts index 13b89678ccac6..db0e8239d9701 100644 --- a/.buildkite/scripts/pipelines/pull_request/pipeline.ts +++ b/.buildkite/scripts/pipelines/pull_request/pipeline.ts @@ -139,6 +139,10 @@ const uploadPipeline = (pipelineContent: string | object) => { pipeline.push(getPipeline('.buildkite/pipelines/pull_request/ux_plugin_e2e.yml')); } + if (await doAnyChangesMatch([/^x-pack\/plugins\/observability_solution/])) { + pipeline.push(getPipeline('.buildkite/pipelines/pull_request/slo_plugin_e2e.yml')); + } + if ( GITHUB_PR_LABELS.includes('ci:deploy-cloud') || GITHUB_PR_LABELS.includes('ci:cloud-deploy') || diff --git a/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh b/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh new file mode 100755 index 0000000000000..95007fbff85bf --- /dev/null +++ b/.buildkite/scripts/steps/functional/slo_plugin_e2e.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -euo pipefail + +source .buildkite/scripts/common/util.sh + +.buildkite/scripts/bootstrap.sh +.buildkite/scripts/download_build_artifacts.sh + +export JOB=kibana-ux-plugin-synthetics + +echo "--- SLO @elastic/synthetics Tests" + +cd "$XPACK_DIR" + +node plugins/observability_solution/slo/scripts/e2e.js --kibana-install-dir "$KIBANA_BUILD_LOCATION" ${GREP:+--grep \"${GREP}\"} diff --git a/x-pack/packages/kbn-data-forge/index.ts b/x-pack/packages/kbn-data-forge/index.ts index bb9d0e104f4a7..056c63e9964a6 100644 --- a/x-pack/packages/kbn-data-forge/index.ts +++ b/x-pack/packages/kbn-data-forge/index.ts @@ -19,3 +19,4 @@ export { cli } from './src/cli'; export { generate } from './src/generate'; export { cleanup } from './src/cleanup'; export { createConfig, readConfig } from './src/lib/create_config'; +export { DEFAULTS } from './src/constants'; diff --git a/x-pack/packages/kbn-data-forge/src/cli.ts b/x-pack/packages/kbn-data-forge/src/cli.ts index 9d7e9e3bca026..88a8aad6bce8d 100644 --- a/x-pack/packages/kbn-data-forge/src/cli.ts +++ b/x-pack/packages/kbn-data-forge/src/cli.ts @@ -6,14 +6,15 @@ */ import { ToolingLog } from '@kbn/tooling-log'; +import { CliOptions } from './types'; import { cliOptionsToPartialConfig } from './lib/cli_to_partial_config'; import { createConfig, readConfig } from './lib/create_config'; import { getEsClient } from './lib/get_es_client'; import { parseCliOptions } from './lib/parse_cli_options'; import { run } from './run'; -export async function cli() { - const options = parseCliOptions(); +export async function cli(cliOptions?: CliOptions) { + const options = cliOptions ?? parseCliOptions(); const partialConfig = options.config ? await readConfig(options.config) : cliOptionsToPartialConfig(options); diff --git a/x-pack/packages/kbn-data-forge/src/constants.ts b/x-pack/packages/kbn-data-forge/src/constants.ts index 0b5ab1978d983..afcc715916ab5 100644 --- a/x-pack/packages/kbn-data-forge/src/constants.ts +++ b/x-pack/packages/kbn-data-forge/src/constants.ts @@ -34,5 +34,5 @@ export const DEFAULTS = { EVENT_TEMPLATE: 'good', REDUCE_WEEKEND_TRAFFIC_BY: 0, EPHEMERAL_PROJECT_IDS: 0, - ALIGN_EVENTS_TO_INTERVAL: 0, + ALIGN_EVENTS_TO_INTERVAL: true, }; diff --git a/x-pack/packages/kbn-data-forge/src/lib/cli_to_partial_config.ts b/x-pack/packages/kbn-data-forge/src/lib/cli_to_partial_config.ts index b116f262303db..5d7d10d787bdc 100644 --- a/x-pack/packages/kbn-data-forge/src/lib/cli_to_partial_config.ts +++ b/x-pack/packages/kbn-data-forge/src/lib/cli_to_partial_config.ts @@ -14,7 +14,7 @@ export function cliOptionsToPartialConfig(options: CliOptions) { const schedule: Schedule = { template: options.eventTemplate, start: options.lookback, - end: false, + end: options.scheduleEnd ?? false, }; const decodedDataset = DatasetRT.decode(options.dataset); diff --git a/x-pack/packages/kbn-data-forge/src/lib/create_config.ts b/x-pack/packages/kbn-data-forge/src/lib/create_config.ts index c8a48b2632998..56b59b7d01365 100644 --- a/x-pack/packages/kbn-data-forge/src/lib/create_config.ts +++ b/x-pack/packages/kbn-data-forge/src/lib/create_config.ts @@ -62,7 +62,7 @@ export function createConfig(partialConfig: PartialConfig = {}) { concurrency: DEFAULTS.CONCURRENCY, reduceWeekendTrafficBy: DEFAULTS.REDUCE_WEEKEND_TRAFFIC_BY, ephemeralProjectIds: DEFAULTS.EPHEMERAL_PROJECT_IDS, - alignEventsToInterval: DEFAULTS.ALIGN_EVENTS_TO_INTERVAL === 1, + alignEventsToInterval: DEFAULTS.ALIGN_EVENTS_TO_INTERVAL, ...(partialConfig.indexing ?? {}), }, schedule: partialConfig.schedule ?? [schedule], diff --git a/x-pack/packages/kbn-data-forge/src/lib/index_schedule.ts b/x-pack/packages/kbn-data-forge/src/lib/index_schedule.ts index 916efe551dfc4..a18ce031c19a1 100644 --- a/x-pack/packages/kbn-data-forge/src/lib/index_schedule.ts +++ b/x-pack/packages/kbn-data-forge/src/lib/index_schedule.ts @@ -52,7 +52,7 @@ export async function indexSchedule(config: Config, client: Client, logger: Tool logger.info( `Indexing "${schedule.template}" events from ${startTs.toISOString()} to ${ - end === false ? 'indefinatly' : end.toISOString() + end === false ? 'indefinitely' : end.toISOString() }` ); await createEvents( diff --git a/x-pack/packages/kbn-data-forge/src/types/index.ts b/x-pack/packages/kbn-data-forge/src/types/index.ts index b84805d8ecb93..ac445478329c2 100644 --- a/x-pack/packages/kbn-data-forge/src/types/index.ts +++ b/x-pack/packages/kbn-data-forge/src/types/index.ts @@ -159,7 +159,7 @@ export interface Point { } export interface CliOptions { - config: string; + config?: string; lookback: string; eventsPerCycle: number; payloadSize: number; @@ -170,7 +170,7 @@ export interface CliOptions { elasticsearchHost: string; elasticsearchUsername: string; elasticsearchPassword: string; - elasticsearchApiKey: undefined | string; + elasticsearchApiKey?: undefined | string; kibanaUrl: string; kibanaUsername: string; kibanaPassword: string; @@ -179,4 +179,5 @@ export interface CliOptions { reduceWeekendTrafficBy: number; ephemeralProjectIds: number; alignEventsToInterval: boolean; + scheduleEnd?: string; } diff --git a/x-pack/plugins/observability_solution/slo/e2e/journeys/index.ts b/x-pack/plugins/observability_solution/slo/e2e/journeys/index.ts new file mode 100644 index 0000000000000..c8a63354ad35d --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/journeys/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './slos_overview.journey'; diff --git a/x-pack/plugins/observability_solution/slo/e2e/journeys/slos_overview.journey.ts b/x-pack/plugins/observability_solution/slo/e2e/journeys/slos_overview.journey.ts new file mode 100644 index 0000000000000..da83a88ac02d2 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/journeys/slos_overview.journey.ts @@ -0,0 +1,50 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { journey, step, before, expect } from '@elastic/synthetics'; +import { RetryService } from '@kbn/ftr-common-functional-services'; +import { SLoDataService } from '../services/slo_data_service'; +import { sloAppPageProvider } from '../page_objects/slo_app'; + +journey(`SLOsOverview`, async ({ page, params }) => { + const sloApp = sloAppPageProvider({ page, kibanaUrl: params.kibanaUrl }); + const dataService = new SLoDataService({ + kibanaUrl: params.kibanaUrl, + elasticsearchUrl: params.elasticsearchUrl, + getService: params.getService, + }); + + const retry: RetryService = params.getService('retry'); + + before(async () => { + await dataService.generateSloData(); + await dataService.addSLO(); + }); + + step('Go to slos overview', async () => { + await sloApp.navigateToOverview(true); + }); + + step('validate data retention tab', async () => { + await retry.tryWithRetries( + 'check if slos are displayed', + async () => { + await page.waitForSelector('text="Test Stack SLO"'); + const cards = await page.locator('text="Test Stack SLO"').all(); + expect(cards.length > 5).toBeTruthy(); + }, + { + retryCount: 50, + retryDelay: 20000, + timeout: 60 * 20000, + }, + async () => { + await page.getByTestId('querySubmitButton').click(); + } + ); + }); +}); diff --git a/x-pack/plugins/observability_solution/slo/e2e/page_objects/slo_app.tsx b/x-pack/plugins/observability_solution/slo/e2e/page_objects/slo_app.tsx new file mode 100644 index 0000000000000..09d3470bea775 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/page_objects/slo_app.tsx @@ -0,0 +1,33 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { Page } from '@elastic/synthetics'; +import { loginPageProvider } from '@kbn/synthetics-plugin/e2e/page_objects/login'; +import { utilsPageProvider } from '@kbn/synthetics-plugin/e2e/page_objects/utils'; +import { recordVideo } from '@kbn/synthetics-plugin/e2e/helpers/record_video'; + +export function sloAppPageProvider({ page, kibanaUrl }: { page: Page; kibanaUrl: string }) { + page.setDefaultTimeout(60 * 1000); + recordVideo(page); + + return { + ...loginPageProvider({ + page, + username: 'elastic', + password: 'changeme', + }), + ...utilsPageProvider({ page }), + + async navigateToOverview(doLogin = false) { + await page.goto(`${kibanaUrl}/app/slo`, { + waitUntil: 'networkidle', + }); + if (doLogin) { + await this.loginToKibana(); + } + }, + }; +} diff --git a/x-pack/plugins/observability_solution/slo/e2e/services/slo_data_service.ts b/x-pack/plugins/observability_solution/slo/e2e/services/slo_data_service.ts new file mode 100644 index 0000000000000..a6f30e24e1771 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/services/slo_data_service.ts @@ -0,0 +1,94 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { KbnClient } from '@kbn/test'; +import { cli, DEFAULTS } from '@kbn/data-forge'; + +export class SLoDataService { + kibanaUrl: string; + elasticsearchUrl: string; + params: Record; + requester: KbnClient['requester']; + + constructor(params: Record) { + this.kibanaUrl = params.kibanaUrl; + this.elasticsearchUrl = params.elasticsearchUrl; + this.requester = params.getService('kibanaServer').requester; + this.params = params; + } + + async generateSloData({ + lookback = 'now-1d', + eventsPerCycle = 50, + }: { + lookback?: string; + eventsPerCycle?: number; + } = {}) { + await cli({ + kibanaUrl: this.kibanaUrl, + elasticsearchHost: this.elasticsearchUrl, + lookback: DEFAULTS.LOOKBACK, + eventsPerCycle: DEFAULTS.EVENTS_PER_CYCLE, + payloadSize: DEFAULTS.PAYLOAD_SIZE, + concurrency: DEFAULTS.CONCURRENCY, + indexInterval: 10_000, + dataset: 'fake_stack', + scenario: DEFAULTS.SCENARIO, + elasticsearchUsername: DEFAULTS.ELASTICSEARCH_USERNAME, + elasticsearchPassword: DEFAULTS.ELASTICSEARCH_PASSWORD, + kibanaUsername: DEFAULTS.KIBANA_USERNAME, + kibanaPassword: DEFAULTS.KIBANA_PASSWORD, + installKibanaAssets: true, + eventTemplate: DEFAULTS.EVENT_TEMPLATE, + reduceWeekendTrafficBy: DEFAULTS.REDUCE_WEEKEND_TRAFFIC_BY, + ephemeralProjectIds: DEFAULTS.EPHEMERAL_PROJECT_IDS, + alignEventsToInterval: DEFAULTS.ALIGN_EVENTS_TO_INTERVAL, + scheduleEnd: 'now+10m', + }).then((res) => { + // eslint-disable-next-line no-console + console.log(res); + }); + } + + async addSLO() { + const example = { + name: 'Test Stack SLO', + description: '', + indicator: { + type: 'sli.kql.custom', + params: { + index: 'kbn-data-forge-fake_stack.admin-console-*', + filter: '', + good: 'log.level : "INFO" ', + total: '', + timestampField: '@timestamp', + }, + }, + budgetingMethod: 'occurrences', + timeWindow: { + duration: '30d', + type: 'rolling', + }, + objective: { + target: 0.99, + }, + tags: [], + groupBy: ['user.id'], + }; + try { + const { data } = await this.requester.request({ + description: 'get monitor by id', + path: '/api/observability/slos', + body: example, + method: 'POST', + }); + return data; + } catch (e) { + console.error(e); + } + } +} diff --git a/x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts b/x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts new file mode 100644 index 0000000000000..d8a4946cfc447 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/synthetics_run.ts @@ -0,0 +1,35 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ +import { FtrConfigProviderContext } from '@kbn/test'; +import { SyntheticsRunner, argv } from '@kbn/synthetics-plugin/e2e'; + +const { headless, grep, bail: pauseOnError } = argv; + +async function runE2ETests({ readConfigFile }: FtrConfigProviderContext) { + const kibanaConfig = await readConfigFile(require.resolve('@kbn/synthetics-plugin/e2e/config')); + return { + ...kibanaConfig.getAll(), + testRunner: async ({ getService }: any) => { + const syntheticsRunner = new SyntheticsRunner(getService, { + headless, + match: grep, + pauseOnError, + }); + + await syntheticsRunner.setup(); + + await syntheticsRunner.loadTestFiles(async () => { + require('./journeys'); + }); + + await syntheticsRunner.run(); + }, + }; +} + +// eslint-disable-next-line import/no-default-export +export default runE2ETests; diff --git a/x-pack/plugins/observability_solution/slo/e2e/tsconfig.json b/x-pack/plugins/observability_solution/slo/e2e/tsconfig.json new file mode 100644 index 0000000000000..46ac09ac5a0d5 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/e2e/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "../../../../../tsconfig.base.json", + "exclude": ["tmp", "target/**/*"], + "include": ["**/*"], + "compilerOptions": { + "outDir": "target/types", + "types": [ "node"], + "isolatedModules": false, + }, + "kbn_references": [ + { "path": "../../../../test/tsconfig.json" }, + { "path": "../../../../../test/tsconfig.json" }, + "@kbn/test", + "@kbn/ftr-common-functional-services", + "@kbn/synthetics-plugin/e2e", + "@kbn/data-forge", + ] +} diff --git a/x-pack/plugins/observability_solution/slo/public/pages/slos/components/card_view/slo_card_item.tsx b/x-pack/plugins/observability_solution/slo/public/pages/slos/components/card_view/slo_card_item.tsx index deb2a1b880ad7..ebdc068435a76 100644 --- a/x-pack/plugins/observability_solution/slo/public/pages/slos/components/card_view/slo_card_item.tsx +++ b/x-pack/plugins/observability_solution/slo/public/pages/slos/components/card_view/slo_card_item.tsx @@ -108,6 +108,7 @@ export function SloCardItem({ slo, rules, activeAlerts, historicalSummary, refet return ( <> } onMouseOver={() => { if (!isMouseOver) { diff --git a/x-pack/plugins/observability_solution/slo/scripts/e2e.js b/x-pack/plugins/observability_solution/slo/scripts/e2e.js new file mode 100644 index 0000000000000..3fe4979236fb1 --- /dev/null +++ b/x-pack/plugins/observability_solution/slo/scripts/e2e.js @@ -0,0 +1,13 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +/* eslint-disable no-console */ +const { executeSyntheticsRunner } = require('@kbn/synthetics-plugin/scripts/base_e2e'); +const path = require('path'); + +const e2eDir = path.join(__dirname, '../e2e'); +executeSyntheticsRunner(e2eDir); diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/helpers/synthetics_runner.ts b/x-pack/plugins/observability_solution/synthetics/e2e/helpers/synthetics_runner.ts index 97e0fe72d81d6..11c71943c4710 100644 --- a/x-pack/plugins/observability_solution/synthetics/e2e/helpers/synthetics_runner.ts +++ b/x-pack/plugins/observability_solution/synthetics/e2e/helpers/synthetics_runner.ts @@ -112,7 +112,11 @@ export class SyntheticsRunner { let results: PromiseType> = {}; for (let i = 0; i < noOfRuns; i++) { results = await syntheticsRun({ - params: { kibanaUrl: this.kibanaUrl, getService: this.getService }, + params: { + kibanaUrl: this.kibanaUrl, + getService: this.getService, + elasticsearchUrl: this.elasticsearchUrl, + }, playwrightOptions: { headless: headless ?? !CI, testIdAttribute: 'data-test-subj', diff --git a/x-pack/plugins/observability_solution/synthetics/e2e/index.ts b/x-pack/plugins/observability_solution/synthetics/e2e/index.ts new file mode 100644 index 0000000000000..f0e9b5e8b760c --- /dev/null +++ b/x-pack/plugins/observability_solution/synthetics/e2e/index.ts @@ -0,0 +1,11 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { SyntheticsRunner } from './helpers/synthetics_runner'; +export { argv } from './helpers/parse_args_params'; +export { loginPageProvider } from './page_objects/login'; +export { utilsPageProvider } from './page_objects/utils';