From c7e7e75f3861793d3fcf39f13041df9aaf5fdcb6 Mon Sep 17 00:00:00 2001 From: Justin Kim Date: Fri, 31 Jan 2025 17:13:35 -0800 Subject: [PATCH] refactor all tests to be consistent across different source codes Signed-off-by: Justin Kim --- cypress.config.ts | 1 + .../apps/query_enhancements/a_check.spec.js | 93 +++-- .../dataset_selector.spec.js | 41 +- .../field_display_filtering.spec.js | 391 +++++++++--------- .../language_specific_display.spec.js | 27 +- .../apps/query_enhancements/queries.spec.js | 249 +++++------ .../query_enhancements/s3_dataset.spec.js | 225 +++++----- .../query_enhancements/saved_queries.spec.js | 38 +- .../query_enhancements/saved_search.spec.js | 41 +- .../saved_search_in_dashboards.spec.js | 31 +- .../query_enhancements/shared_links.spec.js | 34 +- .../apps/query_enhancements/sidebar.spec.js | 28 +- .../simple_dataset_selector.spec.js | 56 ++- .../time_range_selection.spec.js | 37 +- cypress/utils/apps/data_explorer/commands.js | 59 --- cypress/utils/apps/data_explorer/index.d.ts | 5 - .../utils/apps/query_enhancements/commands.js | 237 ++++++----- .../utils/apps/query_enhancements/index.d.ts | 37 +- .../utils/apps/query_enhancements/shared.js | 8 - cypress/utils/apps/workspace/commands.js | 6 +- cypress/utils/commands.js | 298 +------------ cypress/utils/helpers.js | 59 +-- cypress/utils/index.d.ts | 167 ++------ 23 files changed, 911 insertions(+), 1257 deletions(-) diff --git a/cypress.config.ts b/cypress.config.ts index 0e08a3bc06da..8de7f096511c 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -39,6 +39,7 @@ module.exports = defineConfig({ ML_COMMONS_DASHBOARDS_ENABLED: true, WAIT_FOR_LOADER_BUFFER_MS: 0, DISABLE_LOCAL_CLUSTER: false, + SOURCE_CODE: 'osd', }, e2e: { baseUrl: 'http://localhost:5601', diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/a_check.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/a_check.spec.js index 314cd9aa1af4..2de774f905ea 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/a_check.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/a_check.spec.js @@ -3,53 +3,66 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { DATASOURCE_NAME } from '../../../../../utils/apps/constants'; -import { SECONDARY_ENGINE } from '../../../../../utils/constants'; +import { + DATASOURCE_NAME, + INDEX_WITH_TIME_1, + INDEX_WITH_TIME_2, +} from '../../../../../utils/apps/constants'; +import { PATHS } from '../../../../../utils/constants'; import { getRandomizedWorkspaceName } from '../../../../../utils/apps/query_enhancements/shared'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspace = getRandomizedWorkspaceName(); -describe('No Index Pattern Check Test', () => { - before(() => { - // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, - [ - 'cypress/fixtures/query_enhancements/data_logs_1/data_logs_small_time_1.mapping.json', - 'cypress/fixtures/query_enhancements/data_logs_2/data_logs_small_time_2.mapping.json', - ], - [ - 'cypress/fixtures/query_enhancements/data_logs_1/data_logs_small_time_1.data.ndjson', - 'cypress/fixtures/query_enhancements/data_logs_2/data_logs_small_time_2.data.ndjson', - ] - ); +const noIndexPatternTestSuite = () => { + describe('No Index Pattern Check Test', () => { + beforeEach(() => { + // Load test data + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, + [ + `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, + `cypress/fixtures/query_enhancements/data_logs_2/${INDEX_WITH_TIME_2}.mapping.json`, + ], + [ + `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.data.ndjson`, + `cypress/fixtures/query_enhancements/data_logs_2/${INDEX_WITH_TIME_2}.data.ndjson`, + ] + ); - // Add data source - cy.addDataSource({ - name: `${DATASOURCE_NAME}`, - url: `${SECONDARY_ENGINE.url}`, - authType: 'no_auth', + // Add data source + cy.osd.addDataSource({ + name: `${DATASOURCE_NAME}`, + url: `${PATHS.SECONDARY_ENGINE}`, + authType: 'no_auth', + }); + // Create workspace + cy.deleteAllWorkspaces(); + cy.visit('/app/home'); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspace); + cy.wait(2000); }); - // Create workspace - cy.deleteWorkspaceByName(workspace); - cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspace); - cy.wait(2000); - }); - after(() => { - cy.deleteWorkspaceByName(`${workspace}`); - cy.deleteDataSourceByName(`${DATASOURCE_NAME}`); - // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteIndex('data_logs_small_time_1'); - cy.deleteIndex('data_logs_small_time_2'); - }); + afterEach(() => { + cy.deleteWorkspaceByName(`${workspace}`); + cy.osd.deleteDataSourceByName(`${DATASOURCE_NAME}`); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITH_TIME_2); + }); - describe('empty state', () => { - it('no index pattern', function () { - // Go to the Discover page - cy.waitForLoader(true); - cy.getElementByTestId('discoverNoIndexPatterns'); + describe('empty state', () => { + it('no index pattern', function () { + // Go to the Discover page + cy.navigateToWorkSpaceSpecificPage({ + workspaceName: workspace, + page: 'discover', + isEnhancement: true, + }); + cy.waitForLoader(true); + cy.getElementByTestId('discoverNoIndexPatterns').should('be.visible'); + }); }); }); -}); +}; + +prepareTestSuite('a_check', noIndexPatternTestSuite); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/dataset_selector.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/dataset_selector.spec.js index 5bacbdb9ab1b..4f2a608d662c 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/dataset_selector.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/dataset_selector.spec.js @@ -4,16 +4,16 @@ */ import { + DATASOURCE_NAME, INDEX_PATTERN_WITH_TIME, INDEX_WITH_TIME_1, INDEX_WITH_TIME_2, - SECONDARY_ENGINE, + PATHS, } from '../../../../../utils/constants'; import { generateAllTestConfigurations, getRandomizedWorkspaceName, - getRandomizedDatasourceName, setDatePickerDatesAndSearchIfRelevant, getDefaultQuery, } from '../../../../../utils/apps/query_enhancements/shared'; @@ -25,16 +25,16 @@ import { verifyBaseState, setUpBaseState, } from '../../../../../utils/apps/query_enhancements/dataset_selector'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const dataSourceName = getRandomizedDatasourceName(); export const runDatasetSelectorTests = () => { describe('dataset selector', { scrollBehavior: false }, () => { beforeEach(() => { // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [ `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, `cypress/fixtures/query_enhancements/data_logs_2/${INDEX_WITH_TIME_2}.mapping.json`, @@ -45,31 +45,30 @@ export const runDatasetSelectorTests = () => { ] ); // Add data source - cy.addDataSource({ - name: dataSourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); // Create workspace - cy.deleteWorkspaceByName(workspaceName); + cy.deleteAllWorkspaces(); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(dataSourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), timefieldName: 'timestamp', - dataSource: dataSourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); }); afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteDataSourceByName(dataSourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); - cy.deleteIndex(INDEX_WITH_TIME_2); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITH_TIME_2); }); generateAllTestConfigurations(generateDatasetSelectorTestConfiguration).forEach((config) => { @@ -81,9 +80,9 @@ export const runDatasetSelectorTests = () => { }); if (config.datasetType === 'INDEX_PATTERN') { - cy.setIndexPatternFromAdvancedSelector(config.dataset, dataSourceName, config.language); + cy.setIndexPatternFromAdvancedSelector(config.dataset, DATASOURCE_NAME, config.language); } else { - cy.setIndexAsDataset(config.dataset, dataSourceName, config.language); + cy.setIndexAsDataset(config.dataset, DATASOURCE_NAME, config.language); } setDatePickerDatesAndSearchIfRelevant(config.language); @@ -106,7 +105,7 @@ export const runDatasetSelectorTests = () => { }); // Setup the base state - setUpBaseState(INDEX_PATTERN_WITH_TIME, dataSourceName); + setUpBaseState(INDEX_PATTERN_WITH_TIME, DATASOURCE_NAME); // Verify if the base state is setup properly verifyBaseState(INDEX_PATTERN_WITH_TIME); @@ -115,14 +114,14 @@ export const runDatasetSelectorTests = () => { if (config.datasetType === 'INDEX_PATTERN') { cy.setIndexPatternFromAdvancedSelector( config.dataset, - dataSourceName, + DATASOURCE_NAME, config.language, 'cancel' ); } else { cy.setIndexAsDataset( config.dataset, - dataSourceName, + DATASOURCE_NAME, config.language, 'timestamp', 'cancel' @@ -136,4 +135,4 @@ export const runDatasetSelectorTests = () => { }); }; -runDatasetSelectorTests(); +prepareTestSuite('Dataset Selector', runDatasetSelectorTests); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/field_display_filtering.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/field_display_filtering.spec.js index 54f2c6006c1a..37dec8c308d0 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/field_display_filtering.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/field_display_filtering.spec.js @@ -9,217 +9,222 @@ import { INDEX_WITH_TIME_1, } from '../../../../../utils/apps/constants'; import * as fieldFiltering from '../../../../../utils/apps/query_enhancements/field_display_filtering.js'; -import { SECONDARY_ENGINE, BASE_PATH } from '../../../../../utils/constants'; +import { PATHS, BASE_PATH } from '../../../../../utils/constants'; import { NEW_SEARCH_BUTTON } from '../../../../../utils/dashboards/data_explorer/elements.js'; import { generateAllTestConfigurations, getRandomizedWorkspaceName, setDatePickerDatesAndSearchIfRelevant, } from '../../../../../utils/apps/query_enhancements/shared'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspace = getRandomizedWorkspaceName(); -describe('filter for value spec', () => { - beforeEach(() => { - // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, - ['cypress/fixtures/query_enhancements/data_logs_1/data_logs_small_time_1.mapping.json'], - ['cypress/fixtures/query_enhancements/data_logs_1/data_logs_small_time_1.data.ndjson'] - ); - - // Add data source - cy.addDataSource({ - name: DATASOURCE_NAME, - url: `${SECONDARY_ENGINE.url}`, - authType: 'no_auth', - }); - // Create workspace - cy.deleteWorkspaceByName(workspace); - cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspace); - cy.wait(2000); - cy.createWorkspaceIndexPatterns({ - workspaceName: workspace, - indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), - timefieldName: 'timestamp', - indexPatternHasTimefield: true, - dataSource: DATASOURCE_NAME, - isEnhancement: true, - }); +const fieldDisplayFilteringTestSuite = () => { + describe('filter for value spec', () => { + beforeEach(() => { + // Load test data + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, + ['cypress/fixtures/query_enhancements/data_logs_1/data_logs_small_time_1.mapping.json'], + ['cypress/fixtures/query_enhancements/data_logs_1/data_logs_small_time_1.data.ndjson'] + ); + + // Add data source + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: `${PATHS.SECONDARY_ENGINE}`, + authType: 'no_auth', + }); + // Create workspace + cy.deleteAllWorkspaces(); + cy.visit('/app/home'); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspace); + cy.wait(2000); + cy.createWorkspaceIndexPatterns({ + workspaceName: workspace, + indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), + timefieldName: 'timestamp', + indexPatternHasTimefield: true, + dataSource: DATASOURCE_NAME, + isEnhancement: true, + }); - cy.navigateToWorkSpaceSpecificPage({ - url: BASE_PATH, - workspaceName: workspace, - page: 'discover', - isEnhancement: true, + cy.navigateToWorkSpaceSpecificPage({ + url: BASE_PATH, + workspaceName: workspace, + page: 'discover', + isEnhancement: true, + }); + cy.getElementByTestId(NEW_SEARCH_BUTTON).click(); }); - cy.getElementByTestId(NEW_SEARCH_BUTTON).click(); - }); - - afterEach(() => { - cy.deleteWorkspaceByName(workspace); - cy.deleteDataSourceByName(DATASOURCE_NAME); - // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteIndex(INDEX_WITH_TIME_1); - }); - - generateAllTestConfigurations( - fieldFiltering.generateFieldDisplayFilteringTestConfiguration - ).forEach((config) => { - it(`filter actions in table field for ${config.testName}`, () => { - cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); - cy.setQueryLanguage(config.language); - setDatePickerDatesAndSearchIfRelevant(config.language); - cy.getElementByTestId('docTable').get('tbody tr').should('have.length.above', 3); // To ensure it waits until a full table is loaded into the DOM, instead of a bug where table only has 1 hit. + afterEach(() => { + cy.deleteWorkspaceByName(workspace); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + // TODO: Modify deleteIndex to handle an array of index and remove hard code + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + }); - const shouldText = config.isFilterButtonsEnabled ? 'exist' : 'not.exist'; - fieldFiltering.getDocTableField(0, 0).within(() => { - cy.getElementByTestId('filterForValue').should(shouldText); - cy.getElementByTestId('filterOutValue').should(shouldText); - }); + generateAllTestConfigurations( + fieldFiltering.generateFieldDisplayFilteringTestConfiguration + ).forEach((config) => { + it(`filter actions in table field for ${config.testName}`, () => { + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); + cy.setQueryLanguage(config.language); + setDatePickerDatesAndSearchIfRelevant(config.language); - if (config.isFilterButtonsEnabled) { - fieldFiltering.verifyDocTableFilterAction(0, 'filterForValue', '10,000', '1', true); - fieldFiltering.verifyDocTableFilterAction(0, 'filterOutValue', '10,000', '9,999', false); - } - }); + cy.getElementByTestId('docTable').get('tbody tr').should('have.length.above', 3); // To ensure it waits until a full table is loaded into the DOM, instead of a bug where table only has 1 hit. - it(`filter actions in expanded table for ${config.testName}`, () => { - // Check if the first expanded Doc Table Field's first row's Filter For, Filter Out and Exists Filter buttons are disabled. - const verifyFirstExpandedFieldFilterForFilterOutFilterExistsButtons = () => { - const shouldText = config.isFilterButtonsEnabled ? 'be.enabled' : 'be.disabled'; - fieldFiltering.getExpandedDocTableRow(0, 0).within(() => { - cy.getElementByTestId('addInclusiveFilterButton').should(shouldText); - cy.getElementByTestId('removeInclusiveFilterButton').should(shouldText); - cy.getElementByTestId('addExistsFilterButton').should(shouldText); + const shouldText = config.isFilterButtonsEnabled ? 'exist' : 'not.exist'; + fieldFiltering.getDocTableField(0, 0).within(() => { + cy.getElementByTestId('filterForValue').should(shouldText); + cy.getElementByTestId('filterOutValue').should(shouldText); }); - }; - - /** - * Check the Filter For or Out buttons in the expandedDocumentRowNumberth field in the expanded Document filters the correct value. - * @param {string} filterButton For or Out - * @param {number} docTableRowNumber Integer starts from 0 for the first row - * @param {number} expandedDocumentRowNumber Integer starts from 0 for the first row - * @param {string} expectedQueryHitsWithoutFilter expected number of hits in string after the filter is removed Note you should add commas when necessary e.g. 9,999 - * @param {string} expectedQueryHitsAfterFilterApplied expected number of hits in string after the filter is applied. Note you should add commas when necessary e.g. 9,999 - * @example verifyDocTableFirstExpandedFieldFirstRowFilterForButtonFiltersCorrectField('for', 0, 0, '10,000', '1'); - */ - const verifyDocTableFirstExpandedFieldFirstRowFilterForOutButtonFiltersCorrectField = ( - filterButton, - docTableRowNumber, - expandedDocumentRowNumber, - expectedQueryHitsWithoutFilter, - expectedQueryHitsAfterFilterApplied - ) => { - if (filterButton !== 'for' || filterButton !== 'out') { - cy.log('Filter button must be for or or.'); - return; + + if (config.isFilterButtonsEnabled) { + fieldFiltering.verifyDocTableFilterAction(0, 'filterForValue', '10,000', '1', true); + fieldFiltering.verifyDocTableFilterAction(0, 'filterOutValue', '10,000', '9,999', false); } + }); - const filterButtonElement = - filterButton === 'for' ? 'addInclusiveFilterButton' : 'removeInclusiveFilterButton'; - const shouldText = filterButton === 'for' ? 'have.text' : 'not.have.text'; - - fieldFiltering - .getExpandedDocTableRowValue(docTableRowNumber, expandedDocumentRowNumber) - .then(($expandedDocumentRowValue) => { - const filterFieldText = $expandedDocumentRowValue.text(); - fieldFiltering - .getExpandedDocTableRow(docTableRowNumber, expandedDocumentRowNumber) - .within(() => { - cy.getElementByTestId(filterButtonElement).click(); - }); - // Verify pill text - cy.getElementByTestId('globalFilterLabelValue').should('have.text', filterFieldText); - cy.getElementByTestId('discoverQueryHits').should( - 'have.text', - expectedQueryHitsAfterFilterApplied - ); // checkQueryHitText must be in front of checking first line text to give time for DocTable to update. - fieldFiltering - .getExpandedDocTableRowValue(docTableRowNumber, expandedDocumentRowNumber) - .should(shouldText, filterFieldText); + it(`filter actions in expanded table for ${config.testName}`, () => { + // Check if the first expanded Doc Table Field's first row's Filter For, Filter Out and Exists Filter buttons are disabled. + const verifyFirstExpandedFieldFilterForFilterOutFilterExistsButtons = () => { + const shouldText = config.isFilterButtonsEnabled ? 'be.enabled' : 'be.disabled'; + fieldFiltering.getExpandedDocTableRow(0, 0).within(() => { + cy.getElementByTestId('addInclusiveFilterButton').should(shouldText); + cy.getElementByTestId('removeInclusiveFilterButton').should(shouldText); + cy.getElementByTestId('addExistsFilterButton').should(shouldText); }); - cy.getElementByTestId('globalFilterBar').find('[aria-label="Delete"]').click(); - cy.getElementByTestId('discoverQueryHits').should( - 'have.text', - expectedQueryHitsWithoutFilter - ); - }; - - /** - * Check the first expanded Doc Table Field's first row's Exists Filter button filters the correct Field. - * @param {number} docTableRowNumber Integer starts from 0 for the first row - * @param {number} expandedDocumentRowNumber Integer starts from 0 for the first row - * @param {string} expectedQueryHitsWithoutFilter expected number of hits in string after the filter is removed Note you should add commas when necessary e.g. 9,999 - * @param {string} expectedQueryHitsAfterFilterApplied expected number of hits in string after the filter is applied. Note you should add commas when necessary e.g. 9,999 - */ - const verifyDocTableFirstExpandedFieldFirstRowExistsFilterButtonFiltersCorrectField = ( - docTableRowNumber, - expandedDocumentRowNumber, - expectedQueryHitsWithoutFilter, - expectedQueryHitsAfterFilterApplied - ) => { - fieldFiltering - .getExpandedDocTableRowFieldName(docTableRowNumber, expandedDocumentRowNumber) - .then(($expandedDocumentRowField) => { - const filterFieldText = $expandedDocumentRowField.text(); - fieldFiltering - .getExpandedDocTableRow(docTableRowNumber, expandedDocumentRowNumber) - .within(() => { - cy.getElementByTestId('addExistsFilterButton').click(); - }); - // Verify full pill text - // globalFilterLabelValue gives the inner element, but we may want all the text in the filter pill - cy.getElementByTestId('globalFilterLabelValue', { - timeout: 10000, - }) - .parent() - .should('have.text', filterFieldText + ': ' + 'exists'); - cy.getElementByTestId('discoverQueryHits').should( - 'have.text', - expectedQueryHitsAfterFilterApplied - ); - }); - cy.getElementByTestId('globalFilterBar').find('[aria-label="Delete"]').click(); - cy.getElementByTestId('discoverQueryHits').should( - 'have.text', - expectedQueryHitsWithoutFilter - ); - }; - - cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); - cy.setQueryLanguage(config.language); - setDatePickerDatesAndSearchIfRelevant(config.language); - - cy.getElementByTestId('docTable').get('tbody tr').should('have.length.above', 3); // To ensure it waits until a full table is loaded into the DOM, instead of a bug where table only has 1 hit. - fieldFiltering.toggleDocTableRow(0); - verifyFirstExpandedFieldFilterForFilterOutFilterExistsButtons(); - fieldFiltering.verifyDocTableFirstExpandedFieldFirstRowToggleColumnButtonHasIntendedBehavior(); - - if (config.isFilterButtonsEnabled) { - verifyDocTableFirstExpandedFieldFirstRowFilterForOutButtonFiltersCorrectField( - 'for', - 0, - 0, - '10,000', - '1' - ); - verifyDocTableFirstExpandedFieldFirstRowFilterForOutButtonFiltersCorrectField( - 'out', - 0, - 0, - '10,000', - '9,999' - ); - verifyDocTableFirstExpandedFieldFirstRowExistsFilterButtonFiltersCorrectField( - 0, - 0, - '10,000', - '10,000' - ); - } + }; + + /** + * Check the Filter For or Out buttons in the expandedDocumentRowNumberth field in the expanded Document filters the correct value. + * @param {string} filterButton For or Out + * @param {number} docTableRowNumber Integer starts from 0 for the first row + * @param {number} expandedDocumentRowNumber Integer starts from 0 for the first row + * @param {string} expectedQueryHitsWithoutFilter expected number of hits in string after the filter is removed Note you should add commas when necessary e.g. 9,999 + * @param {string} expectedQueryHitsAfterFilterApplied expected number of hits in string after the filter is applied. Note you should add commas when necessary e.g. 9,999 + * @example verifyDocTableFirstExpandedFieldFirstRowFilterForButtonFiltersCorrectField('for', 0, 0, '10,000', '1'); + */ + const verifyDocTableFirstExpandedFieldFirstRowFilterForOutButtonFiltersCorrectField = ( + filterButton, + docTableRowNumber, + expandedDocumentRowNumber, + expectedQueryHitsWithoutFilter, + expectedQueryHitsAfterFilterApplied + ) => { + if (filterButton !== 'for' || filterButton !== 'out') { + cy.log('Filter button must be for or or.'); + return; + } + + const filterButtonElement = + filterButton === 'for' ? 'addInclusiveFilterButton' : 'removeInclusiveFilterButton'; + const shouldText = filterButton === 'for' ? 'have.text' : 'not.have.text'; + + fieldFiltering + .getExpandedDocTableRowValue(docTableRowNumber, expandedDocumentRowNumber) + .then(($expandedDocumentRowValue) => { + const filterFieldText = $expandedDocumentRowValue.text(); + fieldFiltering + .getExpandedDocTableRow(docTableRowNumber, expandedDocumentRowNumber) + .within(() => { + cy.getElementByTestId(filterButtonElement).click(); + }); + // Verify pill text + cy.getElementByTestId('globalFilterLabelValue').should('have.text', filterFieldText); + cy.getElementByTestId('discoverQueryHits').should( + 'have.text', + expectedQueryHitsAfterFilterApplied + ); // checkQueryHitText must be in front of checking first line text to give time for DocTable to update. + fieldFiltering + .getExpandedDocTableRowValue(docTableRowNumber, expandedDocumentRowNumber) + .should(shouldText, filterFieldText); + }); + cy.getElementByTestId('globalFilterBar').find('[aria-label="Delete"]').click(); + cy.getElementByTestId('discoverQueryHits').should( + 'have.text', + expectedQueryHitsWithoutFilter + ); + }; + + /** + * Check the first expanded Doc Table Field's first row's Exists Filter button filters the correct Field. + * @param {number} docTableRowNumber Integer starts from 0 for the first row + * @param {number} expandedDocumentRowNumber Integer starts from 0 for the first row + * @param {string} expectedQueryHitsWithoutFilter expected number of hits in string after the filter is removed Note you should add commas when necessary e.g. 9,999 + * @param {string} expectedQueryHitsAfterFilterApplied expected number of hits in string after the filter is applied. Note you should add commas when necessary e.g. 9,999 + */ + const verifyDocTableFirstExpandedFieldFirstRowExistsFilterButtonFiltersCorrectField = ( + docTableRowNumber, + expandedDocumentRowNumber, + expectedQueryHitsWithoutFilter, + expectedQueryHitsAfterFilterApplied + ) => { + fieldFiltering + .getExpandedDocTableRowFieldName(docTableRowNumber, expandedDocumentRowNumber) + .then(($expandedDocumentRowField) => { + const filterFieldText = $expandedDocumentRowField.text(); + fieldFiltering + .getExpandedDocTableRow(docTableRowNumber, expandedDocumentRowNumber) + .within(() => { + cy.getElementByTestId('addExistsFilterButton').click(); + }); + // Verify full pill text + // globalFilterLabelValue gives the inner element, but we may want all the text in the filter pill + cy.getElementByTestId('globalFilterLabelValue', { + timeout: 10000, + }) + .parent() + .should('have.text', filterFieldText + ': ' + 'exists'); + cy.getElementByTestId('discoverQueryHits').should( + 'have.text', + expectedQueryHitsAfterFilterApplied + ); + }); + cy.getElementByTestId('globalFilterBar').find('[aria-label="Delete"]').click(); + cy.getElementByTestId('discoverQueryHits').should( + 'have.text', + expectedQueryHitsWithoutFilter + ); + }; + + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); + cy.setQueryLanguage(config.language); + setDatePickerDatesAndSearchIfRelevant(config.language); + + cy.getElementByTestId('docTable').get('tbody tr').should('have.length.above', 3); // To ensure it waits until a full table is loaded into the DOM, instead of a bug where table only has 1 hit. + fieldFiltering.toggleDocTableRow(0); + verifyFirstExpandedFieldFilterForFilterOutFilterExistsButtons(); + fieldFiltering.verifyDocTableFirstExpandedFieldFirstRowToggleColumnButtonHasIntendedBehavior(); + + if (config.isFilterButtonsEnabled) { + verifyDocTableFirstExpandedFieldFirstRowFilterForOutButtonFiltersCorrectField( + 'for', + 0, + 0, + '10,000', + '1' + ); + verifyDocTableFirstExpandedFieldFirstRowFilterForOutButtonFiltersCorrectField( + 'out', + 0, + 0, + '10,000', + '9,999' + ); + verifyDocTableFirstExpandedFieldFirstRowExistsFilterButtonFiltersCorrectField( + 0, + 0, + '10,000', + '10,000' + ); + } + }); }); }); -}); +}; + +prepareTestSuite('Field Display Filtering', fieldDisplayFilteringTestSuite); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/language_specific_display.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/language_specific_display.spec.js index 6fc575c459bb..6a9b4ba5aa7b 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/language_specific_display.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/language_specific_display.spec.js @@ -4,6 +4,7 @@ */ import { + DATASOURCE_NAME, INDEX_PATTERN_WITH_TIME, INDEX_WITH_TIME_1, INDEX_WITH_TIME_2, @@ -13,22 +14,21 @@ import { import { generateAllTestConfigurations, getRandomizedWorkspaceName, - getRandomizedDatasourceName, setDatePickerDatesAndSearchIfRelevant, } from '../../../../../utils/apps/query_enhancements/shared'; import { generateDisplayTestConfiguration, getLanguageReferenceTestText, } from '../../../../../utils/apps/query_enhancements/language_specific_display'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const datasourceName = getRandomizedDatasourceName(); export const runDisplayTests = () => { describe('Language-Specific Display', () => { beforeEach(() => { // Load test data - cy.setupTestData( + cy.osd.setupTestData( SECONDARY_ENGINE.url, [ `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, @@ -40,31 +40,30 @@ export const runDisplayTests = () => { ] ); // Add data source - cy.addDataSource({ - name: datasourceName, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, url: SECONDARY_ENGINE.url, authType: 'no_auth', }); // Create workspace - cy.deleteWorkspaceByName(workspaceName); + cy.deleteAllWorkspaces(); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); }); afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - // // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); - cy.deleteIndex(INDEX_WITH_TIME_2); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITH_TIME_2); }); generateAllTestConfigurations(generateDisplayTestConfiguration).forEach((config) => { @@ -75,7 +74,7 @@ export const runDisplayTests = () => { isEnhancement: true, }); - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language); setDatePickerDatesAndSearchIfRelevant(config.language); @@ -163,4 +162,4 @@ export const runDisplayTests = () => { }); }; -runDisplayTests(); +prepareTestSuite('Language Specific Display', runDisplayTests); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/queries.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/queries.spec.js index 2eb55681d1bc..bf259f8d6004 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/queries.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/queries.spec.js @@ -3,133 +3,144 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { DATASOURCE_NAME, START_TIME, END_TIME } from '../../../../../utils/apps/constants'; -import { SECONDARY_ENGINE } from '../../../../../utils/constants'; +import { + INDEX_WITH_TIME_1, + DATASOURCE_NAME, + START_TIME, + END_TIME, +} from '../../../../../utils/apps/constants'; +import { PATHS } from '../../../../../utils/constants'; import { getRandomizedWorkspaceName } from '../../../../../utils/apps/query_enhancements/shared'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspace = getRandomizedWorkspaceName(); -describe('query enhancement queries', { scrollBehavior: false }, () => { - before(() => { - // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, - ['cypress/fixtures/query_enhancements/data_logs_1/data_logs_small_time_1.mapping.json'], - ['cypress/fixtures/query_enhancements/data_logs_1/data_logs_small_time_1.data.ndjson'] - ); - - // Add data source - cy.addDataSource({ - name: `${DATASOURCE_NAME}`, - url: `${SECONDARY_ENGINE.url}`, - authType: 'no_auth', - }); - - // Create workspace and set up index pattern - cy.deleteWorkspaceByName(workspace); - cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspace); - - // Create and select index pattern for data_logs_small_time_1* - cy.createWorkspaceIndexPatterns({ - workspaceName: workspace, - indexPattern: 'data_logs_small_time_1', - timefieldName: 'timestamp', - indexPatternHasTimefield: true, - dataSource: DATASOURCE_NAME, - isEnhancement: true, - }); - - // Go to discover page - cy.navigateToWorkSpaceSpecificPage({ - workspaceName: workspace, - page: 'discover', - isEnhancement: true, - }); - }); - - after(() => { - cy.deleteWorkspaceByName(workspace); - cy.deleteDataSourceByName(`${DATASOURCE_NAME}`); - cy.deleteIndex('data_logs_small_time_1'); - }); - - describe('send queries', () => { - it('with DQL', function () { - cy.setQueryLanguage('DQL'); - cy.setTopNavDate(START_TIME, END_TIME); - - const query = `_id:1`; - cy.setQueryEditor(query); - cy.waitForLoader(true); - cy.waitForSearch(); - cy.verifyHitCount(1); - - // Query should persist across refresh - cy.reload(); - cy.verifyHitCount(1); - }); - - it('with Lucene', function () { - cy.setQueryLanguage('Lucene'); - cy.setTopNavDate(START_TIME, END_TIME); - - const query = `_id:1`; - cy.setQueryEditor(query); - cy.waitForLoader(true); - cy.waitForSearch(); - cy.verifyHitCount(1); +const queriesTestSuite = () => { + describe('query enhancement queries', { scrollBehavior: false }, () => { + beforeEach(() => { + // Load test data + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, + [`cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`], + [`cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.data.ndjson`] + ); - // Query should persist across refresh - cy.reload(); - cy.verifyHitCount(1); + // Add data source + cy.osd.addDataSource({ + name: `${DATASOURCE_NAME}`, + url: `${PATHS.SECONDARY_ENGINE}`, + authType: 'no_auth', + }); + + // Create workspace and set up index pattern + cy.deleteAllWorkspaces(); + cy.visit('/app/home'); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspace); + + // Create and select index pattern for ${INDEX_WITH_TIME_1}* + cy.createWorkspaceIndexPatterns({ + workspaceName: workspace, + indexPattern: INDEX_WITH_TIME_1, + timefieldName: 'timestamp', + indexPatternHasTimefield: true, + dataSource: DATASOURCE_NAME, + isEnhancement: true, + }); + + // Go to discover page + cy.navigateToWorkSpaceSpecificPage({ + workspaceName: workspace, + page: 'discover', + isEnhancement: true, + }); }); - it('with SQL', function () { - cy.setQueryLanguage('OpenSearch SQL'); - - // Default SQL query should be set - cy.waitForLoader(true); - cy.getElementByTestId(`osdQueryEditor__multiLine`).contains( - `SELECT * FROM data_logs_small_time_1* LIMIT 10` - ); - cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); - - // Query should persist across refresh - cy.reload(); - cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); - - cy.getElementByTestId('osdQueryEditor__multiLine') - .find('.monaco-editor') - .should('be.visible') - // Ensure editor is in the correct visual state ('vs' is Monaco's default theme) - // This helps verify the editor is fully initialized and ready - .should('have.class', 'vs') - .click() - .find('textarea.inputarea') - .focus() - .type('{backspace}', { force: true }); - - cy.getElementByTestId(`querySubmitButton`).click(); - cy.waitForSearch(); - cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); + afterEach(() => { + cy.deleteWorkspaceByName(workspace); + cy.osd.deleteDataSourceByName(`${DATASOURCE_NAME}`); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); }); - it('with PPL', function () { - cy.setQueryLanguage('PPL'); - - // Default PPL query should be set - cy.waitForLoader(true); - cy.getElementByTestId(`osdQueryEditor__multiLine`).contains( - `source = data_logs_small_time_1*` - ); - cy.waitForSearch(); - cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); - - // Query should persist across refresh - cy.reload(); - cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); - cy.verifyHitCount('10,000'); + describe('send queries', () => { + it('with DQL', function () { + cy.setQueryLanguage('DQL'); + cy.setTopNavDate(START_TIME, END_TIME); + + const query = `_id:1`; + cy.setQueryEditor(query); + cy.waitForLoader(true); + cy.waitForSearch(); + cy.verifyHitCount(1); + + // Query should persist across refresh + cy.reload(); + cy.verifyHitCount(1); + }); + + it('with Lucene', function () { + cy.setQueryLanguage('Lucene'); + cy.setTopNavDate(START_TIME, END_TIME); + + const query = `_id:1`; + cy.setQueryEditor(query); + cy.waitForLoader(true); + cy.waitForSearch(); + cy.verifyHitCount(1); + + // Query should persist across refresh + cy.reload(); + cy.verifyHitCount(1); + }); + + it('with SQL', function () { + cy.setQueryLanguage('OpenSearch SQL'); + + // Default SQL query should be set + cy.waitForLoader(true); + cy.getElementByTestId(`osdQueryEditor__multiLine`).contains( + `SELECT * FROM ${INDEX_WITH_TIME_1}* LIMIT 10` + ); + cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); + + // Query should persist across refresh + cy.reload(); + cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); + + cy.getElementByTestId('osdQueryEditor__multiLine') + .find('.monaco-editor') + .should('be.visible') + // Ensure editor is in the correct visual state ('vs' is Monaco's default theme) + // This helps verify the editor is fully initialized and ready + .should('have.class', 'vs') + .click() + .find('textarea.inputarea') + .focus() + .type('{backspace}', { force: true }); + + cy.getElementByTestId(`querySubmitButton`).click(); + cy.waitForSearch(); + cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); + }); + + it('with PPL', function () { + cy.setQueryLanguage('PPL'); + cy.setTopNavDate(START_TIME, END_TIME); + + // Default PPL query should be set + cy.waitForLoader(true); + cy.getElementByTestId(`osdQueryEditor__multiLine`).contains( + `source = ${INDEX_WITH_TIME_1}*` + ); + cy.waitForSearch(); + cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); + + // Query should persist across refresh + cy.reload(); + cy.getElementByTestId(`queryResultCompleteMsg`).should('be.visible'); + cy.verifyHitCount('10,000'); + }); }); }); -}); +}; + +prepareTestSuite('Queries', queriesTestSuite); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/s3_dataset.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/s3_dataset.spec.js index 7441d7f25b66..81a79d218d7f 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/s3_dataset.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/s3_dataset.spec.js @@ -9,136 +9,141 @@ import { S3_CLUSTER, } from '../../../../../utils/apps/query_enhancements/constants'; import { getRandomizedWorkspaceName } from '../../../../../utils/apps/query_enhancements/shared'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspace = getRandomizedWorkspaceName(); -let dataSourceId = ''; -const definedS3Variables = !S3_CLUSTER.url; +const s3DatasetTestSuite = () => { + let dataSourceId = ''; + const definedS3Variables = !S3_CLUSTER.url; -(definedS3Variables ? describe.skip : describe)( - 'S3 Dataset', - { defaultCommandTimeout: 120000 }, - () => { - before(() => { - cy.request({ - method: 'POST', - url: `${DSM_API}`, - headers: { - 'osd-xsrf': true, - }, - body: { - dataSourceAttr: { - endpoint: S3_CLUSTER.url, - auth: { - type: 'username_password', - credentials: { - username: S3_CLUSTER.username, - password: S3_CLUSTER.password, + (definedS3Variables ? describe.skip : describe)( + 'S3 Dataset', + { defaultCommandTimeout: 120000 }, + () => { + before(() => { + cy.request({ + method: 'POST', + url: `${DSM_API}`, + headers: { + 'osd-xsrf': true, + }, + body: { + dataSourceAttr: { + endpoint: S3_CLUSTER.url, + auth: { + type: 'username_password', + credentials: { + username: S3_CLUSTER.username, + password: S3_CLUSTER.password, + }, }, }, }, - }, - }).then((metaRes) => { - if (metaRes && metaRes.body) { - cy.request({ - method: 'POST', - url: `${DS_API.CREATE_DATA_SOURCE}`, - headers: { - 'osd-xsrf': true, - 'Content-Type': 'application/json', - }, - body: { - attributes: { - title: S3_CLUSTER.name, - description: '', - ...metaRes.body, - endpoint: S3_CLUSTER.url, - auth: { - type: 'username_password', - credentials: { - username: S3_CLUSTER.username, - password: S3_CLUSTER.password, + }).then((metaRes) => { + if (metaRes && metaRes.body) { + cy.request({ + method: 'POST', + url: `${DS_API.CREATE_DATA_SOURCE}`, + headers: { + 'osd-xsrf': true, + 'Content-Type': 'application/json', + }, + body: { + attributes: { + title: S3_CLUSTER.name, + description: '', + ...metaRes.body, + endpoint: S3_CLUSTER.url, + auth: { + type: 'username_password', + credentials: { + username: S3_CLUSTER.username, + password: S3_CLUSTER.password, + }, }, }, }, - }, - }).then((resp) => { - if (resp && resp.body && resp.body.id) { - dataSourceId = resp.body.id; - } - }); - } + }).then((resp) => { + if (resp && resp.body && resp.body.id) { + dataSourceId = resp.body.id; + } + }); + } + }); }); - }); - after(() => { - cy.request({ - method: 'DELETE', - url: `${DS_API.DELETE_DATA_SOURCE}${dataSourceId}`, - body: { force: false }, - headers: { - 'osd-xsrf': true, - }, + after(() => { + cy.request({ + method: 'DELETE', + url: `${DS_API.DELETE_DATA_SOURCE}${dataSourceId}`, + body: { force: false }, + headers: { + 'osd-xsrf': true, + }, + }); }); - }); - describe('Run S3 Query', () => { - beforeEach(() => { - // Create workspace - cy.deleteWorkspaceByName(workspace); - cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(S3_CLUSTER.name, WORKSPACE_NAME); - }); - afterEach(() => { - cy.deleteWorkspaceByName(workspace); - }); + describe('Run S3 Query', () => { + beforeEach(() => { + // Create workspace + cy.deleteWorkspaceByName(workspace); + cy.visit('/app/home'); + cy.osd.createInitialWorkspaceWithDataSource(S3_CLUSTER.name, workspace); + }); + afterEach(() => { + cy.deleteWorkspaceByName(workspace); + }); - it('with SQL', function () { - cy.getElementByTestId(`datasetSelectorButton`).click(); - cy.getElementByTestId(`datasetSelectorAdvancedButton`).click(); + it('with SQL', function () { + cy.getElementByTestId(`datasetSelectorButton`).click(); + cy.getElementByTestId(`datasetSelectorAdvancedButton`).click(); - cy.get(`[title="S3 Connections"]`).click(); - cy.get(`[title="BasicS3Connection"]`).click(); - cy.get(`[title="mys3"]`).click(); - cy.get(`[title="default"]`).click(); - cy.get(`[title="http_logs"]`).click(); - cy.getElementByTestId('datasetSelectorNext').click(); - cy.get(`[class="euiModalHeader__title"]`).should('contain', 'Step 2: Configure data'); + cy.get(`[title="S3 Connections"]`).click(); + cy.get(`[title="BasicS3Connection"]`).click(); + cy.get(`[title="mys3"]`).click(); + cy.get(`[title="default"]`).click(); + cy.get(`[title="http_logs"]`).click(); + cy.getElementByTestId('datasetSelectorNext').click(); + cy.get(`[class="euiModalHeader__title"]`).should('contain', 'Step 2: Configure data'); - cy.getElementByTestId('advancedSelectorLanguageSelect').select('OpenSearch SQL'); - cy.getElementByTestId('advancedSelectorConfirmButton').click(); - cy.waitForLoader(true); - cy.waitForSearch(); + cy.getElementByTestId('advancedSelectorLanguageSelect').select('OpenSearch SQL'); + cy.getElementByTestId('advancedSelectorConfirmButton').click(); + cy.waitForLoader(true); + cy.waitForSearch(); - cy.getElementByTestId('queryEditorLanguageSelector').should('contain', 'OpenSearch SQL'); - cy.get(`[data-test-subj="queryResultCompleteMsg"]`).should('be.visible'); - cy.getElementByTestId('docTable').should('be.visible'); - cy.getElementByTestId('docTable').find('tr').should('have.length', 11); - }); + cy.getElementByTestId('queryEditorLanguageSelector').should('contain', 'OpenSearch SQL'); + cy.get(`[data-test-subj="queryResultCompleteMsg"]`).should('be.visible'); + cy.getElementByTestId('docTable').should('be.visible'); + cy.getElementByTestId('docTable').find('tr').should('have.length', 11); + }); - // Skipping until #8922 is merged in - it.skip('with PPL', function () { - cy.getElementByTestId(`datasetSelectorButton`).click(); - cy.getElementByTestId(`datasetSelectorAdvancedButton`).click(); + // Skipping until #8922 is merged in + it.skip('with PPL', function () { + cy.getElementByTestId(`datasetSelectorButton`).click(); + cy.getElementByTestId(`datasetSelectorAdvancedButton`).click(); - cy.get(`[title="S3 Connections"]`).click(); - cy.get(`[title="BasicS3Connection"]`).click(); - cy.get(`[title="mys3"]`).click(); - cy.get(`[title="default"]`).click(); - cy.get(`[title="http_logs"]`).click(); - cy.getElementByTestId('datasetSelectorNext').click(); - cy.get(`[class="euiModalHeader__title"]`).should('contain', 'Step 2: Configure data'); + cy.get(`[title="S3 Connections"]`).click(); + cy.get(`[title="BasicS3Connection"]`).click(); + cy.get(`[title="mys3"]`).click(); + cy.get(`[title="default"]`).click(); + cy.get(`[title="http_logs"]`).click(); + cy.getElementByTestId('datasetSelectorNext').click(); + cy.get(`[class="euiModalHeader__title"]`).should('contain', 'Step 2: Configure data'); - cy.getElementByTestId('advancedSelectorLanguageSelect').select('PPL'); - cy.getElementByTestId('advancedSelectorConfirmButton').click(); - cy.waitForLoader(true); - cy.waitForSearch(); + cy.getElementByTestId('advancedSelectorLanguageSelect').select('PPL'); + cy.getElementByTestId('advancedSelectorConfirmButton').click(); + cy.waitForLoader(true); + cy.waitForSearch(); - cy.getElementByTestId('queryEditorLanguageSelector').should('contain', 'PPL'); - cy.get(`[data-test-subj="queryResultCompleteMsg"]`).should('be.visible'); - cy.getElementByTestId('docTable').should('be.visible'); - cy.getElementByTestId('docTable').find('tr').should('have.length', 11); + cy.getElementByTestId('queryEditorLanguageSelector').should('contain', 'PPL'); + cy.get(`[data-test-subj="queryResultCompleteMsg"]`).should('be.visible'); + cy.getElementByTestId('docTable').should('be.visible'); + cy.getElementByTestId('docTable').find('tr').should('have.length', 11); + }); }); - }); - } -); + } + ); +}; + +prepareTestSuite('S3 Dataset', s3DatasetTestSuite); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_queries.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_queries.spec.js index 062ac35c2481..eeac79928b83 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_queries.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_queries.spec.js @@ -4,10 +4,11 @@ */ import { + DATASOURCE_NAME, INDEX_PATTERN_WITH_TIME, INDEX_WITH_TIME_1, INDEX_WITH_TIME_2, - SECONDARY_ENGINE, + PATHS, } from '../../../../../utils/constants'; import { @@ -21,15 +22,14 @@ import { import { getRandomizedWorkspaceName, - getRandomizedDatasourceName, setDatePickerDatesAndSearchIfRelevant, generateAllTestConfigurations, } from '../../../../../utils/apps/query_enhancements/shared'; import { generateSavedTestConfiguration } from '../../../../../utils/apps/query_enhancements/saved'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const datasourceName = getRandomizedDatasourceName(); const createSavedQuery = (config) => { cy.navigateToWorkSpaceSpecificPage({ @@ -38,7 +38,7 @@ const createSavedQuery = (config) => { isEnhancement: true, }); - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language); setDatePickerDatesAndSearchIfRelevant(config.language); @@ -58,7 +58,7 @@ const loadSavedQuery = (config) => { cy.getElementByTestId('discoverNewButton').click(); // Todo - Date Picker sometimes does not load when expected. Have to set dataset and query language again. - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language); setDatePickerDatesAndSearchIfRelevant( @@ -102,13 +102,12 @@ const deleteSavedQuery = (saveAsNewQueryName) => { verifyQueryDoesNotExistInSavedQueries(saveAsNewQueryName); }; -// This spec assumes data.savedQueriesNewUI.enabled is true. -export const runSavedQueriesUITests = () => { +const runSavedQueriesUITests = () => { describe('saved queries UI', () => { beforeEach(() => { // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [ `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, `cypress/fixtures/query_enhancements/data_logs_2/${INDEX_WITH_TIME_2}.mapping.json`, @@ -119,21 +118,21 @@ export const runSavedQueriesUITests = () => { ] ); // Add data source - cy.addDataSource({ - name: datasourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); // Create workspace - cy.deleteWorkspaceByName(workspaceName); + cy.deleteAllWorkspaces(); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); }); @@ -141,10 +140,9 @@ export const runSavedQueriesUITests = () => { afterEach(() => { // No need to explicitly delete all saved queries as deleting the workspace will delete associated saved queries cy.deleteWorkspaceByName(workspaceName); - // // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); - cy.deleteIndex(INDEX_WITH_TIME_2); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITH_TIME_2); }); const testConfigurations = generateAllTestConfigurations(generateSavedTestConfiguration); @@ -163,4 +161,4 @@ export const runSavedQueriesUITests = () => { }); }; -runSavedQueriesUITests(); +prepareTestSuite('Saved Queries', runSavedQueriesUITests); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_search.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_search.spec.js index 9494a73dd324..b1e8c3c1ca5d 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_search.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_search.spec.js @@ -8,12 +8,12 @@ import { INDEX_WITH_TIME_1, INDEX_WITH_TIME_2, QueryLanguages, - SECONDARY_ENGINE, + PATHS, + DATASOURCE_NAME, } from '../../../../../utils/constants'; import { generateAllTestConfigurations, getRandomizedWorkspaceName, - getRandomizedDatasourceName, setDatePickerDatesAndSearchIfRelevant, } from '../../../../../utils/apps/query_enhancements/shared'; import { @@ -24,16 +24,16 @@ import { updateSavedSearchAndSaveAndVerify, generateSavedTestConfiguration, } from '../../../../../utils/apps/query_enhancements/saved'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const datasourceName = getRandomizedDatasourceName(); -export const runSavedSearchTests = () => { +const runSavedSearchTests = () => { describe('saved search', () => { beforeEach(() => { // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [ `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, `cypress/fixtures/query_enhancements/data_logs_2/${INDEX_WITH_TIME_2}.mapping.json`, @@ -44,31 +44,30 @@ export const runSavedSearchTests = () => { ] ); // Add data source - cy.addDataSource({ - name: datasourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); // Create workspace - cy.deleteWorkspaceByName(workspaceName); + cy.deleteAllWorkspaces(); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); }); afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - // // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); - cy.deleteIndex(INDEX_WITH_TIME_2); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITH_TIME_2); }); generateAllTestConfigurations(generateSavedTestConfiguration).forEach((config) => { @@ -79,7 +78,7 @@ export const runSavedSearchTests = () => { isEnhancement: true, }); - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language); setDatePickerDatesAndSearchIfRelevant(config.language); @@ -120,7 +119,7 @@ export const runSavedSearchTests = () => { // This means that we are only testing loading a saved search // starting from an INDEX_PATTERN dataset, but I think testing where the // start is a permutation of other dataset is overkill - cy.setIndexPatternAsDataset(INDEX_PATTERN_WITH_TIME, datasourceName); + cy.setIndexPatternAsDataset(INDEX_PATTERN_WITH_TIME, DATASOURCE_NAME); cy.setQueryLanguage(startingLanguage); cy.loadSaveSearch(config.saveName); @@ -132,16 +131,16 @@ export const runSavedSearchTests = () => { it(`should successfully update a saved search for ${config.testName}`, () => { // using a POST request to create a saved search to load postRequestSaveSearch(config); - updateSavedSearchAndSaveAndVerify(config, workspaceName, datasourceName, false); + updateSavedSearchAndSaveAndVerify(config, workspaceName, DATASOURCE_NAME, false); }); it(`should successfully save a saved search as a new saved search for ${config.testName}`, () => { // using a POST request to create a saved search to load postRequestSaveSearch(config); - updateSavedSearchAndSaveAndVerify(config, workspaceName, datasourceName, true); + updateSavedSearchAndSaveAndVerify(config, workspaceName, DATASOURCE_NAME, true); }); }); }); }; -runSavedSearchTests(); +prepareTestSuite('Saved Search', runSavedSearchTests); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_search_in_dashboards.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_search_in_dashboards.spec.js index a4f3bb38bb62..f412d157cae8 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_search_in_dashboards.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/saved_search_in_dashboards.spec.js @@ -5,16 +5,16 @@ import { DatasetTypes, + DATASOURCE_NAME, INDEX_PATTERN_WITH_TIME, INDEX_WITH_TIME_1, INDEX_WITH_TIME_2, QueryLanguages, - SECONDARY_ENGINE, + PATHS, START_TIME, } from '../../../../../utils/constants'; import { getRandomizedWorkspaceName, - getRandomizedDatasourceName, setDatePickerDatesAndSearchIfRelevant, } from '../../../../../utils/apps/query_enhancements/shared'; import { @@ -24,16 +24,16 @@ import { loadSavedSearchFromDashboards, navigateToDashboardAndOpenSavedSearchPanel, } from '../../../../../utils/apps/query_enhancements/saved'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const datasourceName = getRandomizedDatasourceName(); export const runSavedSearchTests = () => { describe('saved search in dashboards', () => { beforeEach(() => { // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [ `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, `cypress/fixtures/query_enhancements/data_logs_2/${INDEX_WITH_TIME_2}.mapping.json`, @@ -44,31 +44,30 @@ export const runSavedSearchTests = () => { ] ); // Add data source - cy.addDataSource({ - name: datasourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); // Create workspace - cy.deleteWorkspaceByName(workspaceName); + cy.deleteAllWorkspaces(); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); }); afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - // // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); - cy.deleteIndex(INDEX_WITH_TIME_2); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITH_TIME_2); }); it('Load a saved search', () => { @@ -153,4 +152,4 @@ export const runSavedSearchTests = () => { }); }; -runSavedSearchTests(); +prepareTestSuite('Saved Search in Dashboards', runSavedSearchTests); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/shared_links.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/shared_links.spec.js index 0531145feec7..eabdcd82b9ff 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/shared_links.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/shared_links.spec.js @@ -6,11 +6,11 @@ import { INDEX_WITH_TIME_1, INDEX_PATTERN_WITH_TIME_1, - SECONDARY_ENGINE, + PATHS, + DATASOURCE_NAME, } from '../../../../../utils/constants'; import { getRandomizedWorkspaceName, - getRandomizedDatasourceName, generateAllTestConfigurations, setDatePickerDatesAndSearchIfRelevant, setHistogramIntervalIfRelevant, @@ -19,9 +19,9 @@ import { QueryLanguages } from '../../../../../utils/apps/query_enhancements/con import { selectFieldFromSidebar } from '../../../../../utils/apps/query_enhancements/sidebar'; import { verifyShareUrl } from '../../../../../utils/apps/query_enhancements/shared_links'; import { setSort } from '../../../../../utils/apps/query_enhancements/table'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const datasourceName = getRandomizedDatasourceName(); const generateShareUrlsTestConfiguration = (dataset, datasetType, language) => { const baseConfig = { @@ -61,25 +61,25 @@ export const runSharedLinksTests = () => { filter: ['category', 'Network'], }; beforeEach(() => { - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [`cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`], [`cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.data.ndjson`] ); - cy.addDataSource({ - name: datasourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); cy.deleteWorkspaceByName(workspaceName); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); }); afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); }); generateAllTestConfigurations(generateShareUrlsTestConfiguration, { @@ -93,7 +93,7 @@ export const runSharedLinksTests = () => { workspaceName: workspaceName, indexPattern: INDEX_WITH_TIME_1, timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); } @@ -108,7 +108,7 @@ export const runSharedLinksTests = () => { it(`should handle shared document links correctly for ${config.testName}`, () => { // Setup - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language); setDatePickerDatesAndSearchIfRelevant(config.language); @@ -153,7 +153,7 @@ export const runSharedLinksTests = () => { it(`should persist state in shared links for ${config.testName}`, () => { // Set dataset and language - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language); setDatePickerDatesAndSearchIfRelevant(config.language); @@ -181,7 +181,7 @@ export const runSharedLinksTests = () => { cy.getElementByTestId('copyShareUrlButton') .invoke('attr', 'data-share-url') .then((url) => { - verifyShareUrl(url, config, testData, datasourceName, queryString); + verifyShareUrl(url, config, testData, DATASOURCE_NAME, queryString); }); // Test short url @@ -198,7 +198,7 @@ export const runSharedLinksTests = () => { }) .then((response) => { const redirectUrl = response.headers.location; - verifyShareUrl(redirectUrl, config, testData, datasourceName, queryString); + verifyShareUrl(redirectUrl, config, testData, DATASOURCE_NAME, queryString); }); // Test saved object url @@ -229,4 +229,4 @@ export const runSharedLinksTests = () => { }); }; -runSharedLinksTests(); +prepareTestSuite('Shared Links', runSharedLinksTests); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/sidebar.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/sidebar.spec.js index 49cc38d73e41..c064c1f68bee 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/sidebar.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/sidebar.spec.js @@ -6,20 +6,20 @@ import { INDEX_WITH_TIME_1, INDEX_PATTERN_WITH_TIME_1, - SECONDARY_ENGINE, + PATHS, + DATASOURCE_NAME, } from '../../../../../utils/constants'; import { generateAllTestConfigurations, getRandomizedWorkspaceName, - getRandomizedDatasourceName, setDatePickerDatesAndSearchIfRelevant, } from '../../../../../utils/apps/query_enhancements/shared'; import { getDocTableField } from '../../../../../utils/apps/query_enhancements/field_display_filtering'; import * as sideBar from '../../../../../utils/apps/query_enhancements/sidebar'; import { generateSavedTestConfiguration } from '../../../../../utils/apps/query_enhancements/saved'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const datasourceName = getRandomizedDatasourceName(); const addSidebarFieldsAndCheckDocTableColumns = ( testFields, @@ -142,25 +142,25 @@ export const runSideBarTests = () => { }; beforeEach(() => { - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [`cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`], [`cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.data.ndjson`] ); - cy.addDataSource({ - name: datasourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); cy.deleteWorkspaceByName(workspaceName); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); }); afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); cy.window().then((win) => { win.localStorage.clear(); win.sessionStorage.clear(); @@ -178,7 +178,7 @@ export const runSideBarTests = () => { workspaceName: workspaceName, indexPattern: INDEX_WITH_TIME_1, timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); } @@ -187,7 +187,7 @@ export const runSideBarTests = () => { page: 'discover', isEnhancement: true, }); - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language); setDatePickerDatesAndSearchIfRelevant(config.language); sideBar.removeAllSelectedFields(); @@ -227,4 +227,4 @@ export const runSideBarTests = () => { }); }; -runSideBarTests(); +prepareTestSuite('Sidebar', runSideBarTests); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/simple_dataset_selector.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/simple_dataset_selector.spec.js index 9a61a067aee9..2347ebd8d876 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/simple_dataset_selector.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/simple_dataset_selector.spec.js @@ -8,11 +8,11 @@ import { INDEX_WITH_TIME_1, INDEX_PATTERN_WITH_NO_TIME, INDEX_WITHOUT_TIME_1, - SECONDARY_ENGINE, + PATHS, + DATASOURCE_NAME, } from '../../../../../utils/constants'; import { getRandomizedWorkspaceName, - getRandomizedDatasourceName, getDefaultQuery, setDatePickerDatesAndSearchIfRelevant, } from '../../../../../utils/apps/query_enhancements/shared'; @@ -21,17 +21,17 @@ import { generateSimpleDatasetSelectorTestConfigurations, validateItemsInSimpleDatasetSelectorDropDown, } from '../../../../../utils/apps/query_enhancements/simple_dataset_selector'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const datasourceName = getRandomizedDatasourceName(); const noIndexPatterns = 5; // Determines the no of index patterns that should be in the dropdown for filtering test case export const runSimpleDatasetSelectorTests = () => { describe('simple dataset selector selecting an index pattern', () => { beforeEach(() => { // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [ `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITHOUT_TIME_1}.mapping.json`, @@ -42,28 +42,28 @@ export const runSimpleDatasetSelectorTests = () => { ] ); // Add data source - cy.addDataSource({ - name: datasourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); // Create workspace - cy.deleteWorkspaceByName(workspaceName); + cy.deleteAllWorkspaces(); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_NO_TIME.replace('*', ''), timefieldName: '', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, indexPatternHasTimefield: false, }); @@ -71,10 +71,9 @@ export const runSimpleDatasetSelectorTests = () => { afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); - cy.deleteIndex(INDEX_WITHOUT_TIME_1); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITHOUT_TIME_1); }); generateSimpleDatasetSelectorTestConfigurations([ @@ -102,7 +101,7 @@ export const runSimpleDatasetSelectorTests = () => { cy.setQueryLanguage(config.language); // Select the index pattern - cy.setIndexPatternAsDataset(config.indexPattern, datasourceName); + cy.setIndexPatternAsDataset(config.indexPattern, DATASOURCE_NAME); // Verify if the language is unchanged, we get a default query populated, and correct dataset is set verifyDiscoverPageState({ @@ -129,8 +128,8 @@ export const runSimpleDatasetSelectorTests = () => { describe('filtering index pattern in simple dataset selector', () => { beforeEach(() => { // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [ `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITHOUT_TIME_1}.mapping.json`, @@ -141,23 +140,23 @@ export const runSimpleDatasetSelectorTests = () => { ] ); // Add data source - cy.addDataSource({ - name: datasourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); // Create workspace cy.deleteWorkspaceByName(workspaceName); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); for (let i = 1; i <= noIndexPatterns; i++) { cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_TIME.slice(0, i), timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); } @@ -165,10 +164,9 @@ export const runSimpleDatasetSelectorTests = () => { afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); - cy.deleteIndex(INDEX_WITHOUT_TIME_1); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITHOUT_TIME_1); }); it('validate filtering index pattern in simple dataset selector', () => { @@ -188,4 +186,4 @@ export const runSimpleDatasetSelectorTests = () => { }); }; -runSimpleDatasetSelectorTests(); +prepareTestSuite('Simple Dataset Selector', runSimpleDatasetSelectorTests); diff --git a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/time_range_selection.spec.js b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/time_range_selection.spec.js index 187520b76eb6..3849ec7750f6 100644 --- a/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/time_range_selection.spec.js +++ b/cypress/integration/core_opensearch_dashboards/opensearch_dashboards/apps/query_enhancements/time_range_selection.spec.js @@ -4,27 +4,27 @@ */ import { + DATASOURCE_NAME, INDEX_PATTERN_WITH_TIME, INDEX_WITH_TIME_1, INDEX_WITH_TIME_2, - SECONDARY_ENGINE, + PATHS, } from '../../../../../utils/constants'; import { generateAllTestConfigurations, getRandomizedWorkspaceName, - getRandomizedDatasourceName, } from '../../../../../utils/apps/query_enhancements/shared'; import { generateTimeRangeTestConfiguration } from '../../../../../utils/apps/query_enhancements/time_range_selection'; +import { prepareTestSuite } from '../../../../../utils/helpers'; const workspaceName = getRandomizedWorkspaceName(); -const datasourceName = getRandomizedDatasourceName(); export const runTimeRangeSelectionTests = () => { describe('Time Range Selection Tests', () => { beforeEach(() => { // Load test data - cy.setupTestData( - SECONDARY_ENGINE.url, + cy.osd.setupTestData( + PATHS.SECONDARY_ENGINE, [ `cypress/fixtures/query_enhancements/data_logs_1/${INDEX_WITH_TIME_1}.mapping.json`, `cypress/fixtures/query_enhancements/data_logs_2/${INDEX_WITH_TIME_2}.mapping.json`, @@ -35,31 +35,30 @@ export const runTimeRangeSelectionTests = () => { ] ); // Add data source - cy.addDataSource({ - name: datasourceName, - url: SECONDARY_ENGINE.url, + cy.osd.addDataSource({ + name: DATASOURCE_NAME, + url: PATHS.SECONDARY_ENGINE, authType: 'no_auth', }); // Create workspace - cy.deleteWorkspaceByName(workspaceName); + cy.deleteAllWorkspaces(); cy.visit('/app/home'); - cy.osd.createInitialWorkspaceWithDataSource(datasourceName, workspaceName); + cy.osd.createInitialWorkspaceWithDataSource(DATASOURCE_NAME, workspaceName); cy.createWorkspaceIndexPatterns({ workspaceName: workspaceName, indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''), timefieldName: 'timestamp', - dataSource: datasourceName, + dataSource: DATASOURCE_NAME, isEnhancement: true, }); }); afterEach(() => { cy.deleteWorkspaceByName(workspaceName); - // TODO: Modify deleteIndex to handle an array of index and remove hard code - cy.deleteDataSourceByName(datasourceName); - cy.deleteIndex(INDEX_WITH_TIME_1); - cy.deleteIndex(INDEX_WITH_TIME_2); + cy.osd.deleteDataSourceByName(DATASOURCE_NAME); + cy.osd.deleteIndex(INDEX_WITH_TIME_1); + cy.osd.deleteIndex(INDEX_WITH_TIME_2); }); generateAllTestConfigurations(generateTimeRangeTestConfiguration).forEach((config) => { @@ -70,7 +69,7 @@ export const runTimeRangeSelectionTests = () => { isEnhancement: true, }); - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language.name); @@ -91,7 +90,7 @@ export const runTimeRangeSelectionTests = () => { isEnhancement: true, }); - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language.name); @@ -112,7 +111,7 @@ export const runTimeRangeSelectionTests = () => { isEnhancement: true, }); - cy.setDataset(config.dataset, datasourceName, config.datasetType); + cy.setDataset(config.dataset, DATASOURCE_NAME, config.datasetType); cy.setQueryLanguage(config.language.name); @@ -129,4 +128,4 @@ export const runTimeRangeSelectionTests = () => { }); }; -runTimeRangeSelectionTests(); +prepareTestSuite('Time Range Selection', runTimeRangeSelectionTests); diff --git a/cypress/utils/apps/data_explorer/commands.js b/cypress/utils/apps/data_explorer/commands.js index 54f30c682e62..7e826f4ae078 100644 --- a/cypress/utils/apps/data_explorer/commands.js +++ b/cypress/utils/apps/data_explorer/commands.js @@ -5,18 +5,6 @@ export const toTestId = (str, replace = '-') => str.replace(/\s+/g, replace); -Cypress.Commands.add('verifyTimeConfig', (start, end) => { - const opts = { log: false }; - - cy.getElementByTestId('superDatePickerstartDatePopoverButton', opts) - .should('be.visible') - .should('have.text', start); - - cy.getElementByTestId('superDatePickerendDatePopoverButton', opts) - .should('be.visible') - .should('have.text', end); -}); - Cypress.Commands.add('saveSearch', (name, saveAsNew = false) => { cy.log('in func save search'); const opts = { log: false }; @@ -64,20 +52,6 @@ Cypress.Commands.add('waitForSearch', () => { cy.getElementByTestId('docTable'); }); -Cypress.Commands.add('prepareTest', (fromTime, toTime, interval) => { - cy.setTopNavDate(fromTime, toTime); - cy.waitForLoader(); - // wait until the search has been finished - cy.waitForSearch(); - cy.get('select').select(`${interval}`); - cy.waitForLoader(); - cy.waitForSearch(); -}); - -Cypress.Commands.add('verifyMarkCount', (count) => { - cy.getElementByTestId('docTable').find('mark').should('have.length', count); -}); - Cypress.Commands.add( 'submitFilterFromDropDown', (field, operator, value, isEnhancement = false) => { @@ -211,36 +185,3 @@ Cypress.Commands.add('deleteSaveQuery', (name) => { cy.getElementByTestId('confirmModalConfirmButton').click(); }); - -Cypress.Commands.add('switchDiscoverTable', (name) => { - cy.getElementByTestId('discoverOptionsButton') - .then(($button) => { - cy.wrap($button).click({ force: true }); - - cy.getElementByTestId('discoverOptionsLegacySwitch').then(($switchButton) => { - if (name === 'new') { - cy.wrap($switchButton).click({ force: true }); - } - if (name === 'legacy') { - cy.wrap($switchButton).click({ force: true }); - } - cy.waitForLoader(); - }); - }) - .then(() => { - checkForElementVisibility(); - }); -}); - -function checkForElementVisibility() { - cy.getElementsByTestIds('queryInput') - .should('be.visible') - .then(($element) => { - if ($element.is(':visible')) { - return; - } else { - cy.wait(500); // Wait for half a second before checking again - checkForElementVisibility(); // Recursive call - } - }); -} diff --git a/cypress/utils/apps/data_explorer/index.d.ts b/cypress/utils/apps/data_explorer/index.d.ts index 95805f36e5a5..4246d9699ea2 100644 --- a/cypress/utils/apps/data_explorer/index.d.ts +++ b/cypress/utils/apps/data_explorer/index.d.ts @@ -5,14 +5,10 @@ declare namespace Cypress { interface Chainable { - verifyTimeConfig(start: string, end: string): Chainable; saveSearch(name: string, saveAsNew?: boolean): Chainable; loadSaveSearch(name: string): Chainable; verifyHitCount(count: string): Chainable; waitForSearch(): Chainable; - prepareTest(fromTime: string, toTime: string, interval: string): Chainable; - submitQuery(query: string): Chainable; - verifyMarkCount(count: string): Chainable; submitFilterFromDropDown( field: string, operator: string, @@ -56,6 +52,5 @@ declare namespace Cypress { loadSaveQuery(name: string): Chainable; clearSaveQuery(): Chainable; deleteSaveQuery(name: string): Chainable; - switchDiscoverTable(name: string): Chainable; } } diff --git a/cypress/utils/apps/query_enhancements/commands.js b/cypress/utils/apps/query_enhancements/commands.js index 4a28218233d5..79b01de6c3f1 100644 --- a/cypress/utils/apps/query_enhancements/commands.js +++ b/cypress/utils/apps/query_enhancements/commands.js @@ -42,103 +42,6 @@ Cypress.Commands.add('setQueryLanguage', (value) => { }); }); -/** - * Creates a new data source connection with basic auth - * It also saves the created data source's id to the alias @DATASOURCE_ID - * @param {Object} options Configuration options for the data source - * @param {string} options.name The name/title for the data source - * @param {string} options.url The endpoint URL for the data source - * @param {string} options.authType The authentication type (e.g. 'no_auth', 'basic_auth', etc.) - * @param {Object} [options.credentials] Optional credentials for auth types that require them - * @param {string} [options.credentials.username] Username for basic auth - * @param {string} [options.credentials.password] Password for basic auth - */ -Cypress.Commands.add('addDataSource', (options) => { - const { name, url, authType = 'no_auth', credentials = {} } = options; - - // Visit the create data source page - cy.visit('app/management/opensearch-dashboards/dataSources/create'); - - // Intercept the create request to verify success - cy.intercept('POST', '/api/saved_objects/data-source').as('createDataSourceRequest'); - - // Select OpenSearch card - cy.getElementByTestId('datasource_card_opensearch').click(); - - // Fill in basic info - cy.get('[name="dataSourceTitle"]').type(name); - cy.get('[name="endpoint"]').type(url); - - // Select auth type - cy.getElementByTestId('createDataSourceFormAuthTypeSelect').click(); - cy.get(`button[id="${authType}"]`).click(); - - // Handle credentials if provided and required - if (authType === 'basic_auth' && credentials.username && credentials.password) { - cy.get('[name="username"]').type(credentials.username); - cy.get('[name="password"]').type(credentials.password); - } - - // Submit form. Adding 'force' as sometimes a popover hides the button - cy.getElementByTestId('createDataSourceButton').click({ force: true }); - - // Wait for successful creation - cy.wait('@createDataSourceRequest').then((interception) => { - expect(interception.response.statusCode).to.equal(200); - // save the created data source ID as an alias - cy.wrap(interception.response.body.id).as('DATASOURCE_ID'); - }); - - // Verify redirect to data sources list page - cy.location('pathname', { timeout: 6000 }).should( - 'include', - 'app/management/opensearch-dashboards/dataSources' - ); -}); - -Cypress.Commands.add('deleteDataSourceByName', (dataSourceName) => { - // Navigate to the dataSource Management page - cy.visit('app/dataSources'); - - // Find the anchor text corresponding to specified dataSource - cy.get('a').contains(dataSourceName).click(); - - // Delete the dataSource connection - cy.getElementByTestId('editDatasourceDeleteIcon').click(); - cy.getElementByTestId('confirmModalConfirmButton').click(); -}); - -// Deletes all data sources. This command should only be used for convenience during development -// and should never be used in production -Cypress.Commands.add('deleteAllDataSources', () => { - cy.visit('app/dataSources'); - cy.waitForLoader(true); - cy.wait(2000); - - cy.get('body').then(($body) => { - const hasEmptyState = $body.find('[data-test-subj="datasourceTableEmptyState"]').length > 0; - const hasDataSources = $body.find('[data-test-subj="checkboxSelectAll"]').length > 0; - cy.log('hasEmptyState'); - cy.log(hasEmptyState); - cy.log('hasDataSources'); - cy.log(hasDataSources); - - if (hasEmptyState) { - cy.log('No data sources to delete'); - } else if (hasDataSources) { - cy.log('Need to clean out data sources'); - cy.getElementByTestId('checkboxSelectAll') - .should('exist') - .should('not.be.disabled') - .check({ force: true }); - - cy.getElementByTestId('deleteDataSourceConnections').should('be.visible').click(); - - cy.getElementByTestId('confirmModalConfirmButton').should('be.visible').click(); - } - }); -}); - Cypress.Commands.add( 'setIndexAsDataset', (index, dataSourceName, language, timeFieldName = 'timestamp', finalAction = 'submit') => { @@ -181,6 +84,19 @@ Cypress.Commands.add('setIndexPatternAsDataset', (indexPattern, dataSourceName) ); }); +Cypress.Commands.add('setDataset', (dataset, dataSourceName, type) => { + switch (type) { + case 'INDEX_PATTERN': + cy.setIndexPatternAsDataset(dataset, dataSourceName); + break; + case 'INDEXES': + cy.setIndexAsDataset(dataset, dataSourceName); + break; + default: + throw new Error(`setIndexPatternAsDataset encountered unknown type: ${type}`); + } +}); + Cypress.Commands.add( 'setIndexPatternFromAdvancedSelector', (indexPattern, dataSourceName, language, finalAction = 'submit') => { @@ -211,19 +127,6 @@ Cypress.Commands.add( } ); -Cypress.Commands.add('setDataset', (dataset, dataSourceName, type) => { - switch (type) { - case 'INDEX_PATTERN': - cy.setIndexPatternAsDataset(dataset, dataSourceName); - break; - case 'INDEXES': - cy.setIndexAsDataset(dataset, dataSourceName); - break; - default: - throw new Error(`setIndexPatternAsDataset encountered unknown type: ${type}`); - } -}); - Cypress.Commands.add('setQuickSelectTime', (direction, time, timeUnit) => { cy.getElementByTestId('superDatePickerToggleQuickMenuButton').click(); cy.get('[aria-label="Time tense"]').select(direction); @@ -266,3 +169,117 @@ Cypress.Commands.add('setRelativeTopNavDate', (time, timeUnit) => { cy.getElementByTestId('superDatePickerRelativeDateInputUnitSelector').select(timeUnit); cy.getElementByTestId('querySubmitButton').click(); }); + +// OSD specific commands + +/** + * Creates a new data source connection with basic auth + * It also saves the created data source's id to the alias @DATASOURCE_ID + * @param {Object} options Configuration options for the data source + * @param {string} options.name The name/title for the data source + * @param {string} options.url The endpoint URL for the data source + * @param {string} options.authType The authentication type (e.g. 'no_auth', 'basic_auth', etc.) + * @param {Object} [options.credentials] Optional credentials for auth types that require them + * @param {string} [options.credentials.username] Username for basic auth + * @param {string} [options.credentials.password] Password for basic auth + */ +cy.osd.add('addDataSource', (options) => { + // This function should only run in OSD environment + if (Cypress.env('SOURCE_CODE') !== 'osd') { + return; + } + + const { name, url, authType = 'no_auth', credentials = {} } = options; + + // Visit the create data source page + cy.visit('app/management/opensearch-dashboards/dataSources/create'); + + // Intercept the create request to verify success + cy.intercept('POST', '/api/saved_objects/data-source').as('createDataSourceRequest'); + + // Select OpenSearch card + cy.getElementByTestId('datasource_card_opensearch').click(); + + // Fill in basic info + cy.get('[name="dataSourceTitle"]').type(name); + cy.get('[name="endpoint"]').type(url); + + // Select auth type + cy.getElementByTestId('createDataSourceFormAuthTypeSelect').click(); + cy.get(`button[id="${authType}"]`).click(); + + // Handle credentials if provided and required + if (authType === 'basic_auth' && credentials.username && credentials.password) { + cy.get('[name="username"]').type(credentials.username); + cy.get('[name="password"]').type(credentials.password); + } + + // Submit form. Adding 'force' as sometimes a popover hides the button + cy.getElementByTestId('createDataSourceButton').click({ force: true }); + + // Wait for successful creation + cy.wait('@createDataSourceRequest').then((interception) => { + expect(interception.response.statusCode).to.equal(200); + // save the created data source ID as an alias + cy.wrap(interception.response.body.id).as('DATASOURCE_ID'); + }); + + // Verify redirect to data sources list page + cy.location('pathname', { timeout: 6000 }).should( + 'include', + 'app/management/opensearch-dashboards/dataSources' + ); +}); + +cy.osd.add('deleteDataSourceByName', (dataSourceName) => { + // This function should only run in OSD environment + if (Cypress.env('SOURCE_CODE') !== 'osd') { + return; + } + + // Navigate to the dataSource Management page + cy.visit('app/dataSources'); + + // Find the anchor text corresponding to specified dataSource + cy.get('a').contains(dataSourceName).click(); + + // Delete the dataSource connection + cy.getElementByTestId('editDatasourceDeleteIcon').click(); + cy.getElementByTestId('confirmModalConfirmButton').click(); +}); + +// Deletes all data sources. This command should only be used for convenience during development +// and should never be used in production +cy.osd.add('deleteAllDataSources', () => { + // This function should only run in OSD environment + if (Cypress.env('SOURCE_CODE') !== 'osd') { + return; + } + + cy.visit('app/dataSources'); + cy.waitForLoader(true); + cy.wait(2000); + + cy.get('body').then(($body) => { + const hasEmptyState = $body.find('[data-test-subj="datasourceTableEmptyState"]').length > 0; + const hasDataSources = $body.find('[data-test-subj="checkboxSelectAll"]').length > 0; + cy.log('hasEmptyState'); + cy.log(hasEmptyState); + cy.log('hasDataSources'); + cy.log(hasDataSources); + + if (hasEmptyState) { + cy.log('No data sources to delete'); + } else if (hasDataSources) { + cy.log('Need to clean out data sources'); + cy.getElementByTestId('checkboxSelectAll') + .should('exist') + .should('not.be.disabled') + .check({ force: true }); + + cy.getElementByTestId('deleteDataSourceConnections').should('be.visible').click(); + + cy.getElementByTestId('confirmModalConfirmButton').should('be.visible').click(); + } + }); +}); diff --git a/cypress/utils/apps/query_enhancements/index.d.ts b/cypress/utils/apps/query_enhancements/index.d.ts index e8d415907c9b..7f75280dd545 100644 --- a/cypress/utils/apps/query_enhancements/index.d.ts +++ b/cypress/utils/apps/query_enhancements/index.d.ts @@ -10,25 +10,46 @@ declare namespace Cypress { opts?: { parseSpecialCharSequences?: boolean }, submit?: boolean ): Chainable; + setQueryLanguage(value: 'DQL' | 'Lucene' | 'OpenSearch SQL' | 'PPL'): Chainable; - addDataSource(opts: { - name: string; - url: string; - auth_type?: string; - credentials?: { username: string; password: string }; - }): Chainable; - deleteDataSourceByName(dataSourceName: string): Chainable; - deleteAllDataSources(): Chainable; + setIndexAsDataset( index: string, dataSourceName: string, language?: 'OpenSearch SQL' | 'PPL' ): Chainable; + setIndexPatternAsDataset(indexPattern: string, dataSourceName: string): Chainable; + setDataset( dataset: string, dataSourceName: string, type: 'INDEXES' | 'INDEX_PATTERN' ): Chainable; + + setIndexPatternFromAdvancedSelector( + indexPattern: string, + datraSourceName: string, + language: string, + finalAction: string + ): Chainable; + + setQuickSelectTime(direction: string, time: number, timeUnit: string): Chainable; + + setRelativeTopNavDate(time: number, timeUnit: string): Chainable; + + // osd namespace + osd: { + addDataSource(opts: { + name: string; + url: string; + auth_type?: string; + credentials?: { username: string; password: string }; + }): Chainable; + + deleteDataSourceByName(dataSourceName: string): Chainable; + + deleteAllDataSources(): Chainable; + }; } } diff --git a/cypress/utils/apps/query_enhancements/shared.js b/cypress/utils/apps/query_enhancements/shared.js index 73bd9e0032b9..375d055cd954 100644 --- a/cypress/utils/apps/query_enhancements/shared.js +++ b/cypress/utils/apps/query_enhancements/shared.js @@ -5,7 +5,6 @@ import { DatasetTypes, - DATASOURCE_NAME, END_TIME, INDEX_PATTERN_WITH_TIME, INDEX_WITH_TIME_1, @@ -27,13 +26,6 @@ const getRandomString = () => Math.random().toString(36); export const getRandomizedWorkspaceName = () => `${WORKSPACE_NAME}-${getRandomString().substring(7)}`; -/** - * Returns a randomized datasource name - * @returns {string} - */ -export const getRandomizedDatasourceName = () => - `${DATASOURCE_NAME}-${getRandomString().substring(0, 18)}`; - /** * Callback for generateAllTestConfigurations * @callback GenerateTestConfigurationCallback diff --git a/cypress/utils/apps/workspace/commands.js b/cypress/utils/apps/workspace/commands.js index fd7eeb98b1d8..95b080664402 100644 --- a/cypress/utils/apps/workspace/commands.js +++ b/cypress/utils/apps/workspace/commands.js @@ -11,7 +11,11 @@ Cypress.Commands.add( cy.visit('/app/workspace_list#'); cy.osd.openWorkspaceDashboard(workspaceName); // wait until page loads - cy.getElementByTestId('headerAppActionMenu').should('be.visible'); + if (Cypress.env('SOURCE_CODE') === 'osd') { + cy.getElementByTestId('headerAppActionMenu').should('be.visible'); + } else { + cy.getElementByTestId('breadcrumbs').should('be.visible'); + } } ); diff --git a/cypress/utils/commands.js b/cypress/utils/commands.js index e2287c292181..071779b8a42a 100644 --- a/cypress/utils/commands.js +++ b/cypress/utils/commands.js @@ -2,26 +2,11 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { BASE_PATH } from './constants'; import { TestFixtureHandler } from '../lib/test_fixture_handler'; import initCommandNamespace from './command_namespace'; initCommandNamespace(cy, 'osd'); -// This function does not delete all indices -Cypress.Commands.add('deleteAllIndices', () => { - cy.log('Deleting all indices'); - cy.request( - 'DELETE', - `${Cypress.env('openSearchUrl')}/index*,sample*,opensearch_dashboards*,test*,cypress*` - ); -}); - // --- Typed commands -- Cypress.Commands.add('getElementByTestId', (testId, options = {}) => { @@ -37,15 +22,6 @@ Cypress.Commands.add('getElementsByTestIds', (testIds, options = {}) => { return cy.get(selectors.join(','), options); }); -/** - * Find element from previous chained element with a data-test-subj id containing the testId. - * @param {string} subject DOM object to find within. - * @param {string} testId data-test-subj value. - * @param {object} options get options. Default: {} - * @example - * // returns all DOM elements that has a data-test-subj including the string 'table' - * cy.findElementsByTestIdLike('table') - */ Cypress.Commands.add( 'findElementByTestIdLike', { prevSubject: true }, @@ -54,12 +30,6 @@ Cypress.Commands.add( } ); -/** - * Find element from previous chained element by data-test-subj id. - * @param {string} subject DOM object to find within. - * @param {string} testId data-test-subj value. - * @param {object} options get options. Default: {} - */ Cypress.Commands.add( 'findElementByTestId', { prevSubject: true }, @@ -75,251 +45,6 @@ Cypress.Commands.add('whenTestIdNotFound', (testIds, callbackFn, options = {}) = }); }); -Cypress.Commands.add('createIndex', (index, policyID = null, settings = {}) => { - cy.request('PUT', `${Cypress.env('openSearchUrl')}/${index}`, settings); - if (policyID != null) { - const body = { policy_id: policyID }; - - cy.request('POST', `${Cypress.env('openSearchUrl')}${IM_API.ADD_POLICY_BASE}/${index}`, body); - } -}); - -Cypress.Commands.add('deleteIndex', (indexName, options = {}) => { - cy.request({ - method: 'DELETE', - url: `${Cypress.env('openSearchUrl')}/${indexName}`, - failOnStatusCode: false, - ...options, - }); -}); - -Cypress.Commands.add('getIndices', (index = null, settings = {}) => { - cy.request({ - method: 'GET', - url: `${Cypress.env('openSearchUrl')}/_cat/indices/${index ? index : ''}`, - failOnStatusCode: false, - ...settings, - }); -}); - -// TODO: Impliment chunking -Cypress.Commands.add('bulkUploadDocs', (fixturePath, index) => { - const sendBulkAPIRequest = (ndjson) => { - const url = index - ? `${Cypress.env('openSearchUrl')}/${index}/_bulk` - : `${Cypress.env('openSearchUrl')}/_bulk`; - cy.log('bulkUploadDocs') - .request({ - method: 'POST', - url, - headers: { - 'content-type': 'application/json;charset=UTF-8', - 'osd-xsrf': true, - }, - body: ndjson, - }) - .then((response) => { - if (response.body.errors) { - console.error(response.body.items); - throw new Error('Bulk upload failed'); - } - }); - }; - - cy.fixture(fixturePath, 'utf8').then((ndjson) => { - sendBulkAPIRequest(ndjson); - }); - - cy.request({ - method: 'POST', - url: `${Cypress.env('openSearchUrl')}/_all/_refresh`, - }); -}); - -Cypress.Commands.add('importSavedObjects', (fixturePath, overwrite = true) => { - const sendImportRequest = (ndjson) => { - const url = `/api/saved_objects/_import?${overwrite ? `overwrite=true` : ''}`; - - const formData = new FormData(); - formData.append('file', ndjson, 'savedObject.ndjson'); - - cy.log('importSavedObject') - .request({ - method: 'POST', - url, - headers: { - 'content-type': 'multipart/form-data', - 'osd-xsrf': true, - }, - body: formData, - }) - .then((response) => { - if (response.body.errors) { - console.error(response.body.items); - throw new Error('Import failed'); - } - }); - }; - - cy.fixture(fixturePath) - .then((file) => Cypress.Blob.binaryStringToBlob(file)) - .then((ndjson) => { - sendImportRequest(ndjson); - }); -}); - -Cypress.Commands.add('deleteSavedObject', (type, id, options = {}) => { - const url = `/api/saved_objects/${type}/${id}`; - - return cy.request({ - method: 'DELETE', - url, - headers: { - 'osd-xsrf': true, - }, - failOnStatusCode: false, - ...options, - }); -}); - -Cypress.Commands.add('deleteSavedObjectByType', (type, search) => { - const searchParams = new URLSearchParams({ - fields: 'id', - type, - }); - - if (search) { - searchParams.set('search', search); - } - - const url = `/api/opensearch-dashboards/management/saved_objects/_find?${searchParams.toString()}`; - - return cy.request(url).then((response) => { - console.log('response', response); - response.body.saved_objects.map(({ type, id }) => { - cy.deleteSavedObject(type, id); - }); - }); -}); - -// TODO: we should really make this a helper function that if the data source does not exist, it creates it so take what you have for the dataset selector spec and move it here -Cypress.Commands.add('ifDataSourceExists', (search) => { - const searchParams = new URLSearchParams({ - fields: 'id', - type: 'data-source', - }); - - if (search) { - searchParams.set('search', search); - } - - const url = `/api/opensearch-dashboards/management/saved_objects/_find?${searchParams.toString()}`; - - return cy.request(url).then((response) => { - console.log('response', response); - return response.body.saved_objects.length > 0; - }); -}); - -Cypress.Commands.add('createIndexPattern', (id, attributes, header = {}) => { - const url = `/api/saved_objects/index-pattern/${id}`; - - cy.request({ - method: 'POST', - url, - headers: { - 'content-type': 'application/json;charset=UTF-8', - 'osd-xsrf': true, - ...header, - }, - body: JSON.stringify({ - attributes, - references: [], - }), - }); -}); - -Cypress.Commands.add('createDashboard', (attributes = {}, headers = {}) => { - const url = '/api/saved_objects/dashboard'; - - cy.request({ - method: 'POST', - url, - headers: { - 'content-type': 'application/json;charset=UTF-8', - 'osd-xsrf': true, - ...headers, - }, - body: JSON.stringify({ - attributes, - }), - }); -}); - -Cypress.Commands.add('changeDefaultTenant', (attributes, header = {}) => { - const url = Cypress.env('openSearchUrl') + '/_plugins/_security/api/tenancy/config'; - - cy.request({ - method: 'PUT', - url, - headers: { - 'content-type': 'application/json;charset=UTF-8', - 'osd-xsrf': true, - ...header, - }, - body: JSON.stringify(attributes), - }); -}); - -Cypress.Commands.add('deleteIndexPattern', (id, options = {}) => - cy.deleteSavedObject('index-pattern', id, options) -); - -Cypress.Commands.add('setAdvancedSetting', (changes) => { - const url = '/api/opensearch-dashboards/settings'; - cy.log('setAdvancedSetting') - .request({ - method: 'POST', - url, - qs: Cypress.env('SECURITY_ENABLED') - ? { - security_tenant: CURRENT_TENANT.defaultTenant, - } - : {}, - headers: { - 'content-type': 'application/json;charset=UTF-8', - 'osd-xsrf': true, - }, - body: { changes }, - }) - .then((response) => { - if (response.body.errors) { - console.error(response.body.items); - throw new Error('Setting advanced setting failed'); - } - }); -}); - -// type: logs, ecommerce, flights -Cypress.Commands.add('loadSampleData', (type) => { - cy.request({ - method: 'POST', - headers: { 'osd-xsrf': 'opensearch-dashboards' }, - url: `${BASE_PATH}/api/sample_data/${type}`, - }); -}); - -Cypress.Commands.add('fleshTenantSettings', () => { - if (Cypress.env('SECURITY_ENABLED')) { - // Use xhr request is good enough to flesh tenant - cy.request({ - url: `${BASE_PATH}/app/home?security_tenant=${CURRENT_TENANT.defaultTenant}`, - method: 'GET', - failOnStatusCode: false, - }); - } -}); - Cypress.Commands.add('deleteWorkspace', (workspaceName) => { cy.wait(3000); cy.getElementByTestId('workspace-detail-delete-button').should('be.visible').click(); @@ -329,6 +54,8 @@ Cypress.Commands.add('deleteWorkspace', (workspaceName) => { cy.contains(/successfully/); }); +// OSD-specific commands + cy.osd.add('createInitialWorkspaceWithDataSource', (dataSourceTitle, workspaceName) => { cy.intercept('POST', '/api/workspaces').as('createWorkspaceInterception'); cy.getElementByTestId('workspace-initial-card-createWorkspace-button') @@ -369,7 +96,26 @@ cy.osd.add('openWorkspaceDashboard', (workspaceName) => { .click(); }); -Cypress.Commands.add('setupTestData', (endpoint, mappingFiles, dataFiles) => { +cy.osd.add('deleteIndex', (indexName, options = {}) => { + // This function should only run in OSD environment + if (Cypress.env('SOURCE_CODE') !== 'osd') { + return; + } + + cy.request({ + method: 'DELETE', + url: `${Cypress.env('openSearchUrl')}/${indexName}`, + failOnStatusCode: false, + ...options, + }); +}); + +cy.osd.add('setupTestData', (endpoint, mappingFiles, dataFiles) => { + // This function should only run in OSD environment + if (Cypress.env('SOURCE_CODE') !== 'osd') { + return; + } + if (!Array.isArray(mappingFiles) || !Array.isArray(dataFiles)) { throw new Error('Both mappingFiles and dataFiles must be arrays'); } diff --git a/cypress/utils/helpers.js b/cypress/utils/helpers.js index 9624e8b408a6..2098540eb102 100644 --- a/cypress/utils/helpers.js +++ b/cypress/utils/helpers.js @@ -3,29 +3,38 @@ * SPDX-License-Identifier: Apache-2.0 */ -export const apiRequest = (url, method = 'POST', body = undefined, qs = undefined) => - cy.request({ - method: method, - failOnStatusCode: false, - url: url, - headers: { - 'content-type': 'application/json', - 'osd-xsrf': true, - }, - body: body, - qs: qs, - }); +/** + * Callback for prepareTestSuite + * @callback PrepareTestSuiteCallback + * @returns {void} + */ + +const loginMethods = [ + { + name: 'IAM Auth Session', + method: 'iamAuthLogin', + }, +]; + +/** + Sets up the test suite depending on the codebase + * @param {string} testSuiteName - name of test suite + * @param {PrepareTestSuiteCallback} runTestSuiteCallback - function that run's the test suite's tests + */ +export const prepareTestSuite = (testSuiteName, runTestSuiteCallback) => { + if (Cypress.env('SOURCE_CODE') === 'osd') { + runTestSuiteCallback(); + } else { + loginMethods.forEach(({ name, method }) => { + describe(`${testSuiteName} Test with ${name}`, () => { + beforeEach(() => { + cy.session(name, () => { + cy[method](); + }); + }); -export const devToolsRequest = (url, method = 'POST', body = undefined, qs = undefined) => - cy.request({ - method: 'POST', - form: false, - failOnStatusCode: false, - url: encodeURI(`api/console/proxy?path=${url}&method=${method}`), - headers: { - 'content-type': 'application/json;charset=UTF-8', - 'osd-xsrf': true, - }, - body: body, - qs: qs, - }); + runTestSuiteCallback(); + }); + }); + } +}; diff --git a/cypress/utils/index.d.ts b/cypress/utils/index.d.ts index f6696c8c16e2..309acc087715 100644 --- a/cypress/utils/index.d.ts +++ b/cypress/utils/index.d.ts @@ -6,24 +6,15 @@ declare namespace Cypress { interface Chainable { /** - * Call a function when an element with a test id cannot be found - * @example - * cy.whenTestIdNotFound(['query', 'puery'], () => {...}) - */ - whenTestIdNotFound( - testIds: string | string[], - callbackFn: void, - options?: Partial - ): Chainable; - /** - * Get elements by their test ids + * Get an element by its test id * @example - * cy.getElementsByTestIds(['query', 'puery']) + * cy.getElementByTestId('query') */ - getElementsByTestIds( - testIds: string | string[], + getElementByTestId( + testId: string, options?: Partial ): Chainable; + /** * Get an element which contains testId * @example @@ -33,13 +24,14 @@ declare namespace Cypress { testId: string, options?: Partial ): Chainable; + /** - * Get an element by its test id + * Get elements by their test ids * @example - * cy.getElementByTestId('query') + * cy.getElementsByTestIds(['query', 'puery']) */ - getElementByTestId( - testId: string, + getElementsByTestIds( + testIds: string | string[], options?: Partial ): Chainable; @@ -69,127 +61,22 @@ declare namespace Cypress { ): Chainable; /** - * Create an index - * @example - * cy.createIndex('indexID') - * cy.createIndex('indexID', 'policy') - */ - createIndex(index: string, policyID?: string, settings?: any): Chainable; - - /** - * Delete an index - * @example - * cy.deleteIndex('indexID') - */ - deleteIndex(index: string): Chainable; - - /** - * Bulk upload NDJSON fixture data - * @example - * cy.bulkUploadDocs('plugins/test/test_data.txt') - */ - bulkUploadDocs( - fixturePath: string, - index: string - // options?: Partial - ): Chainable; - - /** - * Import saved objects - * @example - * cy.importSavedObject('plugins/test/exported_data.ndjson') - */ - importSavedObjects(fixturePath: string, overwrite?: boolean): Chainable; - - /** - * Delete a saved object - * @example - * cy.deleteSavedObject('index-pattern', 'id') - */ - deleteSavedObject(type: string, id: string): Chainable; - - /** - * Test if data source exists - * @example - * cy.ifDataSourceExists('data-source') - */ - ifDataSourceExists(search: string): Chainable; - - /** - * Delete all saved objects of a particular type - * Optionally, narrow down the results using search - * @example - * cy.deleteSavedObjectByType('index-pattern') - * cy.deleteSavedObjectByType('index-pattern', 'search string') - */ - deleteSavedObjectByType(type: string, search?: string): Chainable; - - /** - * Adds an index pattern - * @example - * cy.createIndexPattern('patterId', { title: 'patt*', timeFieldName: 'timestamp' }) - */ - createIndexPattern( - id: string, - attributes: { - title: string; - timeFieldName?: string; - [key: string]: any; - }, - header: string - ): Chainable; - - /** - * Adds a dashboard - * @example - * cy.createDashboard({ title: 'My dashboard'}) - */ - createDashboard( - attributes: { - title: string; - [key: string]: any; - }, - headers?: { - [key: string]: any; - } - ): Chainable; - - /** - * Changes the Default tenant for the domain. + * Call a function when an element with a test id cannot be found * @example - * cy.changeDefaultTenant({multitenancy_enabled: true, private_tenant_enabled: true, default_tenant: tenantName, }); + * cy.whenTestIdNotFound(['query', 'puery'], () => {...}) */ - changeDefaultTenant( - attributes: { - multitenancy_enabled: boolean; - private_tenant_enabled: boolean; - default_tenant: string; - } - // header: string, - // default_tenant: string + whenTestIdNotFound( + testIds: string | string[], + callbackFn: void, + options?: Partial ): Chainable; /** - * Delete an index pattern - * @example - * cy.createIndexPattern('patterId') - */ - deleteIndexPattern(id: string): Chainable; - - /** - * Set advanced setting values - * tip: setting the value to null set's it to its default value + * Deletes a workspace * @example - * cy.setAdvancedSetting({ 'visualize:enableLabs' : true }) + * cy.deleteWorkspace('workspace-name'); */ - setAdvancedSetting(changes: { [key: string]: any }): Chainable; - - /** - * Performs drag and drop action - * @example - * cy.get('sourceSelector').drag('targetSelector') - */ - drag(targetSelector: string): Chainable; + deleteWorkspace(workspaceName: string): Chainable; // osd namespace osd: { @@ -206,6 +93,22 @@ declare namespace Cypress { * Opens workspace dashboard */ openWorkspaceDashboard(workspaceName: string): Chainable; + + /** + * Delete an index + * @example + * cy.deleteIndex('indexID') + */ + deleteIndex(index: string): Chainable; + + /** + * Sets up test data + */ + setupTestData( + endpoint: string, + mappingFiles: string[], + dataFiles: string[] + ): Chainable; }; } }