From dfb3272bdba0779d45972d0ab71edec3c6208eaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 13 Mar 2020 18:48:38 +0100 Subject: [PATCH] E2E: Moving files & refactoring (#22787) * E2E refactoring * Updated circleci * Updated lint paths --- .circleci/config.yml | 8 +- .gitignore | 6 +- e2e/conf/scenario1.ini | 0 e2e/run-suite | 7 +- e2e/start-and-run-suite | 8 ++ .../expected}/smoke-test-scenario.png | Bin .../suite1/specs}/queryVariableCrud.spec.ts | 0 .../suite1/specs}/smoketests.spec.ts | 4 +- .../e2e-tests => e2e/suite1}/tsconfig.json | 0 package.json | 7 +- public/e2e-test/pages/panels/editPanel.ts | 19 ---- public/e2e-test/pages/panels/panel.ts | 13 --- .../e2e-test/pages/panels/sharePanelModal.ts | 11 -- .../e2e-test/pages/templating/variablePage.ts | 92 ---------------- .../pages/templating/variablesPage.ts | 49 --------- public/e2e-test/scenarios/smoke.test.ts | 81 -------------- .../templating/templatevariables-crud.test.ts | 104 ------------------ .../theTruth/smoke-test-scenario.png | Bin 29349 -> 0 bytes 18 files changed, 25 insertions(+), 384 deletions(-) delete mode 100644 e2e/conf/scenario1.ini create mode 100755 e2e/start-and-run-suite rename {public/e2e-tests/screenShots/theTruth => e2e/suite1/screenshots/expected}/smoke-test-scenario.png (100%) rename {public/e2e-tests/integration => e2e/suite1/specs}/queryVariableCrud.spec.ts (100%) rename {public/e2e-tests/integration => e2e/suite1/specs}/smoketests.spec.ts (93%) rename {public/e2e-tests => e2e/suite1}/tsconfig.json (100%) delete mode 100644 public/e2e-test/pages/panels/editPanel.ts delete mode 100644 public/e2e-test/pages/panels/panel.ts delete mode 100644 public/e2e-test/pages/panels/sharePanelModal.ts delete mode 100644 public/e2e-test/pages/templating/variablePage.ts delete mode 100644 public/e2e-test/pages/templating/variablesPage.ts delete mode 100644 public/e2e-test/scenarios/smoke.test.ts delete mode 100644 public/e2e-test/scenarios/templating/templatevariables-crud.test.ts delete mode 100644 public/e2e-test/screenShots/theTruth/smoke-test-scenario.png diff --git a/.circleci/config.yml b/.circleci/config.yml index 87cba212117..2958e04ab68 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -517,13 +517,13 @@ jobs: command: ./e2e/run-suite no_output_timeout: 5m - store_artifacts: - path: public/e2e-tests/screenShots/theTruth + path: e2e/suite1/screenshots/expected destination: expected-screenshots - store_artifacts: - path: public/e2e-tests/screenShots/theOutput - destination: output-screenshots + path: e2e/suite1/screenshots/received + destination: received-screenshots - store_artifacts: - path: public/e2e-tests/videos + path: e2e/suite1/videos destination: output-videos - store_artifacts: path: e2e/tmp/data/log diff --git a/.gitignore b/.gitignore index 847ec7ad725..cc6bb1ee8ff 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,6 @@ compilation-stats.json /packages/grafana-e2e/cypress/screenshots /packages/grafana-e2e/cypress/videos /packages/grafana-e2e/cypress/logs -/public/e2e-test/screenShots/theOutput -/public/e2e-tests/screenShots/theOutput/*.png -/public/e2e-tests/videos +/e2e/server.log +/e2e/suite1/screenshots/received +/e2e/suite1/videos/* diff --git a/e2e/conf/scenario1.ini b/e2e/conf/scenario1.ini deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/e2e/run-suite b/e2e/run-suite index 386c0b42e40..b2f8dfb4229 100755 --- a/e2e/run-suite +++ b/e2e/run-suite @@ -4,9 +4,12 @@ echo -e "Waiting for grafana-server to finish starting" -timeout 200 bash -c 'until nc -z $0 $1; do sleep 1; done' localhost $PORT +timeout 60 bash -c 'until nc -z $0 $1; do sleep 1; done' localhost $PORT echo -e "Starting Cypress scenarios" -env BASE_URL=http://localhost:$PORT yarn e2e-tests +cd packages/grafana-e2e +yarn start --env BASE_URL=http://localhost:$PORT,CIRCLE_SHA1=$CIRCLE_SHA1,SLOWMO=$SLOWMO \ + --config integrationFolder=../../e2e/suite1/specs,screenshotsFolder=../../e2e/suite1/screenshots,videosFolder=../../e2e/suite1/videos,fileServerFolder=./cypress,viewportWidth=1920,viewportHeight=1080,trashAssetsBeforeRuns=false \ + "$@" diff --git a/e2e/start-and-run-suite b/e2e/start-and-run-suite new file mode 100755 index 00000000000..736fbb2500f --- /dev/null +++ b/e2e/start-and-run-suite @@ -0,0 +1,8 @@ +#!/bin/bash + +. e2e/variables + +# Start it in the background +./e2e/start-server 2>&1 > e2e/server.log & + +./e2e/run-suite "$@" diff --git a/public/e2e-tests/screenShots/theTruth/smoke-test-scenario.png b/e2e/suite1/screenshots/expected/smoke-test-scenario.png similarity index 100% rename from public/e2e-tests/screenShots/theTruth/smoke-test-scenario.png rename to e2e/suite1/screenshots/expected/smoke-test-scenario.png diff --git a/public/e2e-tests/integration/queryVariableCrud.spec.ts b/e2e/suite1/specs/queryVariableCrud.spec.ts similarity index 100% rename from public/e2e-tests/integration/queryVariableCrud.spec.ts rename to e2e/suite1/specs/queryVariableCrud.spec.ts diff --git a/public/e2e-tests/integration/smoketests.spec.ts b/e2e/suite1/specs/smoketests.spec.ts similarity index 93% rename from public/e2e-tests/integration/smoketests.spec.ts rename to e2e/suite1/specs/smoketests.spec.ts index 9e2cdf9b0ab..077575401ec 100644 --- a/public/e2e-tests/integration/smoketests.spec.ts +++ b/e2e/suite1/specs/smoketests.spec.ts @@ -40,8 +40,8 @@ e2e.scenario({ return; } - const theOutputImage = `${e2e.config().screenshotsFolder}/theOutput/smoke-test-scenario.png`; - const theTruthImage = `${e2e.config().screenshotsFolder}/theTruth/smoke-test-scenario.png`; + const theOutputImage = `${e2e.config().screenshotsFolder}/received/smoke-test-scenario.png`; + const theTruthImage = `${e2e.config().screenshotsFolder}/expected/smoke-test-scenario.png`; e2e().wrap( e2e.imgSrcToBlob(url).then((blob: any) => { diff --git a/public/e2e-tests/tsconfig.json b/e2e/suite1/tsconfig.json similarity index 100% rename from public/e2e-tests/tsconfig.json rename to e2e/suite1/tsconfig.json diff --git a/package.json b/package.json index 2b0a3aae2ac..eb99c302bcc 100644 --- a/package.json +++ b/package.json @@ -154,12 +154,11 @@ "api-tests": "jest --notify --watch --config=devenv/e2e-api-tests/jest.js", "build": "grunt build", "dev": "webpack --progress --colors --config scripts/webpack/webpack.dev.js", - "e2e": "cd packages/grafana-e2e && yarn start --env BASE_URL=$BASE_URL,CIRCLE_SHA1=$CIRCLE_SHA1,SLOWMO=$SLOWMO --config integrationFolder=../../public/e2e-tests/integration,screenshotsFolder=../../public/e2e-tests/screenShots,videosFolder=../../public/e2e-tests/videos,fileServerFolder=./cypress,viewportWidth=1920,viewportHeight=1080,trashAssetsBeforeRuns=false", - "e2e-tests": "yarn e2e", - "e2e-tests:debug": "SLOWMO=1 yarn e2e --headed --no-exit", + "e2e": "./e2e/start-and-run-suite", + "e2e:debug": "SLOWMO=1 && ./e2e/start-and-run-suite --headed --no-exit", "jest": "jest --notify --watch", "jest-ci": "mkdir -p reports/junit && export JEST_JUNIT_OUTPUT_DIR=reports/junit && jest --ci --reporters=default --reporters=jest-junit --maxWorkers 2", - "lint": "eslint public/app public/e2e-test public/test --ext=.js,.ts,.tsx", + "lint": "eslint public/app e2e/suite1 public/test --ext=.js,.ts,.tsx", "lint:fix": "yarn lint --fix", "packages:build": "lerna run clean && lerna run build", "packages:docsExtract": "rm -rf ./scripts/docs && lerna run docsExtract", diff --git a/public/e2e-test/pages/panels/editPanel.ts b/public/e2e-test/pages/panels/editPanel.ts deleted file mode 100644 index 1e43b0eabf8..00000000000 --- a/public/e2e-test/pages/panels/editPanel.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { ClickablePageObjectType, Selector, SelectPageObjectType, TestPage } from '@grafana/toolkit/src/e2e'; - -export interface EditPanelPage { - queriesTab: ClickablePageObjectType; - saveDashboard: ClickablePageObjectType; - scenarioSelect: SelectPageObjectType; - showXAxis: ClickablePageObjectType; - visualizationTab: ClickablePageObjectType; -} - -export const editPanelPage = new TestPage({ - pageObjects: { - queriesTab: 'Queries tab button', - saveDashboard: 'Save dashboard navbar button', - scenarioSelect: 'Scenario Select', - showXAxis: () => Selector.fromSelector('[aria-label="X-Axis section"] [label=Show] .gf-form-switch'), - visualizationTab: 'Visualization tab button', - }, -}); diff --git a/public/e2e-test/pages/panels/panel.ts b/public/e2e-test/pages/panels/panel.ts deleted file mode 100644 index 31e69338b0b..00000000000 --- a/public/e2e-test/pages/panels/panel.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { ClickablePageObjectType, TestPage } from '@grafana/toolkit/src/e2e'; - -export interface Panel { - panelTitle: ClickablePageObjectType; - share: ClickablePageObjectType; -} - -export const panel = new TestPage({ - pageObjects: { - panelTitle: 'Panel Title', - share: 'Share panel menu item', - }, -}); diff --git a/public/e2e-test/pages/panels/sharePanelModal.ts b/public/e2e-test/pages/panels/sharePanelModal.ts deleted file mode 100644 index f1f2c669e81..00000000000 --- a/public/e2e-test/pages/panels/sharePanelModal.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { ClickablePageObjectType, TestPage } from '@grafana/toolkit/src/e2e'; - -export interface SharePanelModal { - directLinkRenderedImage: ClickablePageObjectType; -} - -export const sharePanelModal = new TestPage({ - pageObjects: { - directLinkRenderedImage: 'Link to rendered image', - }, -}); diff --git a/public/e2e-test/pages/templating/variablePage.ts b/public/e2e-test/pages/templating/variablePage.ts deleted file mode 100644 index 2842845a0fe..00000000000 --- a/public/e2e-test/pages/templating/variablePage.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { - ClickablePageObjectType, - InputPageObjectType, - PageObjectType, - Selector, - SelectPageObjectType, - SwitchPageObjectType, - TestPage, -} from '@grafana/toolkit/src/e2e'; - -export interface VariablePage { - headerLink: ClickablePageObjectType; - modeLabel: PageObjectType; - generalNameInput: InputPageObjectType; - generalTypeSelect: SelectPageObjectType; - generalLabelInput: InputPageObjectType; - generalHideSelect: SelectPageObjectType; - queryOptionsDataSourceSelect: SelectPageObjectType; - queryOptionsRefreshSelect: SelectPageObjectType; - queryOptionsRegExInput: InputPageObjectType; - queryOptionsSortSelect: SelectPageObjectType; - queryOptionsQueryInput: InputPageObjectType; - selectionOptionsMultiSwitch: SwitchPageObjectType; - selectionOptionsIncludeAllSwitch: SwitchPageObjectType; - selectionOptionsCustomAllInput: InputPageObjectType; - valueGroupsTagsEnabledSwitch: SwitchPageObjectType; - valueGroupsTagsTagsQueryInput: InputPageObjectType; - valueGroupsTagsTagsValuesQueryInput: InputPageObjectType; - previewOfValuesOption: PageObjectType; - addButton: ClickablePageObjectType; - updateButton: ClickablePageObjectType; -} - -export const variablePage = new TestPage({ - pageObjects: { - headerLink: 'Variable editor Header link', - modeLabel: 'Variable editor Header mode New', - generalNameInput: 'Variable editor Form Name field', - generalTypeSelect: 'Variable editor Form Type select', - generalLabelInput: 'Variable editor Form Label field', - generalHideSelect: 'Variable editor Form Hide select', - queryOptionsDataSourceSelect: 'Variable editor Form Query DataSource select', - queryOptionsRefreshSelect: 'Variable editor Form Query Refresh select', - queryOptionsRegExInput: 'Variable editor Form Query RegEx field', - queryOptionsSortSelect: 'Variable editor Form Query Sort select', - queryOptionsQueryInput: 'Variable editor Form Default Variable Query Editor textarea', - selectionOptionsMultiSwitch: () => Selector.fromSwitchLabel('Variable editor Form Multi switch'), - selectionOptionsIncludeAllSwitch: () => Selector.fromSwitchLabel('Variable editor Form IncludeAll switch'), - selectionOptionsCustomAllInput: 'Variable editor Form IncludeAll field', - valueGroupsTagsEnabledSwitch: () => Selector.fromSwitchLabel('Variable editor Form Query UseTags switch'), - valueGroupsTagsTagsQueryInput: 'Variable editor Form Query TagsQuery field', - valueGroupsTagsTagsValuesQueryInput: 'Variable editor Form Query TagsValuesQuery field', - previewOfValuesOption: 'Variable editor Preview of Values option', - addButton: 'Variable editor Add button', - updateButton: 'Variable editor Update button', - }, -}); - -export interface CreateQueryVariableArguments { - page: TestPage; - name: string; - label: string; - datasourceName: string; - query: string; -} - -export const createQueryVariable = async ({ - page, - name, - label, - datasourceName, - query, -}: CreateQueryVariableArguments) => { - console.log('Creating a Query Variable with required'); - await page.pageObjects.generalNameInput.enter(name); - await page.pageObjects.generalLabelInput.enter(label); - await page.pageObjects.queryOptionsDataSourceSelect.select(`string:${datasourceName}`); - await page.pageObjects.queryOptionsQueryInput.exists(); - await page.pageObjects.queryOptionsQueryInput.containsPlaceholder('metric name or tags query'); - await page.pageObjects.queryOptionsQueryInput.enter(query); - await page.pageObjects.queryOptionsQueryInput.blur(); - await page.pageObjects.previewOfValuesOption.exists(); - await page.pageObjects.selectionOptionsMultiSwitch.toggle(); - await page.pageObjects.selectionOptionsMultiSwitch.isSwitchedOn(); - await page.pageObjects.selectionOptionsIncludeAllSwitch.toggle(); - await page.pageObjects.selectionOptionsIncludeAllSwitch.isSwitchedOn(); - await page.pageObjects.selectionOptionsCustomAllInput.exists(); - await page.pageObjects.selectionOptionsCustomAllInput.containsText(''); - await page.pageObjects.selectionOptionsCustomAllInput.containsPlaceholder('blank = auto'); - await page.pageObjects.addButton.click(); - console.log('Creating a Query Variable with required, OK!'); -}; diff --git a/public/e2e-test/pages/templating/variablesPage.ts b/public/e2e-test/pages/templating/variablesPage.ts deleted file mode 100644 index e1684d930eb..00000000000 --- a/public/e2e-test/pages/templating/variablesPage.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { ArrayPageObjectType, ClickablePageObjectType, TestPage } from '@grafana/toolkit/src/e2e'; - -export interface VariablesPage { - callToActionButton: ClickablePageObjectType; - variableTableNameField: ArrayPageObjectType; - variableTableDefinitionField: ArrayPageObjectType; - variableTableArrowUpButton: ArrayPageObjectType; - variableTableArrowDownButton: ArrayPageObjectType; - variableTableDuplicateButton: ArrayPageObjectType; - variableTableRemoveButton: ArrayPageObjectType; - newVariableButton: ClickablePageObjectType; - goBackButton: ClickablePageObjectType; -} - -export const variablesPage = new TestPage({ - pageObjects: { - callToActionButton: 'Call to action button Add variable', - variableTableNameField: 'Variable editor Table Name field', - variableTableDefinitionField: 'Variable editor Table Definition field', - variableTableArrowUpButton: 'Variable editor Table ArrowUp button', - variableTableArrowDownButton: 'Variable editor Table ArrowDown button', - variableTableDuplicateButton: 'Variable editor Table Duplicate button', - variableTableRemoveButton: 'Variable editor Table Remove button', - newVariableButton: 'Variable editor New variable button', - goBackButton: 'Dashboard settings Go Back button', - }, -}); - -export interface AssertVariableTableArguments { - name: string; - query: string; -} - -export const assertVariableTable = async (page: TestPage, args: AssertVariableTableArguments[]) => { - console.log('Asserting variable table'); - await page.pageObjects.variableTableNameField.waitForSelector(); - await page.pageObjects.variableTableNameField.hasLength(args.length); - await page.pageObjects.variableTableDefinitionField.hasLength(args.length); - await page.pageObjects.variableTableArrowUpButton.hasLength(args.length); - await page.pageObjects.variableTableArrowDownButton.hasLength(args.length); - await page.pageObjects.variableTableDuplicateButton.hasLength(args.length); - await page.pageObjects.variableTableRemoveButton.hasLength(args.length); - for (let index = 0; index < args.length; index++) { - const { name, query } = args[index]; - await page.pageObjects.variableTableNameField.containsTextAtPos(`$${name}`, index); - await page.pageObjects.variableTableDefinitionField.containsTextAtPos(query, index); - } - console.log('Asserting variable table, Ok'); -}; diff --git a/public/e2e-test/scenarios/smoke.test.ts b/public/e2e-test/scenarios/smoke.test.ts deleted file mode 100644 index 2684118edf1..00000000000 --- a/public/e2e-test/scenarios/smoke.test.ts +++ /dev/null @@ -1,81 +0,0 @@ -import { Browser, Page, Target } from 'puppeteer-core'; - -import { compareScreenShots, constants, e2eScenario, takeScreenShot } from '@grafana/toolkit/src/e2e'; -import { - cleanDashboard, - createDashboardPage, - dashboardsPageFactory, - saveDashboardModal, -} from '@grafana/toolkit/src/e2e/pages'; -import { panel } from 'e2e-test/pages/panels/panel'; -import { editPanelPage } from 'e2e-test/pages/panels/editPanel'; -import { sharePanelModal } from 'e2e-test/pages/panels/sharePanelModal'; - -export const addDashboardAndSetupTestDataGraph = async (page: Page) => { - // Create a new Dashboard - const dashboardTitle = `e2e - Dashboard-${new Date().getTime()}`; - await createDashboardPage.init(page); - await createDashboardPage.navigateTo(); - await createDashboardPage.pageObjects.addQuery.click(); - - await editPanelPage.init(page); - await editPanelPage.waitForNavigation(); - await editPanelPage.pageObjects.queriesTab.click(); - await editPanelPage.pageObjects.scenarioSelect.select('string:csv_metric_values'); - await editPanelPage.pageObjects.visualizationTab.click(); - await editPanelPage.pageObjects.showXAxis.click(); - await editPanelPage.pageObjects.saveDashboard.click(); - - // Confirm save modal - await saveDashboardModal.init(page); - await saveDashboardModal.expectSelector({ selector: 'save-dashboard-as-modal' }); - await saveDashboardModal.pageObjects.name.enter(dashboardTitle); - await saveDashboardModal.pageObjects.save.click(); - await saveDashboardModal.pageObjects.success.exists(); - - return dashboardTitle; -}; - -export const clickOnSharePanelImageLinkAndCompareImages = async ( - browser: Browser, - page: Page, - dashboardTitle: string -) => { - // Share the dashboard - const dashboardsPage = dashboardsPageFactory(dashboardTitle); - await dashboardsPage.init(page); - await dashboardsPage.navigateTo(); - await dashboardsPage.pageObjects.dashboard.exists(); - await dashboardsPage.pageObjects.dashboard.click(); - - await panel.init(page); - await panel.pageObjects.panelTitle.click(); - await panel.pageObjects.share.click(); - - // Verify that a new tab is opened - const targetPromise = new Promise(resolve => browser.once('targetcreated', resolve)); - await sharePanelModal.init(page); - await sharePanelModal.pageObjects.directLinkRenderedImage.click(); - const newTarget: Target = (await targetPromise) as Target; - expect(newTarget.url()).toContain(`${constants.baseUrl}/render/d-solo`); - - // Take snapshot of page only when running on CircleCI - if (process.env.CIRCLE_SHA1) { - const newPage = await newTarget.page(); - const fileName = 'smoke-test-scenario'; - await takeScreenShot(newPage, fileName); - await compareScreenShots(fileName); - } -}; - -e2eScenario({ - describeName: 'Smoke tests', - itName: 'Login scenario, create test data source, dashboard, panel, and export scenario', - skipScenario: false, - createTestDataSource: true, - scenario: async (browser: Browser, page: Page) => { - const dashboardTitle = await addDashboardAndSetupTestDataGraph(page); - await clickOnSharePanelImageLinkAndCompareImages(browser, page, dashboardTitle); - await cleanDashboard(page, dashboardTitle); - }, -}); diff --git a/public/e2e-test/scenarios/templating/templatevariables-crud.test.ts b/public/e2e-test/scenarios/templating/templatevariables-crud.test.ts deleted file mode 100644 index a6316507e0b..00000000000 --- a/public/e2e-test/scenarios/templating/templatevariables-crud.test.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { e2eScenario, TestPage } from '@grafana/toolkit/src/e2e'; -import { Browser, Page } from 'puppeteer-core'; -import { - assertVariableLabelsAndComponents, - DashboardPage, - dashboardSettingsPage, - saveChangesDashboardModal, -} from '@grafana/toolkit/src/e2e/pages'; - -import { assertVariableTable, variablesPage } from '../../pages/templating/variablesPage'; -import { createQueryVariable, variablePage } from '../../pages/templating/variablePage'; - -e2eScenario({ - describeName: 'Template Variables tests', - itName: 'Template Variables QueryVariable CRUD', - createTestDataSource: true, - createTestDashboard: true, - skipScenario: true, - scenario: async (browser: Browser, page: Page, datasourceName?: string, dashboardPage?: TestPage) => { - await dashboardPage?.pageObjects.settings.click(); - - await dashboardSettingsPage.init(page); - await dashboardSettingsPage.pageObjects.variablesSection.click(); - - await variablesPage.init(page); - await variablesPage.pageObjects.callToActionButton.exists(); - await variablesPage.pageObjects.callToActionButton.click(); - - console.log('Asserting defaults for new variable'); - await variablePage.init(page); - await variablePage.pageObjects.generalNameInput.exists(); - await variablePage.pageObjects.generalNameInput.containsText(''); - await variablePage.pageObjects.generalNameInput.containsPlaceholder('name'); - await variablePage.pageObjects.generalTypeSelect.exists(); - await variablePage.pageObjects.generalTypeSelect.selectedTextIs('Query'); - await variablePage.pageObjects.generalLabelInput.exists(); - await variablePage.pageObjects.generalLabelInput.containsText(''); - await variablePage.pageObjects.generalLabelInput.containsPlaceholder('optional display name'); - await variablePage.pageObjects.generalHideSelect.exists(); - await variablePage.pageObjects.generalHideSelect.selectedTextIs(''); - await variablePage.pageObjects.queryOptionsDataSourceSelect.exists(); - await variablePage.pageObjects.queryOptionsDataSourceSelect.selectedTextIs(''); - await variablePage.pageObjects.queryOptionsRefreshSelect.exists(); - await variablePage.pageObjects.queryOptionsRefreshSelect.selectedTextIs('Never'); - await variablePage.pageObjects.queryOptionsRegExInput.exists(); - await variablePage.pageObjects.queryOptionsRegExInput.containsText(''); - await variablePage.pageObjects.queryOptionsRegExInput.containsPlaceholder('/.*-(.*)-.*/'); - await variablePage.pageObjects.queryOptionsSortSelect.exists(); - await variablePage.pageObjects.queryOptionsSortSelect.selectedTextIs('Disabled'); - await variablePage.pageObjects.selectionOptionsMultiSwitch.exists(); - await variablePage.pageObjects.selectionOptionsMultiSwitch.isSwitchedOff(); - await variablePage.pageObjects.selectionOptionsIncludeAllSwitch.exists(); - await variablePage.pageObjects.selectionOptionsIncludeAllSwitch.isSwitchedOff(); - await variablePage.pageObjects.valueGroupsTagsEnabledSwitch.exists(); - await variablePage.pageObjects.valueGroupsTagsEnabledSwitch.isSwitchedOff(); - console.log('Asserting defaults for new variable, OK!'); - - await variablesPage.pageObjects.goBackButton.click(); - - await dashboardPage?.pageObjects.settings.click(); - - await dashboardSettingsPage.init(page); - await dashboardSettingsPage.pageObjects.variablesSection.click(); - - await variablesPage.pageObjects.callToActionButton.exists(); - await variablesPage.pageObjects.callToActionButton.click(); - - const queryVariables = [ - { name: 'query1', query: '*', label: 'query1-label', options: ['All', 'A', 'B', 'C'] }, - { name: 'query2', query: '$query1.*', label: 'query2-label', options: ['All', 'AA', 'AB', 'AC'] }, - { name: 'query3', query: '$query1.$query2.*', label: 'query2-label', options: ['All', 'AAA', 'AAB', 'AAC'] }, - ]; - - for (let queryVariableIndex = 0; queryVariableIndex < queryVariables.length; queryVariableIndex++) { - const { name, label, query } = queryVariables[queryVariableIndex]; - const asserts = queryVariables.slice(0, queryVariableIndex + 1); - await createQueryVariable({ - page: variablePage, - datasourceName: datasourceName as string, - name, - label, - query, - }); - - await assertVariableTable(variablesPage, asserts); - - await dashboardSettingsPage.pageObjects.saveDashBoard.click(); - - await saveChangesDashboardModal.init(page); - await saveChangesDashboardModal.pageObjects.save.click(); - await saveChangesDashboardModal.pageObjects.success.exists(); - - await variablesPage.pageObjects.goBackButton.click(); - - await assertVariableLabelsAndComponents(dashboardPage as TestPage, asserts); - - await dashboardPage?.pageObjects.settings.click(); - - await dashboardSettingsPage.pageObjects.variablesSection.click(); - - await variablesPage.pageObjects.newVariableButton.click(); - } - }, -}); diff --git a/public/e2e-test/screenShots/theTruth/smoke-test-scenario.png b/public/e2e-test/screenShots/theTruth/smoke-test-scenario.png deleted file mode 100644 index b294765e31a9045062e0d14547cd94dd1311a6ad..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29349 zcmeFZXH=8f7d{$n%qWV8G^vV;N*AR=P$?<`B1kWqpdi)IJDG8m9&l7Zx*#B3q<55% zpmgb72+~_5^g!-;-;DF~zt+8L-7ojUU2FUT%e?P7`|SPfy`R1J3Bk9uH4YybB%m_fl}l{?Y4TYkm+u7dj@E_c`= z!zJ^p?I3Ti$zVc=)IxL7pEjEB_p`j&FNr;+eU-)OsK8(I{0aB7PjTFE{@Ze7skYfA zF@B{Xz*os%cIy0H1A`JsGU%VVMMMfC{Z~Z0^uBxaU$LkE`|Up+AQ1oQ;6EKe5#c`^ z{HKHeaPVJl{Erp<7YYB1g#XWx@Jk3Epww5ALi{_j4ZU8jibUMW)Ti)(7pA36Oij^- znlAH0{XBzlCG1L!2zez2PQ($Fe2ee87CqV1T;Q?xg_k$el#)J|%CIROKvW?*Wwi&>u*-1co_i*5_@St_?~YcsTvQ#f#hhtp$-oVJz-6&Opw2qqwH zV2q5D1$B7TU(kDfk#{O!nz}&|l_74e&XJySSe&!c(bUv5a~47_~4(N2@SCKU=fM zANNZ>Zm;XBaI;g~-S|{CWx9^fGp{^#YTPbhwb;{p<}0V1vz7kgoRFZP+TE>D{EzQV zf~QjSFEn^fX+<0ouaJNaz%An#R_;>E)s$#aGODHEu}GzDyO$CcqlTTl93X9xv}6bm z_^ULl_^C3sj+ruzkJ+}dOWPmK@gP)j}HU6#Lnl$?sqMt8l_E$~@i+}Laj;2VwK z&h4x5+xDSFL&{;ijsOK&a(#kZkFl(& z;_uMalgL*(vh{icf4Ns3jYh{gSd=}!-_^xa+NPnhzBsI~`vWIVpl%)-$JJ8rP-eS@ z9d{{OnC`p$5%~c-`GfX{fRt5nd}_?#XR^=6H^#Q$?ZsMg|K<8XQmjsMaP9V>6F&B` z-FWa*9XuhNJGHp@vb25QQRG&Kpyb2mm{RIuL;&?g--qwrK|w~8P&LWN-|9Fe?y0D% zno%@32z~(p0bNK-{@mPLUBgn@M6ZWl@m`Z$yt~%%vKW-A>X1)cbpe`)^CX4Kj1R8+ zE41!DDOB)<#2S8~fCXrsK6;2|gjC%UvUB$d8-BU!VZutfNC|Dgfz9}4r14#QJq0)fz1^hnRl zuRGp-6!Kyp5=%SyaXdXGMTauqFEu-t6w%YuBTHSbM-NaQF}mUScj}S&u-AsFE#%9( z7ha_p=38LCoX^0wzqvN(yCj@nb?jOM&j}^(`5h7^Ma@mu!h%oAx-|%3PCZjUb}LYTS$0qR^5u&kvi2Y%C@X6=MF{+#N@=N=3%Y|9 zk=(iRn+^C6@bCZc$rnx!ugx~e!ss6pQs~CWljKZp$1pZwgLygW`&S*zl&rO1Mn^Zy z-pwYpD%2huEOi=Pl7x>gdwUUa04rT&(~(lPnI4C(e|Ff(t2!m+R9@}a*2d66DY2QW z5M`>TXSOw_x#j~xE%Db@Y|Z@aFgS^r-v!`Q@0FDxW&Vm#}VgD?#3^ zmIy!l?H-7z?F?Crh8pN=q{Jld6DLsZ$yWfh*1UeMwgg1jGcUZjC{FF^;_2;i0dFSj zc`lY;R?|UX#dN^ON8V>*^x~x*hZ2*rv$N5i=~^HjRkWdfNc9r%K1&3_WQOFuA{JG$ zI9@ONFiy!+r%oxme%vg$xBKS~ADpm(WBA0j>;tfSpu?^k;QLq{I|1vm>i6PL39zPWN-Z)A0g4fE81L zOg8;jYpjyAKe-O%a8JL^9;3qPh8W6oy@M9}p;g@JtGH15-ms(vg$r}9SCyZy6vT&+GA*O*_v=zS^qp4`G#nyR+|-KRBA;!nJ^_2kspyYrh$L2+wD*U z1&iq{SRa4hO1ylQ9a)s3)w()C^EwM`%=_K%%fN&$ct)l7G?Y7!-}{s%MEsJJ7oIQ} zJ2)S|vSe|r6DgP3(#i-Pa#ey&$jGhetU^Klc<;HJ89hL$%D-I2)h0f2lojdg9GA!e z?I;iLctqN3Eyjp?Z%}b-b73}VfD=FpWwr3$T@DEzYA=VC!D~b1j|jO7gkEr3v@f+& zTzz6moSwOiF}kZ(X%3lbI-b7TMH^MxJLU9%9OI%l!}+Uy!$7k_LM7X7;2CVI8Vo@J zy@wIO6^sV%jm0Ik<$_U46=z#3oXrBwAgVOYLtjW@F@+VSHLO{%P@I0Ga`G8lcmaA2 zC;MntlGj}eJeO<)pHm_GgawyIqQp4^B>8HfrB6RHHyEwM&wjT)VtUyQl8hGwSp>qy z8f+R`5tt}CzqK;8rF;+CN(|o0{nF&M%LV>3P2|Z*oDXGXaKZ6W@Y7KXc7Ey->%l?r zAi|Xi24Mn)-`kT3`QSR$`(s5)20p#m6xCT_h;H5l;T(d+01S(E?eu0o?ofU+T}qge zf8jlICi(OLCWS}0JM_RaSa+BK6+P+bv>Q)*6;9qt&sEd$3KgnZxpga1N&1WTs)#p) z>h>eBc+v*O!{Ruu0IM8o|3FH{d^fc+8c&%MVpD=nXTu1qfjjOZa1XZ>4P@=G-C-xy z9X)O|KnaHu#Bl$U!yY>d5!C22cbc3tNmrOEDt6jJBYRcVo(A6v^71Ol2H^K zWnM}8TpMtcMh@pzrl#nCd=C-y zC{fdtw6rs}c6O;XHOf7?ru-}{5v@L#(zO#zCiD?&sZ~10VU^Q(qaUev)pQ=o==fV3 z$FGtxx;@#}boD>^1_*XL11QQ#hmvS0YJbw`F)gjZRLjvC%v^4%nw_1UK64(9@Qk3e zR+ik7r&Ff>z*M=dEzU0gHG&8GhY&AcwGRas67@xP zGUiLlR8ggjg$<6`Vs+Q3G8sd2I)IQ>ZBr^u2c3ZS?;wt~GpO@OoqBedn_V+nEIcU4 zI>}EO)F-6r)Cinbc-(jqhO|~yT2Ub{?Jz*e^Pp`>Fx`$%{H`=kv1<+H-)*3#WVpr@ z+X>o=Z?<)qm0#}Jnf;zJ1;J!{4AcY3d+m1@CS~W}*QZQw#0G5)n^(Ph^$OIx1fwcX z43EN(K2Awcm#O+r|G9$xNZqg2y8oN`t=CFovwY38>8VmvYI}(6!lxH0PBdkDI9W^8 zH#VMW+RtzgnU$5ryvd`Np&i~NwrvBI%qAu#k{7~c9c;oll`hTJjPC4ttg5Q^AW7DzwQOV8nok&nn)Yyqk$JoG zh57|acYNmq61bT?RyO3QFF#7YeW;@7ztjmyR&gGL!?iX*OA&}Q z^q2Vp-F21qs+INi>awbnu^vVEU_JHYFcdvGU{6!#QF?g&rFRsn{-8&ks;c77^z6IW zuKG-}Ss$j)1O3%mz;3%O;WZia&Zm92w$^EP=nQ5tNn6Al;+gO1%T&f(bg>ul_k5sg zXV;-8|H`~YlpMMpMU)G;Lk}k39KZ<>*lJFHXSbJkUp9ocT>>tmXY0B9s*gH8C+Y32 zMF;b!Ae2ex8zzj=r~(b-QfK~De)ufu0KT1+9g+wU7^(sk^1nH>ZuDgqSdGlu^mpH( zywqL_c0%&<$4DuAM{RpT9j1gobf&?NTI;?WPg0h)xA0qM#5t7GnqrXahLQ^v>l|>H zhT2bvZ=w)o3+3}HndUP85*6-*-^!qg+KThtg=5$B8Z3N(%#5HAQ3{zEVXgpj@;xQX zb(B6&hmd-_bP=qq_FOn8vq-*8zi=d9S)^yz`C5lwMNR{AZV;Ix;ke)S`gu5kxSGOo zUlm{s@H7*Ujl`A;0M*%YV+~D&($#Pj#9~~1SjD*5g#Amz+#QFFDKEvG2D7yasFt(n zA7PR(5V4Lopm1MNJd0+;Z{y@o&2soqTui+%Ue}7|2Y7{UDOb=XTY$`{QY!tRXWEF` z{w2lwZK3N(mh5ix7`aAJI22awa0tAqDT&q;T(HH#oiek!pr&(Url2wkUtYru^n{Rq z2|uE_H$wSTmWDxti%!jL%F7}Xmd6R2Kkov>nxZ<^qlo+#rgYO!!=w0Q)7Xo4W?f1} zDbrjtmvg+%lvGjG!xG)iV6!Ki9tC|5tfTe(ej(KzaT$DxsFs?}47 zeKr`jUs>Vj9@Oc+!gRg6QegW=1L^Vh>AF}rvdrC=GChMn2gEEAN~)sq`*@s4T=g5> z+!a3h+fjw6zB4L&7!GjVk84o>6wER9wmEjT z(=2D5{(|7x>G1W0j>-JPxJ#0rq0d!5NoOO1@7iCTzdj$&ID?7n!Tt4|QW@Pl_xizS zf8>t` z>ScWw#jOdyOetuuDNbmwc^o;C)q^9sBf5%D?+pA`Cjgh##05{)Q$ zp^>Hyu#Jts2SL{P4!LYh_pT|QX>Fy9+d-wiWmlIRZcyVX}S`X7TdA(ryQX3Yuf!CEko zg0=8qV>+d5-%ZkgFJKp!qOxdq~ zH7vznt9^g^-~qO`u;e*bu;l99AwOh$JJ@))buzTaq^p)+O?py3f2S){7&cz~ZGFYl zi{1ebs&>Ppy3P-!cP?0yme*J@Z5oqwE##o#wXP1w#@hHk)R<-3YE0zk3DG^%pj%>t zqATI6h~9}GN#^HIKt zjO|&tnQ#^9IhJ{LX)jPU))dzG2NP&7vq~km13MGrnTvhp_XBb^DCTs~3By3hxor2j ztIU1VglE|AqT9ip>AE=$(!-%Jx%Fte*kRU?*dM+oe|&=4)DaER3aFNHJGeJ_b2BPt zyDp4V;gJWFD3K=F;M1yZ(E0#~RI5%eudjjgz@^GwOv z(Rpg5LIqNF#OMq8{ouKaQBZhplrK&)KOWxZy(PoXJLq?@)y=ixQ{X@LRlf+?*Z9Hr z*x0^}=8`V1(?vTzxsu;o6;P;#4LVdXFjRWZRodNQ;=3qW_~{8PNe7D<%6EG`=S z=bwMlpVOs3_s+EXoOGVs)@yzf+N)n2nEtWByPf8gK1#0wX)>HUQ?K!8#*w7(HD00d z#E51s7)o6A@ad{DS2R;>Cfa2;2nAi|tzwjrvd2?3)d`f!v*|FN!eF~xv>2X^^<3b} zNb>T@-|6ltTVg$w3SOg&6b~!FX5H_De2K4%&QO`RsM|rV`E8xc4ck?4OonBij5WJ_ zGDL_eVy`Lgt@g=!^ZEIZ1aaP=ZRFhqSd)LlYU0Xvbkn+&GaM!VL%;EStCC!SnzSc6c5f=d*{z=noH8ErBKcIak@l*!4k=`3KNNa=NmdoGL_s8 z8r#%&w&>D?o9-6b$=QvLze`-r6cl2(8KR2Zv@<=w(EbEVi3+F{45p>bm4e zSPkt?uo^C+g(5;89XdikXQa)HrLUQvDprm9mPnx9rT*4ixQM9doOSyT8kdy{9w76$~w{-Bv%qY z7r5-ybe*5bY?*U!_%qPDVfriG!tEK5Om$e(^7OOP%7ub%2fQ42^niKsAK*mx^K+Ap z#}9ahT`sipDJ021RrE-kyLkx-OuR`Qrt6>$jwuXM&YzZJ{6>rVK)eKzV{(8o{iBQMe6l~IgfS7Pt-n#Vc@mCmB9LBz)bmAH++6FHLkzi z-*tYu&EB)(ynz=#)CrZVgRDZSWI+cuR53op$Bp)bATl!25_CAA%YGkFkiZ|e2W)>*i3oW1K?ouM{DhVtgj#z61x#Z#xAG4u0T zZgh*|m#Aw_L~CTBd__eiiIlWqG;V^e(Y@qsqu*lsy0lqX9A^yJROcD>xZrkhC<@Hi)OJ{#&~wc04Vy2_GhKiglQpze_SPY$XJIg@ z5@?;2p`CEz#aZo;CydO{XtFsghE3{Wb5KxF(nj4jso}7Adh`A=QMRUZ!uL?a@eK=- zc=`Q$J*|S-LnDj;yejVJIF-C9gGh^gh^hyl{`xw&YyC1R3ZK6_kgpE3YF6%ire+OU z!!BM;aR#EYoZ0mA{NE&>iPOC}4}9BTE2gMyCfVt@RxK5JRUQk>1v5PAP8U2ah*)P6I6 zUGo@>tPMB=OZY(G{G3C(srN5q{`Su^nSdjlNF07HzxQu>k8#wzgH6PDWU&AYYE?Kz zdU7n%T-`1qM~}(mnM3O=4EoAGJI4KKt8FI0W;Q7f-xmX+%K;83Fc{)-0NI9!?Q*v;mEwq`gbO`M15 zWV^7N9XrsKHu9Rz2yng_%=*i+RNq^0QvMMMX?(ilYeY-L!ssZ6VMSiL_8&R#$7TRq z!qdy0iIdPW%jj1IVWAj3qN*ur3>@#W<475c_|E4j&xzZ@`qE{6i_DFEzgdyX;3k^C%; z5+QJ66=7z!`#P4{Ky5Hxu){4~!R_G0UoJvos&=F?qc!q=*n*817%&=PNz(G^EyBT3 z(adkP|A_xuGIRYn8*{Mnx;BhZl_Rj`ktis_IzTx*^2LeT8bS8z0PQdyq#_I&xi3)q zs`{R{-WI$!pFcD-0Wfm`YX{FPIRm8`zdGO#6lO@JAc3OKhKK#AJloL9v7Q0;>+#!l zt|I3uL1%>7P~;PsRj8`wm=(%`stJ&8uQ90nD4zp%zNZcw(HVA|pxL{xFjj^d@d^-U zmK}e=ZBD>-xVeO&>oRmfzkiE(+Wx>^z zPnOjK=z=a57Q)sKee%YbZgGlYiNA*vu%VBF3-}0DQWf&%AGuqw+CLWC`7)jbY<>K?J3*tY^l$QGZ*yn7FKa4Lzy! zf_}WG@bNUVY@0}C8iw&h0cYI~E~76|+ZGBo_PV5c4lTppWyJ;Aa3VNm{A9tjUm?0l z#sq|@>VI5^POyGHQ_&5Ck)DzFIS*-}D>sjhCU%JGs_?z5a`JXfs^k$e(E*O+`0-7y z(D;|MY)Pl@Vf3AThj%Lbl=7);z1}5trW^nY>oFGYv=AJU)`|K=>7avn4+P>yjBsxk zo?spC|IEGw`0+a$8(cglTf?8;FO1R-&mJ};^(D4;ov(8!^I!0$eiJVt)YEKlz;?R` zS1Qh3$)BziP)UbSoul)&682F3AUR}=cfBdp-I<=JizNYz^$ze^6v-qb>16c0Q7!VU z0WRY6T~;_+N4|ko`Q(e;SzQi=o#W75D)~|XRtnxoX<|= zeDaJbq<#?OMTRY75U@C3d;mL0?(b&^s^@tA&_$lgtsjQ`6yo!R}bTj zqCW!>aV(GS_G`6*nS2jI3%3k7(q5=Kf@FfLqsWu6OncLZjgMUm4J3S&!2$anFzW4J z?AsbHdABp4LRS<5Rup!i`*KGaRAmS}zh)XiEEeuXI}X!LeEK1@oruS%P&R4RX%S~< zm=BTPL_2;s$_^aM=(KZKJ-QvQ{S&Mi z=%>4L5p6c4t1M>1eFO;gp>Af)1@o;1T`-Zl0(+>03aodv*&dM)^^BxHOxr8SkHa`+ zM80t7zzGtNa>%iw%C-ev&~0{sDRc8_UFTJz9vM|CE$%c{aZ0As8!kI=&@jIDi6SYP z`2*LtvKK7pVGV#+Fna^`nnpMHWP9Ae$;OPKPxI$)dDzo-YoO)Kx2Y; zv^2)YGxMO$6hO0}%zZ&!KldD5w5cFrbROWAanrvQXL$lpn@FGmmi&O!Gm~A~5g0vn zINBxi!#aP`oom5!*e=2Uz*f&zMhPB-=yWJV|MT&+yZN!afu+2mD?;!itHQk~lBEOa zs_oLdY7|LWs$|I*>V)P>p$6RQHc+12Y~M($tPka?ets6+XZRszrg9()VnIE?j~FJ( z7Q~AhCbE8)e6;o#{eUd+QZY{>^a&kNdYHA1O>jUy&zbCe;P*0Iv*$kk|M`!jpwv@+{ zWHVHTk;*c734980V|KRmBM7Xogk4Wg*>}=f}F9$k~D4F3NLXk!tB?tv<>5==}a$sW=m6j3%sKYc)@#>8`m!zc$ zIU&Z9XhIlJZZLBd(qd>CHI7QIJhz_9w9xzztMT z=H}+)ZOg4TavW`!7KEgQB12gO)xp)vw)ECLTgITh2imTnjiM5PTU6$n(gxj2jjCrL z^$2hu593qXT3FKDgK&B+@#d1-p2%w#qO?NKyPU;Ox^lpL>%Xp~l!lkg~AK1!VD3!Ur9Z zu=|36aM$x2-0V?73WL$c{{1Wq3i8k?U$5#o;52F^*$^&H z8tq`A&``liur2|_ne*{rP49`$MbO0zp?A4{!0qm>cBnlvvavQ&=HESIEr6`vAcF3j zX49#IfZ%`)h2RaJe|9;mO6C@&ksV zW4#hmPTa&onKN3<4A9xg=VGqzYE8n7|28ul4?$viM&EoG26cB)OLl=k%~nM_fU@1b zf2G+%8JGdEwpNJ$*Hu#LJKI13hp>8@?pBG~eIkYa*{GI-XADc9#+h5fd!DUL%MXH1 zxPTp~o#keemZ5t_*j<=8m|nWSH63&3?y*Wsxft+Bw%JU@39W!a;gOX-B^Xf7uw5K= zhhu>o&G!He8_zW6;mSO-6rgejmPZ@Ph;^byl-+))lrL+ap-9zL7Oz(NpuJb$o_j4P zMi(1S=i=cBiWkxibwbR=7eShjT!ny_ok*|f=|j2X=-HkwRnh?H$03{vTN$8D%}Q@m zPaKEs|H(Z2LaLv&M*LhEB(=3GpCY%0M^acN@)GQ{Dy=ZSnbo7&iOoJ41l&?7uzRB% zI3*|gY(&V+p_?!-n%D;~mkjy~HjlTsl1Ho7ie=E78IWb^O={yd2uw)ApMe*~J?Ca* zT`0Z)aPcMZbQ4dXBz{PQBF)IhMshv6HDsn>VaWunK2fy-+DGfzjHI8v>PB1Gyo?|N z(o1i|L^v=l>hW0eJ2Po8+W@BRfmJyeLrZ+!;Wcdrq7lfu0{Qk9rBdvQW-O_i^eWft z3?w*}RgiGJfx6xrCR1A|1ORMdLi^t+5ev`kumLF`B!RgHptS-{=H;GefW85_=-x)n zQvdFf15Rc9;IXc52Zx>pLM5GB^3(5E8#!F&9;$v!{?lf#3xp4Wd~<*b-t9K^RY5l0 za(nN1IdA2PHgoNS{VJ&8VGcbZgbD#bQbn8-dlCt%|L|8yrpLwo+%!S7IyG*)-YvT^ zNrt8li@WVrF?TMX)aH}rHWWA4OgNx&3g~1S>f3sML@;fRf%L-a(f__(?zFbkiJo3QS>ORRTo6@HK+}Sd z*n6bmok9rdBXJ3L3}3EaIYypjpHhd}Yk`h%oJ=GO4ZDGz{?WDdZi ziKEn>P+g--vi`F)@6(ex=QI<*Hw7Xk52{D-Em#~wQ0{>qW!LX2NblvTrSOtxYqBM! zQXeR@6JR~~?*ahnOKp}TKkAxWa=mz{69h&Lcu?&aZ}kgYZGq#V zsQVl5^yXVch>qF>vyh%?xcNO00TX3le9$&X)J_0ZdnD9f!&Y7PY)m%VD%ek!sVzPU z?UXs^>S0gK&XshW1s5m(D95C2n_S8rWU-F$F+2@kvy8`rR_EhAC!9Nd_$q~t)cA|w z=4Sy3RpE~_bJGyX$q&MnNOe$#TFZRNwzH=ZTUb_bv#Ri;nwpyBEnWu1Ye#(0I5;nV z9A_&0ib`{L1U8ZBCKxIp z_U0xh3m8zF%{!KnrGU04C-L?bundmtbs}WYnzCYOBgUC8z_VMgpuG=it6Xz5 z-YjpxSXpcEj4b-{s3@NYLpwmK@nAlpzTzSZPvDLrN*jN-0U#0rNvl!#d+1rUkKgu? z#8vnzyjC7oZPu<4)@k?jZdDeZR^D*3Lw~6PK@$)<%6T0Kvz~hZoywrCWNmD5$fyoc zYCj@29bIP{p+;m-TnCk!H?8O@=5`FBw7gFOL4oR@>U)T1L$}xaV`X~=xB^EMfsbIB z?wCj_L;P_{+YGX`8UQXhj3*<6@BVKoDJkOVI0W{V3`!L{0e=cLUv!2{C>T@~Iyav*Sh%^{{8G#`g6b*nj)QL5nK&BsmL$5$> zmFfL^C9E=_wsC9XG&ndjfP?^OCMur=w--oXH=6(y^JGDHdG<863UnW^iHF55T-ZX* z{)T9D6;wZ-p^b3tc-zmKV&5?V?ln|`fE#%Y#3URDs3byoT){l+Ar!T%15>r?YF6tm z=ypQIVtR4yBR66?_@7|YKjl~JZdtX_-AmfKn~}sY?K7q0u4Y#cwLXj#m&NT+)9*jY zmQ+f)vBMcxrR}E=eQ5d{bbmuL#5GJ)J3)d7D|h>jnR-%~GE*J}t`=C%@?hh?L+Z4@ zJqzGGt(jIxAZ|QDht(pO-kF2ZbnPy)I3XrjaNLr*=7S~-zt9r`fb{wTXl?tUM8vas zG8oG!kv|#6rfv^`LL4!XIhGod0&cUYzY2rUMd)D9c*C$2@_(o^5Zr)}$uq0q^RZ9@ zX%T^HeydyXzUI_8p{1|N(Hi8EB>CUrL141=lhBZmni%;#h>OEPEDf2gSU%7{I6IjW z!=g3k4#+AtD5%VL>?V&d$;hn6^YY@(XD`W2)KzEzVJL2?lW-uixS~=#UHj({MFMec z(sUK?N1p8+38VK$u>2UZt4EQ8{IMs2M?%<=jE@Tv4sVYc+sO1I;7Oe~XyITjz?%=V zH&yOJ#G6=!X2CMKFK#sStvtCV=|0}*xxo>QZ{cPU8qiK_-O~tN3z_au?gH9+tU%D= z=Ixw}R&8n;**>J=-nTOFW@D??l3y(~kRY_%c}=(6*BeAK(4&P)je(CR5z`<4`aKOC z)?;}rz>p(b~CtCSC(ht+s_Q(7uP>I}Nfkr5U<@$x*eJ^I}`y z4M7sr+rzXC^BOfl!;}%mhH`EYGUON3#587{t{@KR~p4d z!=Du^fNKK6fRZJ*xh7TfY{x|qTFwP+E0;?Uw8WqSi5PAN8g2|$5T*GyN;TgB*@vUe z3|Bu0G7kgRA5%j@%@2FZK8xVu1Jmm9b37MiaSL*<U2V}4A02HzR1aJ}KXTR=oLMt})gRUs!rx1S3E~(#}lMx~|d$ zZa;HN2xxT>dzKcVYb>9`IHg3`6)y^_Nnb4ud?(U(g?luNk{!D zA(fFi(APeZG|XbU>uv}Qw67s+PZPBHfE7SD`shx%-|~Q!DG>;@u#KgDM1YC(O5snvqY$e~GcPSIvj=Z)I+DG+Zaaj}(O{+Bm{TZ~5{kDbOtOx>>?_R)Fm?DV* zi{zM#tAV5?@J+_iE${#P`SX{*te{?dMfYX$8{q!e)S1FLYO3Tcwg=)srYlpcv{d4> zq_wO{`rgv@rF!s(fKdVPX24zEK^R|TU{GN`d+S_vZj!jLa43@cC7MK;kpfq#nUknW zE|a&Y*_PBa0PzxgW5uxV9bdm5v)rm4jR4mW1{a^*GQ55JsEff%wYv^pfbbmy z-C&g}h#P$^9KktRYQI+to9NQ1wQ>~Y3tLAn5 z2=DRSI2^vFWdWpSf&7=voE(SdtQ(%TYQj9QeP)&^(j216QV{O{WZ)dBeohJmt)~az z;-eQ&KjxH)3~#VrU@K_#V5_9v{BHm(t8cgBY)ndp^UTW=<$q5${Plgu;iCO%(8Wje zS#}LO(sLUJS)Shft@;Ou#?x$?>4ZNuE}*Bj{K$mT^|!4I)6kUs-yE^T1o}~+7pK-8 z@&VxcYSHBqBIvzm79y~G&0x&coGTofK4qHnJ#8k}ST9p2&X~vR-0qv9%{*sbaqA2EJ z9_$C1%^-ktFQVTOA2U|)-roRR|5J=j8ss9b#otWWx6J-)5Dt)VoP8Hlm?H8}Jc-ZY z@IuExAK#R`7|7g0Wl5obtZcfx;W&x0}YBI}SlS9k(!ZD`7vv z>K)V+4{Lal@@P2E#(`q{+r4`bwttes@|+@+S#z#v#OIa8pq^)f1x|WCt_jsIL1=8; z17=3*ZaX=gG7lgZVOvmOl2V~TeaJRQa?m|-*1m!Vd!if{!pcC79RQO`K#%nd8NlSm z^#%r%Ibom%RO_rr0M)hq>}dfF_+H09gtQzr^s)oG)Z1L{T@_>06HWAt$Fbk{HadJIvx7o#*r{C+M3HM+EdLQC|KDaIc%14&L z+FEhY6HDxpQ#k?}8~@wyGguh~Y{4nxrC;P|8&Q3D0kiX^7?PJSZawj}t{aVF<6U#c zz1CR**tg+V``(hVGP(~r#n1q7C<6#A|is(*-80iQCxelE8S;UDGfKD}#d-TAGMPMHP z^Te^hp-_%c9IRo7+yS zc@h|*PB&uO<8JYTf^TCXeLtv+eY;mpXL$tbf5r9Q2Y}>3m%+i*N)3c;$eoItV0U7A z2SyM^MwVO;JYK1HRz%qhPV70jeC=0<3OI2wM`E%os-bG&hpfvy3W|OmfRq4dJZ-3N zcD#GF5H&xc+fn3bShO^MJz*a-9{m?xlt9o;L<3#lDqq3bfa^L?PY}8U$_~f^m%04K zEW^91s#h){OQqzQ6F{Tm^sknpq=75YU<&l3%z4J7$iX(^niIGiv=0%}JAxv=%Tw>Rv+|%gwbiTC08k@Th}Y3i@-xfp?}5JB`1< zV+OiAg?2PI4ogs!H{oUmnQ9E&3-<Ec93K71?LqGL+IY{A!S4aLk$oB>(ocXt@H{xnfX_p(`x0w&BLJqF3jqW zRnWm(ED`{N{!r`dZ9@xj#@s1=vW@teKfT{r3+ehDYvsM78~-T{b)0(+}L8v}>} z(?Svd@u7!9N|&<$J6JSiCPdW^C&)8;M*kwUEV9K&QEsv)iWtdqEyY0(NL>P2U5Ibe zEx>#V{IP7HJPJwRUdw1@aQro~PelOYUAdh@4>f6*g^mP-M#kYcX>%{o$OJN zQ}mT?UY|Xk{D17Zu9aYU;%@PGNyjhmZ0zhKdb9#bvuSparlvLO)!EhD{M5q4L~$)p z!5q~J!Zq`+BFnI`v#A4sG@I+PTkE)j+fHk;1J0105PR*L-_r7{D-Y+bh%hVqfA*FW zFKK1*4<+IMW@T);&l+g;=J+YSWPJLJn3O{OyjW$D&F|u(?@g~`_lP#{QSf`?`cUCe zQS*t3fSU%(R4 z0)Rd6TbfzPO$q?|G@<2v%VbyAG|+V%Greyc;gzXYsyLI5GoAD1^1V(wEyyengm z`?&i?_AB!E3v}_ri2aDV3&pEjS{PEz+&GarDQtV4)~fNS+Yt<9aup>x zp$R?}=&9UcNm&V=CwuE9m7U>Z*R53SP62M0Kg!2Q>wP2ZI-1(T$M_X|;qs@#YN|>` zogY9j`BZ`9oaYY#g2yDSi-KxZxIVV0r>}Z_pl|Dg7G^m+aJ&zhV6%2T68f7ZKarAV zxOhR#aYfO2!=mo5F4--+487dLX+l)3Oe9YgK!12hA05>uI#Z4TC)=cxlMrsnzxt9>1 z?~XxGyRWGLvPiJ}mjwu@1-rAhJR2(kxX=Qv)lJ5DPhZj%)&V6ZvaWwY>C#5r0}ME+#H6VqnAdR#^9wW&F(6QgK0M zgPqiisW>HRGwEMc+U78~%AM-bw$}GP_t=x2dW_Y#GMOZB#p0B$c!^rMN!T6hlOeCJ zzN|c`J1e4^X%d98KKZ5lYR)5b)?)I^R@PWSW(;R`1Ae+oZ_LyFLVByV%(m{g6!1OD zpNRFpY5L}}`EiDb->TWA;)m?}1f)IN?Ua@sy994WYtyKsJ9@Y8E zTO?5_VC<3g(O#5u(Jscp*ISY^DJj-rEj1GQwyLI`-5LT>_d^c^YIlIZ5mTS>>5r_C zo1*6NbRRN9=}o1hXNO$z?JJ|5x{eWFuQG`l%uXFOEY(kG@8|n&D+yFzqzbY}^HqU7 zpmf(;iJ8Rl&*ZIxq?zE?O>1TdM0PM62s7C34|~d{CWTXhv)p{O#>peg63EOG@%R9k zjxO&hb}v#Zzk_^I)>r(#!PT00RdzAxPVv!&hLRYbI0%97!nKIy9RuSbU~TVni9B(( zYpL&>jlka}pvRHddmm@s2^s-dEYA?)LpsZYWH&z1Q2>X6)1~Rfmv44|`gC(VWBhRe z1fd=8OcNtnA5VE((xK;`WX16ScDrId}FHePh`dWp{EINv&J%o!|3Nw|ZMO9CB(<8$(8ZY(Vc7{@P?C z(X+%1l>64zLjI?rD*tXE!cdl*df9z(1_!Bse&(~RyaZ4EKM@Eae)GPA#>)#&&)uCf z)r!v6Q6K*Y_IRpqS7|(7Tu5++)8*>-A!Y6lFQf}mZ_hgmA@+j{Q_5)uHxL)p}K(-aRKotIfpA%_OeclyDGe*BP@JcGUoV2xOKSl0r6XZ)WVIoXrJdE2p*93hT{2$-Z z$=5#*;$PzQa3^mBGmh|vwqg^nC_ex(EQCRIri+xMo8wv3^ybpcxl)bH!ZObznDVbI z-R9h>?fop@Z%TeKe;Q;H3`Ip;H_Ewct;k5xoSA^ulk{_({Gh~vcuzG32tRD(D2y## z9N<*g_C|HC+49Iq@Ms*s5t}DAVR+Z6^$d)h_&%#QsL7Euo(F${2V+&MS)RDDrZ2kPhUdMQasd9D|sQ!F~7iH3?E_r3VffWLF{59-p6)ESx= zUtX-5YlXf51pXWyerdPWdK_F{Yx7znESO7TD%@{O0i_Ehfwk#oaqGvf;PvtpX0Xiv^;`qPXyV{_p&MX}3cCpi1pcX=_4P}`Y zR~MK{vs95}M&o3yR<@uEf;AMG(ux=q<%a$XewalU-S$w2@%>5P>9=5|}Lr zx&na&Z!`f>LK3d|x_o^#&koOjMW&#kq+^uI81Y?0tl z%grg-8jd@NAy$K=Jx|pN}UB_;y3QT;J7&wp} z@_t1cETDts%JDaaTO^9Xr!CE{zB5?`+W&4z@XSP28w*a6t5-?{2!dcw=#)TzfA_N8 z?PucJd()tV2o;RDck_4QX1-Fxuc@x7=0ofi*@azuGOun|Y)vfr@QGNm1u4u*yt+-X z^+*iF6Iu#8Wn%1LWIcU%z;`*XtUx{*ga0F7A@FaTltP)oQuA~KODD7yKuUNwz3$J< zukPJ%cqb-iv%h~%PEPjXM9Xwv{uS^(LjU(sF`SUG`ugIiurQ%rIMqeA1`Jj}?2B*o z+uU{JI`;hsB^jHDQ|BfwE>BwqUK{M}c1wbLO5>g*?8MWLmpW-9X0CB@d)G%p`(Y9f z_f?lx*OgWW)s*nR*+(;cCOXC6laX>(oK<4$yz!y&m{R=5b#LOKzjkR(7p8kl&%nRB z#DxRu!>}l3m#2GKOd74vwNWBC zK^M%pUc;|CPZT%Cw~R)!HxQDHKqgtZU+$VrWW2#btQV5xTuy0Oxx&PK9EDB`NWK4- zya2asztfT+h-UwekYw`!9k?8v_4r>>ZTLUI=J@y;CrKrHIiW$MRZHY4aSN|*`Cq==_nMlTB4+fS z8J$5p$ZP^3>4RO-H_FD!=8KH69LK?fPzNYqZW+(-EW9#HcE1!)(S=w2nBxKm5cempvZU3A-g-r2F^6(&cY z($L%-1Jo_oRg(d7^rog*@vsN|mgu(>Ua4vs)L(m}Nlhgb`T{u3UA`VB(!SdsV%;hc)*oA=|_=@usZN zeUCQY(`bSl8cv{P(1{81m7x_9$4tAi_vw7}zT3yj8Toc7eBmsfK*CzI`E;>TYFyBc z`OXG-if_`z46W3}W)@mPgMl|EvsL;wvL>`EMn^}ZWQgiqrjF`duq)`)XE)#Dte!nJ z-4;SwTN9N5mu-2s)27y?XjXF_lML5n)N$CzfS-=w8>W$Oq|+ExYG_=Lu}p|sE0AFd zsD;}Rw(eqZpMK~1<%VHWnL&MPm<_MPj2C@}`um;GPx`IMS%a$tv+$gUX|xX7fvZYN z=eCuHP9ISKFZ2(`bDbk;&Q&Y=h~d7g?KCfBq(NII>NN4Fh;@uL)N@X+Wow+LcQ5y| zUgsXdB_c$SU_C}OT&5oy9x|TXYo~D2Ek~8D53;i?r%Zgi+^r0h++fHlr@PUj?vwf> zsLAD&gEFXVKZ%B8?X#Oqg~PqtrYg;=w`L1-`V3V_p!}JUQC3+=_Gn$^XpE#T=nutF z+3Kgo#pKN7v`;_XO371xDYV`mCb9oSay|Mndjyz$!MKmRokXS}j`QsP#@H~ActnB6 zzDlZnDf}9vn8UUYF&qlUkf?AaE>I$!4`=f6bI>}Rp%b!=NBd+S|5#LAUthXjL-F%5 zQG;&T9=tx;3VRUS1#DTu+bYYoE7Q``@h)9$ZKi}zCvoS(WHw*!@?TFq=pi3p`6&^! zjg!stoL2PiUaLMdG!$j=?DlMI`7$=w>>4R4_(gTxT@XpcegI);k5V2%vHXzH+AlT9 z)^Lk{iQXRPP)>HLv{sySI0nh5cXWWo4jh=}fCO`ce<`gG;!|C^NCkYhUU#pjiJUyu zF{m^-SHE6|EzPu}D6A^PWn+}b81vX-2&X`5>uF@pvt}NDgKb9w+uPgy&Hf^{gBbB2 z%`Z4bJ$j%qzJ_V@BeczCO}E{i2?aIwf6H2&(A^=eBUDPscSs(^f-{!vCE!h3Fn&EU z%k#e<9UsSG6@X>(@xnrF>~jS12GG;HskQ+~H1M~5r&_uMI|IPrJt^%zg!2`tpEQxq ztCQ8&8x`X?pIra^4l0${PW*)EX1+{tq=#3$uMk$^x59WKX#jsZjDP$c4`bdVuMWI^ x;9Uz|Jn$l+7YV&c=tV*=5_*yF|3bn+_j