Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TESTID-58,59] Inspect functionality for Discover and Visualizations #9292

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
2 changes: 2 additions & 0 deletions changelogs/fragments/9292.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
test:
- Add cypress integration test for the inspect functionality in the Discover and Dashboards pages. ([#9292](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/9292))
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
INDEX_PATTERN_WITH_TIME,
INDEX_WITH_TIME_1,
} from '../../../../../utils/apps/constants';
import * as dataExplorer from '../../../../../utils/apps/query_enhancements/field_display_filtering.js';
import * as dataExplorer from '../../../../../utils/apps/query_enhancements/doc_table.js';
import { SECONDARY_ENGINE, BASE_PATH } from '../../../../../utils/constants';
import { NEW_SEARCH_BUTTON } from '../../../../../utils/dashboards/data_explorer/elements.js';
import {
Expand All @@ -32,7 +32,7 @@ describe('filter for value spec', () => {
// Add data source
cy.addDataSource({
name: DATASOURCE_NAME,
url: `${SECONDARY_ENGINE.url}`,
url: 'http://opensearch-node:9200/',
ArgusLi marked this conversation as resolved.
Show resolved Hide resolved
authType: 'no_auth',
});
// Create workspace
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import {
INDEX_PATTERN_WITH_TIME,
INDEX_WITH_TIME_1,
QueryLanguages,
} from '../../../../../utils/apps/constants.js';
import * as docTable from '../../../../../utils/apps/query_enhancements/doc_table.js';
import { SECONDARY_ENGINE, BASE_PATH } from '../../../../../utils/constants.js';
import { NEW_SEARCH_BUTTON } from '../../../../../utils/dashboards/data_explorer/elements.js';
import {
generateAllTestConfigurations,
getRandomizedWorkspaceName,
getRandomizedDatasourceName,
setDatePickerDatesAndSearchIfRelevant,
} from '../../../../../utils/apps/query_enhancements/shared.js';
import {
generateInspectTestConfiguration,
getFlattenedFieldsWithValue,
verifyVisualizationsWithNoInspectOption,
verifyVisualizationsWithInspectOption,
} from '../../../../../utils/apps/query_enhancements/inspect.js';

const workspaceName = getRandomizedWorkspaceName();
const datasourceName = getRandomizedDatasourceName();

const NUMBER_OF_VISUALIZATIONS_IN_FLIGHTS_DASHBOARD = 17;

describe('inspect 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: datasourceName,
url: SECONDARY_ENGINE.url,
authType: 'no_auth',
});
// Create workspace
cy.deleteWorkspaceByName(workspaceName);
cy.visit('/app/home');
cy.createInitialWorkspaceWithDataSource(datasourceName, workspaceName);
cy.wait(2000);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think we need this wait here

cy.createWorkspaceIndexPatterns({
workspaceName: workspaceName,
indexPattern: INDEX_PATTERN_WITH_TIME.replace('*', ''),
timefieldName: 'timestamp',
indexPatternHasTimefield: true,
dataSource: datasourceName,
isEnhancement: true,
});

cy.navigateToWorkSpaceSpecificPage({
url: BASE_PATH,
workspaceName: workspaceName,
page: 'discover',
isEnhancement: true,
});
cy.getElementByTestId(NEW_SEARCH_BUTTON).click();
});

afterEach(() => {
cy.deleteWorkspaceByName(workspaceName);
cy.deleteDataSourceByName(datasourceName);
// TODO: Modify deleteIndex to handle an array of index and remove hard code
cy.deleteIndex(INDEX_WITH_TIME_1);
});

generateAllTestConfigurations(generateInspectTestConfiguration).forEach((config) => {
it(`should inspect and validate the first row data for ${config.testName}`, () => {
cy.setDataset(config.dataset, datasourceName, config.datasetType);
cy.setQueryLanguage(config.language);
setDatePickerDatesAndSearchIfRelevant(config.language);

cy.intercept('POST', '**/search/*').as('docTablePostRequest');

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.
docTable.toggleDocTableRow(0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this toggleDocTableRow is to // To ensure it waits until a full table is loaded into the DOM, instead of a bug where table only has 1 hit.?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment is for cy.getElementByTestId('docTable').get('tbody tr').should('have.length.above', 3);. I can move the comment above the line instead if it makes it more legible?


cy.wait('@docTablePostRequest').then((interceptedDocTableResponse) => {
const flattenedFieldsWithValues = getFlattenedFieldsWithValue(
interceptedDocTableResponse,
config.language
);

cy.log(flattenedFieldsWithValues);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this cy.log?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, should have cleaned it up.


for (const [key, value] of Object.entries(flattenedFieldsWithValues)) {
// For SQL and PPL, this number is not accurate.
if (
key === 'event_sequence_number' &&
(config.language === QueryLanguages.SQL.name ||
config.language === QueryLanguages.PPL.name)
) {
cy.log(`Skipped for ${key}`);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to keep this here. These are skipped because of a bug (#9305), and I want to make it clear they are skipped. I'll add the link to the bug to the comment as well.

continue;
}
docTable.getExpandedDocTableRowFieldValue(key).should('have.text', value);
}
});
});
});

it('should test visualizations inspect', () => {
cy.navigateToWorkSpaceSpecificPage({
url: BASE_PATH,
workspaceName: workspaceName,
page: 'import_sample_data',
isEnhancement: true,
});

cy.getElementByTestId('addSampleDataSetflights').click();
cy.getElementByTestId('sampleDataSetInstallToast').should('exist');

cy.navigateToWorkSpaceSpecificPage({
url: BASE_PATH,
workspaceName: workspaceName,
page: 'dashboards',
isEnhancement: true,
});

cy.getElementByTestIdLike(
'dashboardListingTitleLink-[Flights]-Global-Flight-Dashboard'
).click();

cy.getElementByTestId('visualizationLoader').should(
'have.length',
NUMBER_OF_VISUALIZATIONS_IN_FLIGHTS_DASHBOARD
);

verifyVisualizationsWithNoInspectOption();
verifyVisualizationsWithInspectOption();
});
});
199 changes: 199 additions & 0 deletions cypress/utils/apps/query_enhancements/doc_table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

/**
* Get specific row of DocTable.
* @param {number} rowNumber Integer starts from 0 for the first row
*/
export const getDocTableRow = (rowNumber) => {
return cy.getElementByTestId('docTable').get('tbody tr').eq(rowNumber);
};

/**
* Get specific field of DocTable.
* @param {number} columnNumber Integer starts from 0 for the first column
* @param {number} rowNumber Integer starts from 0 for the first row
*/
export const getDocTableField = (columnNumber, rowNumber) => {
return getDocTableRow(rowNumber).findElementByTestId('docTableField').eq(columnNumber);
};

/**
* find all Rows in Doc Table Field Expanded Document.
* @param expandedDocument cypress representation of the Doc Table Field Expanded Document
*/
export const findExpandedDocTableRows = (expandedDocument) => {
return expandedDocument.findElementByTestIdLike('tableDocViewRow-');
};

/**
* Get the "expandedDocumentRowNumber"th row from the expanded document from the "docTableRowNumber"th row of the DocTable.
* @param {number} docTableRowNumber Integer starts from 0 for the first row
* @param {number} expandedDocumentRowNumber Integer starts from 0 for the first row
* @example
* // returns the first row from the expanded document from the second row of the DocTable.
* getExpandedDocTableRow(1, 0);
*/
export const getExpandedDocTableRow = (docTableRowNumber, expandedDocumentRowNumber) => {
return findExpandedDocTableRows(getDocTableRow(docTableRowNumber + 1)).eq(
expandedDocumentRowNumber
);
};

/**
* Get the value for the "expandedDocumentRowNumber"th row from the expanded document from the "docTableRowNumber"th row of the DocTable.
* @param {number} docTableRowNumber Integer starts from 0 for the first row
* @param {number} expandedDocumentRowNumber Integer starts from 0 for the first row
* @example
* // returns the value of the field from the first row from the expanded document from the second row of the DocTable.
* getExpandedDocTableRowValue(1, 0);
*/
export const getExpandedDocTableRowValue = (docTableRowNumber, expandedDocumentRowNumber) => {
return getExpandedDocTableRow(docTableRowNumber, expandedDocumentRowNumber)
.find(`[data-test-subj*="tableDocViewRow-"]`)
.find('span');
};

/**
* Get the field name for the "expandedDocumentRowNumber"th row from the expanded document from the "docTableRowNumber"th row of the DocTable.
* @param {number} docTableRowNumber Integer starts from 0 for the first row
* @param {number} expandedDocumentRowNumber Integer starts from 0 for the first row
* @example
* // returns the name of the field from the first row from the expanded document from the second row of the DocTable.
* getExpandedDocTableRowFieldName(1, 0);
*/
export const getExpandedDocTableRowFieldName = (docTableRowNumber, expandedDocumentRowNumber) => {
return getExpandedDocTableRow(docTableRowNumber, expandedDocumentRowNumber)
.find('td')
.eq(1) // Field name is in the second column.
.find('span[class*="textTruncate"]');
};

/**
* Get the value for the row with the expandedDocumentField from the expanded document.
* @param {string} expandedDocumentField Field name
* @example
* // returns the value of the 'AvgTicketPrice' field from the expanded document.
* getExpandedDocTableRowFieldName(1, 'AvgTicketPrice');
*/
export const getExpandedDocTableRowFieldValue = (expandedDocumentField) => {
return cy.getElementByTestId(`tableDocViewRow-${expandedDocumentField}-value`);
};

/**
* Select a language in the Dataset Selector for Index
* @param {string} datasetLanguage Index supports "OpenSearch SQL" and "PPL"
*/
export const selectIndexDatasetLanguage = (datasetLanguage) => {
cy.getElementByTestId('advancedSelectorLanguageSelect').select(datasetLanguage);
cy.getElementByTestId('advancedSelectorTimeFieldSelect').select('timestamp');
cy.getElementByTestId('advancedSelectorConfirmButton').click();
};

/**
* Select an index dataset.
* @param {string} indexClusterName Name of the cluster to be used for the Index.
* @param {string} indexName Name of the index dataset to be used.
* @param {string} datasetLanguage Index supports "OpenSearch SQL" and "PPL".
*/
export const selectIndexDataset = (indexClusterName, indexName, datasetLanguage) => {
cy.getElementByTestId('datasetSelectorButton').click();
cy.getElementByTestId('datasetSelectorAdvancedButton').click();
cy.getElementByTestId('datasetExplorerWindow').contains('Indexes').click();
cy.getElementByTestId('datasetExplorerWindow').contains(indexClusterName).click();
cy.getElementByTestId('datasetExplorerWindow').contains(indexName).click();
cy.getElementByTestId('datasetSelectorNext').click();
selectIndexDatasetLanguage(datasetLanguage);
};

/**
* Select a language in the Dataset Selector for Index Pattern
* @param {string} datasetLanguage Index Pattern supports "DQL", "Lucene", "OpenSearch SQL" and "PPL"
*/
export const selectIndexPatternDatasetLanguage = (datasetLanguage) => {
cy.getElementByTestId('advancedSelectorLanguageSelect').select(datasetLanguage);
cy.getElementByTestId('advancedSelectorConfirmButton').click();
};

/**
* Select an index pattern dataset.
* @param {string} indexPatternName Name of the index pattern to be used.
* @param {string} datasetLanguage Index Pattern supports "DQL", "Lucene", "OpenSearch SQL" and "PPL"
*/
export const selectIndexPatternDataset = (indexPatternName, datasetLanguage) => {
cy.getElementByTestId('datasetSelectorButton').click();
cy.getElementByTestId('datasetSelectorAdvancedButton').click();
cy.getElementByTestId('datasetExplorerWindow').contains('Index Patterns').click();
cy.getElementByTestId('datasetExplorerWindow').contains(indexPatternName).click();
cy.getElementByTestId('datasetSelectorNext').click();
selectIndexPatternDatasetLanguage(datasetLanguage);
};

/**
* Toggle expansion of row rowNumber of Doc Table.
* @param {number} rowNumber rowNumber of Doc Table starts at 0 for row 1.
*/
export const toggleDocTableRow = (rowNumber) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe call it expandDocTableRow is more straightforward?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer that name, but it can also shrink the tableRow, so I think toggle is a better word.

getDocTableRow(rowNumber).within(() => {
cy.getElementByTestId('docTableExpandToggleColumn').find('button').click();
});
};

/**
* Check the Doc Table rowNumberth row's Filter buttons filters the correct value.
* @param {number} rowNumber Doc table row number to check (First row is row 0)
* @param {string} filterElement data-test-sub element for filter.
* @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
* @param {boolean} shouldMatch boolean to determine if same rowNumber text should match after filter is applied
* @example verifyDocTableFilterAction(0, 'filterForValue', '10,000', '1', true)
*/
export const verifyDocTableFilterAction = (
rowNumber,
filterElement,
expectedQueryHitsWithoutFilter,
expectedQueryHitsAfterFilterApplied,
shouldMatch
) => {
getDocTableField(0, rowNumber).then(($field) => {
const shouldText = shouldMatch ? 'have.text' : 'not.have.text';

const filterFieldText = $field.find('span span').text();
$field.find(`[data-test-subj="${filterElement}"]`).click();
// Verify pill text
cy.getElementByTestId('globalFilterLabelValue', {
timeout: 10000,
}).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.
getDocTableField(0, rowNumber).find('span span').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 Toggle Column button has intended behavior.
*/
export const verifyDocTableFirstExpandedFieldFirstRowToggleColumnButtonHasIntendedBehavior = () => {
getExpandedDocTableRowFieldName(0, 0).then(($expandedDocumentRowFieldText) => {
const fieldText = $expandedDocumentRowFieldText.text();
getExpandedDocTableRow(0, 0).within(() => {
cy.getElementByTestId('docTableHeader-' + fieldText).should('not.exist');
cy.getElementByTestId('toggleColumnButton').click();
});
cy.getElementByTestId('fieldList-selected').within(() => {
cy.getElementByTestId('field-' + fieldText).should('exist');
});
cy.getElementByTestId('docTableHeader-' + fieldText).should('exist');
cy.getElementByTestId('fieldToggle-' + fieldText).click();
cy.getElementByTestId('fieldList-selected').within(() => {
cy.getElementByTestId('field-' + fieldText).should('not.exist');
});
cy.getElementByTestId('docTableHeader-' + fieldText).should('not.exist');
});
};
Loading
Loading