mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Dataset Quality] Fix flaky tests for Dataset Quality Summary (#184640)
## Summary As part of the PR, i have rewritten 4 of the test files for both serverless and stateful for Dataset Quality Project - `/dataset_quality/dataset_quality_summary.ts` - Closes - https://github.com/elastic/kibana/issues/178874 - Closes - https://github.com/elastic/kibana/issues/178884 - Closes - https://github.com/elastic/kibana/issues/186354 - `/dataset_quality/dataset_quality_table.ts` - Closes - https://github.com/elastic/kibana/issues/183940 (Possibly, not guaranteed) - Closes - https://github.com/elastic/kibana/issues/182353 - `/dataset_quality/dataset_quality_table_filters.ts` - Closes - https://github.com/elastic/kibana/issues/183861 - Closes - https://github.com/elastic/kibana/issues/182320 - Closes - https://github.com/elastic/kibana/issues/184852 - `/dataset_quality/dataset_quality_flyout.ts` - Closes - https://github.com/elastic/kibana/issues/184438 - Closes - https://github.com/elastic/kibana/issues/183851 - Closes - https://github.com/elastic/kibana/issues/183771 - Closes - https://github.com/elastic/kibana/issues/183525 - Closes - https://github.com/elastic/kibana/issues/183312 - Closes - https://github.com/elastic/kibana/issues/183129 - Closes - https://github.com/elastic/kibana/issues/182154 ## Why are the tests re-written - Most of the `it` were loading its own data, which add 2 problems, 1. Makes our tests slower, 2. Data cleanup becomes challenging. Now the tests simply load one master set of data and all the Functional tests can be executed on that master data. This makes our tests leaner and more functional. - Every `it` resets the page state after the tests. Like when a `it` blocks opens the Flyout, it should also close the flyout which was missing. In order to refresh the page, the `navigate` API was used, which is not good. Navigate API should only be used once to navigate to the page in the starting and then refresh events should be used if a refresh is required, or the action should be un-done in order to get the same state as previous. For ex - Sorting make update the state of the whole page. At the end of the sorting test, sorting should be reset. With these changes `it` block now only focus on pure functional testing. This means the `it` blocks can be moved around, skipped without impacting other tests - We had too much of generic tests, which could be combined into 1 `it` block and be checked together. Idea to split 1 `it` block into another is when we test for a completely different scenario. For eg - Writing a single `it` for testing different columns of a table is much more cleaner than multiple `it` for testing various columns of the same table. - Removed usage of `retry.try`. (Personal Opinion, please read it with a pinch of salt) - The retry service seems like an escape hatch (read workaround) when we don't have control over the rendering of UI elements. Better alternative is to use `retry.tryforTime` as the last resort. Also the only time i found using the whole `retry` package was when we use the `browser` package for getting URL value of refreshing page. I cannot prove yet the problem with the `browser` package but somehow it breaks the sync behaviour causing elements to be not available hence requiring these retries. I have removed `browser.refresh` completely from our code in favour of better refresh handlers using DateTimePicker Refresh action Linked Issued - https://github.com/elastic/kibana/issues/184145
This commit is contained in:
parent
153ec668e3
commit
103e619c0a
13 changed files with 1007 additions and 1602 deletions
|
@ -22,13 +22,15 @@ export function FieldsList({
|
|||
title,
|
||||
fields,
|
||||
actionsMenu: ActionsMenu,
|
||||
dataTestSubj = `datasetQualityFlyoutFieldsList-${title.toLowerCase().split(' ').join('_')}`,
|
||||
}: {
|
||||
title: string;
|
||||
fields: Array<{ fieldTitle: string; fieldValue: ReactNode; isLoading: boolean }>;
|
||||
actionsMenu?: ReactNode;
|
||||
dataTestSubj?: string;
|
||||
}) {
|
||||
return (
|
||||
<EuiPanel hasBorder grow={false}>
|
||||
<EuiPanel hasBorder grow={false} data-test-subj={dataTestSubj}>
|
||||
<EuiFlexGroup justifyContent="spaceBetween">
|
||||
<EuiTitle size="s">
|
||||
<span>{title}</span>
|
||||
|
|
|
@ -61,6 +61,7 @@ export async function getDataStreamDetails({
|
|||
await getDataStreamsStats({
|
||||
esClient,
|
||||
dataStreams: [dataStream],
|
||||
sizeStatsAvailable,
|
||||
})
|
||||
).items[0]?.lastActivity
|
||||
: undefined;
|
||||
|
|
|
@ -191,6 +191,7 @@ export function createDegradedFieldsRecord({
|
|||
|
||||
export const datasetNames = ['synth.1', 'synth.2', 'synth.3'];
|
||||
export const defaultNamespace = 'default';
|
||||
export const productionNamespace = 'production';
|
||||
|
||||
// Logs Data logic
|
||||
const MESSAGE_LOG_LEVELS: MessageWithLevel[] = [
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
datasetNames,
|
||||
getInitialTestLogs,
|
||||
getLogsForDataset,
|
||||
productionNamespace,
|
||||
} from './data';
|
||||
|
||||
const integrationActions = {
|
||||
|
@ -32,22 +33,70 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
const retry = getService('retry');
|
||||
const browser = getService('browser');
|
||||
const to = '2024-01-01T12:00:00.000Z';
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const apacheIntegrationId = 'apache';
|
||||
const apachePkg = {
|
||||
name: 'apache',
|
||||
version: '1.14.0',
|
||||
};
|
||||
|
||||
describe('Dataset quality flyout', () => {
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/182154
|
||||
// Added this sub describe block so that the existing flaky tests can be skipped and new ones can be added in the other describe block
|
||||
describe.skip('Other dataset quality flyout tests', () => {
|
||||
before(async () => {
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
const bitbucketDatasetName = 'atlassian_bitbucket.audit';
|
||||
const bitbucketDatasetHumanName = 'Bitbucket Audit Logs';
|
||||
const bitbucketPkg = {
|
||||
name: 'atlassian_bitbucket',
|
||||
version: '1.14.0',
|
||||
};
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await PageObjects.observabilityLogsExplorer.removeInstalledPackages();
|
||||
});
|
||||
const degradedDatasetName = datasetNames[2];
|
||||
|
||||
it('opens the flyout for the right dataset', async () => {
|
||||
describe('Flyout', () => {
|
||||
before(async () => {
|
||||
// Install Apache Integration and ingest logs for it
|
||||
await PageObjects.observabilityLogsExplorer.installPackage(apachePkg);
|
||||
|
||||
// Install Bitbucket Integration (package which does not has Dashboards) and ingest logs for it
|
||||
await PageObjects.observabilityLogsExplorer.installPackage(bitbucketPkg);
|
||||
|
||||
await synthtrace.index([
|
||||
// Ingest basic logs
|
||||
getInitialTestLogs({ to, count: 4 }),
|
||||
// Ingest Degraded Logs
|
||||
createDegradedFieldsRecord({
|
||||
to: new Date().toISOString(),
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
// Index 15 logs for `logs-apache.access` dataset
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 15,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: productionNamespace,
|
||||
}),
|
||||
// Index degraded docs for Apache integration
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 1,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: productionNamespace,
|
||||
isMalformed: true,
|
||||
}),
|
||||
// Index logs for Bitbucket integration
|
||||
getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName }),
|
||||
]);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await PageObjects.observabilityLogsExplorer.uninstallPackage(apachePkg);
|
||||
await PageObjects.observabilityLogsExplorer.uninstallPackage(bitbucketPkg);
|
||||
await synthtrace.clean();
|
||||
});
|
||||
|
||||
describe('open flyout', () => {
|
||||
it('should open the flyout for the right dataset', async () => {
|
||||
const testDatasetName = datasetNames[1];
|
||||
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
@ -55,42 +104,12 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutTitle
|
||||
);
|
||||
});
|
||||
|
||||
it('shows the correct last activity', async () => {
|
||||
const testDatasetName = datasetNames[0];
|
||||
|
||||
// Update last activity for the dataset
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to: new Date().toISOString(), count: 1, dataset: testDatasetName })
|
||||
);
|
||||
await PageObjects.datasetQuality.refreshTable();
|
||||
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
|
||||
const testDatasetRowIndex = datasetNameColCellTexts.findIndex(
|
||||
(dName: string) => dName === testDatasetName
|
||||
);
|
||||
|
||||
const lastActivityText = (await cols['Last Activity'].getCellTexts())[testDatasetRowIndex];
|
||||
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
||||
const lastActivityTextExists = await PageObjects.datasetQuality.doestTextExistInFlyout(
|
||||
lastActivityText,
|
||||
`[data-test-subj=${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutFieldValue}]`
|
||||
);
|
||||
|
||||
expect(lastActivityTextExists).to.eql(true);
|
||||
});
|
||||
|
||||
it('reflects the breakdown field state in url', async () => {
|
||||
const testDatasetName = datasetNames[0];
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const breakdownField = 'service.name';
|
||||
await PageObjects.datasetQuality.selectBreakdownField(breakdownField);
|
||||
|
@ -109,25 +128,25 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
const currentUrl = await browser.getCurrentUrl();
|
||||
expect(currentUrl).to.not.contain('breakdownField');
|
||||
});
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the integration details', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const apacheIntegrationId = 'apache';
|
||||
describe('integrations', () => {
|
||||
it('should hide the integration section for non integrations', async () => {
|
||||
const testDatasetName = datasetNames[1];
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
await testSubjects.missingOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors
|
||||
.datasetQualityFlyoutFieldsListIntegrationDetails
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should shows the integration section for integrations', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const integrationNameElements = await PageObjects.datasetQuality.getFlyoutElementsByText(
|
||||
|
@ -135,175 +154,17 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
apacheIntegrationId
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors
|
||||
.datasetQualityFlyoutFieldsListIntegrationDetails
|
||||
);
|
||||
|
||||
expect(integrationNameElements.length).to.eql(1);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('goes to log explorer page when open button is clicked', async () => {
|
||||
const testDatasetName = datasetNames[2];
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
||||
await (await PageObjects.datasetQuality.getFlyoutLogsExplorerButton()).click();
|
||||
|
||||
// Confirm dataset selector text in observability logs explorer
|
||||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.eql(testDatasetName);
|
||||
});
|
||||
|
||||
it('shows summary KPIs', async () => {
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const summary = await PageObjects.datasetQuality.parseFlyoutKpis();
|
||||
expect(summary).to.eql({
|
||||
docsCountTotal: '0',
|
||||
size: '0.0 B',
|
||||
services: '0',
|
||||
hosts: '0',
|
||||
degradedDocs: '0',
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the updated KPIs', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const summaryBefore = await PageObjects.datasetQuality.parseFlyoutKpis();
|
||||
|
||||
// Set time range to 3 days ago
|
||||
const flyoutBodyContainer = await testSubjects.find(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutBody
|
||||
);
|
||||
await PageObjects.datasetQuality.setDatePickerLastXUnits(flyoutBodyContainer, 3, 'd');
|
||||
|
||||
// Index 2 doc 2 days ago
|
||||
const time2DaysAgo = Date.now() - 2 * 24 * 60 * 60 * 1000;
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: time2DaysAgo,
|
||||
count: 2,
|
||||
dataset: apacheAccessDatasetName,
|
||||
isMalformed: false,
|
||||
})
|
||||
);
|
||||
|
||||
// Index 5 degraded docs 2 days ago
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: time2DaysAgo,
|
||||
count: 5,
|
||||
dataset: apacheAccessDatasetName,
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.refreshFlyout();
|
||||
const summaryAfter = await PageObjects.datasetQuality.parseFlyoutKpis();
|
||||
|
||||
expect(parseInt(summaryAfter.docsCountTotal, 10)).to.be.greaterThan(
|
||||
parseInt(summaryBefore.docsCountTotal, 10)
|
||||
);
|
||||
|
||||
expect(parseInt(summaryAfter.degradedDocs, 10)).to.be.greaterThan(
|
||||
parseInt(summaryBefore.degradedDocs, 10)
|
||||
);
|
||||
|
||||
expect(parseInt(summaryAfter.size, 10)).to.be.greaterThan(parseInt(summaryBefore.size, 10));
|
||||
expect(parseInt(summaryAfter.services, 10)).to.be.greaterThan(
|
||||
parseInt(summaryBefore.services, 10)
|
||||
);
|
||||
expect(parseInt(summaryAfter.hosts, 10)).to.be.greaterThan(
|
||||
parseInt(summaryBefore.hosts, 10)
|
||||
);
|
||||
});
|
||||
|
||||
it('shows the right number of services', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const summaryBefore = await PageObjects.datasetQuality.parseFlyoutKpis();
|
||||
const testServices = ['test-srv-1', 'test-srv-2'];
|
||||
|
||||
// Index 2 docs with different services
|
||||
const timeNow = Date.now();
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: timeNow,
|
||||
count: 2,
|
||||
dataset: apacheAccessDatasetName,
|
||||
isMalformed: false,
|
||||
services: testServices,
|
||||
})
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.refreshFlyout();
|
||||
const summaryAfter = await PageObjects.datasetQuality.parseFlyoutKpis();
|
||||
|
||||
expect(parseInt(summaryAfter.services, 10)).to.eql(
|
||||
parseInt(summaryBefore.services, 10) + testServices.length
|
||||
);
|
||||
});
|
||||
|
||||
it('goes to log explorer for degraded docs when show all is clicked', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const degradedDocsShowAllSelector = `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutKpiLink}-${PageObjects.datasetQuality.texts.degradedDocs}`;
|
||||
await testSubjects.click(degradedDocsShowAllSelector);
|
||||
await browser.switchTab(1);
|
||||
|
||||
// Confirm dataset selector text in observability logs explorer
|
||||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.contain(apacheAccessDatasetName);
|
||||
|
||||
await browser.closeCurrentWindow();
|
||||
await browser.switchTab(0);
|
||||
});
|
||||
|
||||
// Blocked by https://github.com/elastic/kibana/issues/181705
|
||||
it.skip('goes to infra hosts for hosts when show all is clicked', async () => {
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const hostsShowAllSelector = `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutKpiLink}-${PageObjects.datasetQuality.texts.hosts}`;
|
||||
await testSubjects.click(hostsShowAllSelector);
|
||||
await browser.switchTab(1);
|
||||
|
||||
// Confirm url contains metrics/hosts
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
expect(parsedUrl.pathname).to.contain('/app/metrics/hosts');
|
||||
});
|
||||
|
||||
await browser.closeCurrentWindow();
|
||||
await browser.switchTab(0);
|
||||
});
|
||||
|
||||
it('Integration actions menu is present with correct actions', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
it('should show the integration actions menu with correct actions', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
|
@ -314,25 +175,10 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
);
|
||||
|
||||
expect(actions.length).to.eql(3);
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('Integration dashboard action hidden for integrations without dashboards', async () => {
|
||||
const bitbucketDatasetName = 'atlassian_bitbucket.audit';
|
||||
const bitbucketDatasetHumanName = 'Bitbucket Audit Logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.installPackage({
|
||||
name: 'atlassian_bitbucket',
|
||||
version: '1.14.0',
|
||||
});
|
||||
|
||||
// Index 10 logs for `atlassian_bitbucket.audit` dataset
|
||||
await synthtrace.index(getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName }));
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
it('should hide integration dashboard for integrations without dashboards', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(bitbucketDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
|
@ -341,25 +187,10 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
integrationActions.viewDashboards
|
||||
)
|
||||
);
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('Integration overview action should navigate to the integration overview page', async () => {
|
||||
const bitbucketDatasetName = 'atlassian_bitbucket.audit';
|
||||
const bitbucketDatasetHumanName = 'Bitbucket Audit Logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.installPackage({
|
||||
name: 'atlassian_bitbucket',
|
||||
version: '1.14.0',
|
||||
});
|
||||
|
||||
// Index 10 logs for `atlassian_bitbucket.audit` dataset
|
||||
await synthtrace.index(getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName }));
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
it('Should navigate to integration overview page on clicking integration overview action', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(bitbucketDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
|
@ -375,58 +206,31 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
|
||||
expect(parsedUrl.pathname).to.contain('/app/integrations/detail/atlassian_bitbucket');
|
||||
});
|
||||
});
|
||||
|
||||
it('Integration template action should navigate to the index template page', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
it('should navigate to index template page in clicking Integration template', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction(
|
||||
integrationActions.template
|
||||
);
|
||||
|
||||
await action.click();
|
||||
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction(
|
||||
integrationActions.template
|
||||
);
|
||||
|
||||
await action.click();
|
||||
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
expect(parsedUrl.pathname).to.contain(
|
||||
`/app/management/data/index_management/templates/logs-${apacheAccessDatasetName}`
|
||||
);
|
||||
});
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
it('Integration dashboard action should navigate to the selected dashboard', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
it('should navigate to the selected dashboard on clicking integration dashboard action ', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
|
@ -445,148 +249,207 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
const breadcrumbText = await testSubjects.getVisibleText('breadcrumb last');
|
||||
|
||||
expect(breadcrumbText).to.eql(dashboardText);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
});
|
||||
|
||||
// The above describe block has some failing/flaky tests which will
|
||||
// be fixed as part of the tech debt mentioned here
|
||||
// https://github.com/elastic/kibana/issues/184145
|
||||
// Until then, the below describe block is added to cover the tests for the
|
||||
// newly added degraded Fields Table. This must be merged under the above
|
||||
// describe block once the tech debt is fixed.
|
||||
describe('Dataset quality flyout with degraded fields', () => {
|
||||
const goodDatasetName = 'good';
|
||||
const degradedDatasetName = 'degraded';
|
||||
const today = new Date().toISOString();
|
||||
describe('summary panel', () => {
|
||||
it('should show summary KPIs', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
describe('Degraded Fields Table with common data', () => {
|
||||
before(async () => {
|
||||
await synthtrace.index([
|
||||
getLogsForDataset({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: goodDatasetName,
|
||||
isMalformed: false,
|
||||
}),
|
||||
createDegradedFieldsRecord({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
]);
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
const { docsCountTotal, degradedDocs, services, hosts, size } =
|
||||
await PageObjects.datasetQuality.parseFlyoutKpis();
|
||||
expect(parseInt(docsCountTotal, 10)).to.be(226);
|
||||
expect(parseInt(degradedDocs, 10)).to.be(1);
|
||||
expect(parseInt(services, 10)).to.be(3);
|
||||
expect(parseInt(hosts, 10)).to.be(52);
|
||||
expect(parseInt(size, 10)).to.be.greaterThan(0);
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
});
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the degraded fields table with no data when no degraded fields are present', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(goodDatasetName);
|
||||
describe('navigation', () => {
|
||||
it('should go to log explorer page when the open in log explorer button is clicked', async () => {
|
||||
const testDatasetName = datasetNames[2];
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutDegradedTableNoData
|
||||
);
|
||||
const logExplorerButton = await PageObjects.datasetQuality.getFlyoutLogsExplorerButton();
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
await logExplorerButton.click();
|
||||
|
||||
it('should load the degraded fields table with data', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
// Confirm dataset selector text in observability logs explorer
|
||||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.eql(testDatasetName);
|
||||
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutDegradedFieldTable
|
||||
);
|
||||
|
||||
const rows =
|
||||
await PageObjects.datasetQuality.getDatasetQualityFlyoutDegradedFieldTableRows();
|
||||
|
||||
expect(rows.length).to.eql(2);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should display Spark Plot for every row of degraded fields', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const rows =
|
||||
await PageObjects.datasetQuality.getDatasetQualityFlyoutDegradedFieldTableRows();
|
||||
|
||||
const sparkPlots = await testSubjects.findAll(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualitySparkPlot
|
||||
);
|
||||
|
||||
expect(rows.length).to.be(sparkPlots.length);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should sort the table when the count table header is clicked', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
|
||||
const countColumn = table['Docs count'];
|
||||
const cellTexts = await countColumn.getCellTexts();
|
||||
|
||||
await countColumn.sort('ascending');
|
||||
const sortedCellTexts = await countColumn.getCellTexts();
|
||||
|
||||
expect(cellTexts.reverse()).to.eql(sortedCellTexts);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
// Should bring back the test to the dataset quality page
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
describe('Degraded Fields Table with data ingestion', () => {
|
||||
before(async () => {
|
||||
await synthtrace.index([
|
||||
getLogsForDataset({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: goodDatasetName,
|
||||
isMalformed: false,
|
||||
}),
|
||||
createDegradedFieldsRecord({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
]);
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
it('should go log explorer for degraded docs when the show all button is clicked', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const degradedDocsShowAllSelector = `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutKpiLink}-${PageObjects.datasetQuality.texts.degradedDocs}`;
|
||||
await testSubjects.click(degradedDocsShowAllSelector);
|
||||
await browser.switchTab(1);
|
||||
|
||||
// Confirm dataset selector text in observability logs explorer
|
||||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.contain(apacheAccessDatasetName);
|
||||
|
||||
await browser.closeCurrentWindow();
|
||||
await browser.switchTab(0);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
// Blocked by https://github.com/elastic/kibana/issues/181705
|
||||
// Its a test written ahead of its time.
|
||||
it.skip('goes to infra hosts for hosts when show all is clicked', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const hostsShowAllSelector = `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutKpiLink}-${PageObjects.datasetQuality.texts.hosts}`;
|
||||
await testSubjects.click(hostsShowAllSelector);
|
||||
await browser.switchTab(1);
|
||||
|
||||
// Confirm url contains metrics/hosts
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
expect(parsedUrl.pathname).to.contain('/app/metrics/hosts');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await browser.closeCurrentWindow();
|
||||
await browser.switchTab(0);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
});
|
||||
|
||||
describe('degraded fields table', () => {
|
||||
it(' should show empty degraded fields table when no degraded fields are present', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(datasetNames[0]);
|
||||
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutDegradedTableNoData
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should show the degraded fields table with data when present', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutDegradedFieldTable
|
||||
);
|
||||
|
||||
const rows =
|
||||
await PageObjects.datasetQuality.getDatasetQualityFlyoutDegradedFieldTableRows();
|
||||
|
||||
expect(rows.length).to.eql(2);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should display Spark Plot for every row of degraded fields', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const rows =
|
||||
await PageObjects.datasetQuality.getDatasetQualityFlyoutDegradedFieldTableRows();
|
||||
|
||||
const sparkPlots = await testSubjects.findAll(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualitySparkPlot
|
||||
);
|
||||
|
||||
expect(rows.length).to.be(sparkPlots.length);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should sort the table when the count table header is clicked', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
|
||||
const countColumn = table['Docs count'];
|
||||
const cellTexts = await countColumn.getCellTexts();
|
||||
|
||||
await countColumn.sort('ascending');
|
||||
const sortedCellTexts = await countColumn.getCellTexts();
|
||||
|
||||
expect(cellTexts.reverse()).to.eql(sortedCellTexts);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should update the URL when the table is sorted', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
const countColumn = table['Docs count'];
|
||||
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
const pageState = parsedUrl.searchParams.get('pageState');
|
||||
|
||||
expect(decodeURIComponent(pageState as string)).to.contain(
|
||||
'sort:(direction:desc,field:count)'
|
||||
);
|
||||
});
|
||||
|
||||
it('should update the table when new data is ingested and the flyout is refreshed using the time selector', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
countColumn.sort('ascending');
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
const pageState = parsedUrl.searchParams.get('pageState');
|
||||
|
||||
const countColumn = table['Docs count'];
|
||||
const cellTexts = await countColumn.getCellTexts();
|
||||
|
||||
await synthtrace.index([
|
||||
createDegradedFieldsRecord({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
]);
|
||||
|
||||
await PageObjects.datasetQuality.refreshFlyout();
|
||||
|
||||
const updatedCellTexts = await countColumn.getCellTexts();
|
||||
|
||||
const singleValuePreviously = parseInt(cellTexts[0], 10);
|
||||
const singleValueNow = parseInt(updatedCellTexts[0], 10);
|
||||
|
||||
expect(singleValueNow).to.be(singleValuePreviously * 2);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
expect(decodeURIComponent(pageState as string)).to.contain(
|
||||
'sort:(direction:asc,field:count)'
|
||||
);
|
||||
});
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
// This is the only test which ingest data during the test.
|
||||
// This block tests the refresh behavior of the degraded fields table.
|
||||
// Even though this test ingest data, it can also be freely moved inside
|
||||
// this describe block, and it won't affect any of the existing tests
|
||||
it('should update the table when new data is ingested and the flyout is refreshed using the time selector', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
|
||||
const countColumn = table['Docs count'];
|
||||
const cellTexts = await countColumn.getCellTexts();
|
||||
|
||||
await synthtrace.index([
|
||||
createDegradedFieldsRecord({
|
||||
to: new Date().toISOString(),
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
]);
|
||||
|
||||
await PageObjects.datasetQuality.refreshFlyout();
|
||||
|
||||
const updatedTable = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
const updatedCountColumn = updatedTable['Docs count'];
|
||||
|
||||
const updatedCellTexts = await updatedCountColumn.getCellTexts();
|
||||
|
||||
const singleValuePreviously = parseInt(cellTexts[0], 10);
|
||||
const singleValueNow = parseInt(updatedCellTexts[0], 10);
|
||||
|
||||
expect(singleValueNow).to.be.greaterThan(singleValuePreviously);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,21 +17,48 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
'datasetQuality',
|
||||
]);
|
||||
const synthtrace = getService('logSynthtraceEsClient');
|
||||
const browser = getService('browser');
|
||||
const retry = getService('retry');
|
||||
const to = '2024-01-01T12:00:00.000Z';
|
||||
|
||||
const ingestDataForSummary = async () => {
|
||||
// Ingest documents for 3 type of datasets
|
||||
return synthtrace.index([
|
||||
// Ingest good data to all 3 datasets
|
||||
getInitialTestLogs({ to, count: 4 }),
|
||||
// Ingesting poor data to one dataset
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[1],
|
||||
isMalformed: true,
|
||||
}),
|
||||
// Ingesting degraded docs into another dataset by ingesting malformed 1st and then good data
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
}),
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 10,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: false,
|
||||
}),
|
||||
]);
|
||||
};
|
||||
|
||||
describe('Dataset quality summary', () => {
|
||||
before(async () => {
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
afterEach(async () => {
|
||||
await synthtrace.clean();
|
||||
});
|
||||
|
||||
it('shows poor, degraded and good count', async () => {
|
||||
it('shows poor, degraded and good count as 0 and all dataset as healthy', async () => {
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
await PageObjects.datasetQuality.refreshTable();
|
||||
const summary = await PageObjects.datasetQuality.parseSummaryPanel();
|
||||
expect(summary).to.eql({
|
||||
datasetHealthPoor: '0',
|
||||
|
@ -42,92 +69,21 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
});
|
||||
});
|
||||
|
||||
it('updates the poor count when degraded docs are ingested', async () => {
|
||||
// Index malformed document with current timestamp
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
it('shows updated count for poor, degraded and good datasets, estimated size and updates active datasets', async () => {
|
||||
await ingestDataForSummary();
|
||||
await PageObjects.datasetQuality.refreshTable();
|
||||
|
||||
await browser.refresh();
|
||||
await PageObjects.datasetQuality.waitUntilSummaryPanelLoaded();
|
||||
|
||||
await retry.try(async () => {
|
||||
const summary = await PageObjects.datasetQuality.parseSummaryPanel();
|
||||
const { estimatedData, ...restOfSummary } = summary;
|
||||
expect(restOfSummary).to.eql({
|
||||
datasetHealthPoor: '1',
|
||||
datasetHealthDegraded: '0',
|
||||
datasetHealthGood: '2',
|
||||
activeDatasets: '1 of 3',
|
||||
});
|
||||
const summary = await PageObjects.datasetQuality.parseSummaryPanel();
|
||||
const { estimatedData, ...restOfSummary } = summary;
|
||||
expect(restOfSummary).to.eql({
|
||||
datasetHealthPoor: '1',
|
||||
datasetHealthDegraded: '1',
|
||||
datasetHealthGood: '1',
|
||||
activeDatasets: '2 of 3',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the degraded count when degraded docs are ingested', async () => {
|
||||
// Index malformed document with current timestamp
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[1],
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
|
||||
// Index healthy documents
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 10,
|
||||
dataset: datasetNames[1],
|
||||
isMalformed: false,
|
||||
})
|
||||
);
|
||||
|
||||
await browser.refresh();
|
||||
await PageObjects.datasetQuality.waitUntilSummaryPanelLoaded();
|
||||
|
||||
await retry.try(async () => {
|
||||
const { estimatedData, ...restOfSummary } =
|
||||
await PageObjects.datasetQuality.parseSummaryPanel();
|
||||
expect(restOfSummary).to.eql({
|
||||
datasetHealthPoor: '1',
|
||||
datasetHealthDegraded: '1',
|
||||
datasetHealthGood: '1',
|
||||
activeDatasets: '2 of 3',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('updates active datasets and estimated data KPIs', async () => {
|
||||
const { estimatedData: existingEstimatedData } =
|
||||
await PageObjects.datasetQuality.parseSummaryPanel();
|
||||
|
||||
// Index document at current time to mark dataset as active
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 4,
|
||||
dataset: datasetNames[0],
|
||||
isMalformed: false,
|
||||
})
|
||||
);
|
||||
|
||||
await browser.refresh(); // Summary panel doesn't update reactively
|
||||
await PageObjects.datasetQuality.waitUntilSummaryPanelLoaded();
|
||||
|
||||
await retry.try(async () => {
|
||||
const { activeDatasets: updatedActiveDatasets, estimatedData: updatedEstimatedData } =
|
||||
await PageObjects.datasetQuality.parseSummaryPanel();
|
||||
|
||||
expect(updatedActiveDatasets).to.eql('3 of 3');
|
||||
expect(updatedEstimatedData).to.not.eql(existingEstimatedData);
|
||||
});
|
||||
const sizeInNumber = parseFloat(estimatedData.split(' ')[0]);
|
||||
expect(sizeInNumber).to.be.greaterThan(0);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import { DatasetQualityFtrProviderContext } from './config';
|
||||
import { datasetNames, defaultNamespace, getInitialTestLogs, getLogsForDataset } from './data';
|
||||
import {
|
||||
datasetNames,
|
||||
defaultNamespace,
|
||||
getInitialTestLogs,
|
||||
getLogsForDataset,
|
||||
productionNamespace,
|
||||
} from './data';
|
||||
|
||||
export default function ({ getService, getPageObjects }: DatasetQualityFtrProviderContext) {
|
||||
const PageObjects = getPageObjects([
|
||||
|
@ -17,40 +23,78 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
'datasetQuality',
|
||||
]);
|
||||
const synthtrace = getService('logSynthtraceEsClient');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const to = '2024-01-01T12:00:00.000Z';
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const pkg = {
|
||||
name: 'apache',
|
||||
version: '1.14.0',
|
||||
};
|
||||
|
||||
describe('Dataset quality table', () => {
|
||||
before(async () => {
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
// Install Integration and ingest logs for it
|
||||
await PageObjects.observabilityLogsExplorer.installPackage(pkg);
|
||||
// Ingest basic logs
|
||||
await synthtrace.index([
|
||||
// Ingest basic logs
|
||||
getInitialTestLogs({ to, count: 4 }),
|
||||
// Ingest Degraded Logs
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
}),
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
getLogsForDataset({
|
||||
to,
|
||||
count: 10,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: productionNamespace,
|
||||
}),
|
||||
]);
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await PageObjects.observabilityLogsExplorer.removeInstalledPackages();
|
||||
await PageObjects.observabilityLogsExplorer.uninstallPackage(pkg);
|
||||
});
|
||||
|
||||
it('shows the right number of rows in correct order', async () => {
|
||||
it('shows sort by dataset name and show namespace', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
await datasetNameCol.sort('descending');
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTexts).to.eql([...datasetNames].reverse());
|
||||
expect(datasetNameColCellTexts).to.eql(
|
||||
[apacheAccessDatasetHumanName, ...datasetNames].reverse()
|
||||
);
|
||||
|
||||
const namespaceCol = cols.Namespace;
|
||||
const namespaceColCellTexts = await namespaceCol.getCellTexts();
|
||||
expect(namespaceColCellTexts).to.eql([defaultNamespace, defaultNamespace, defaultNamespace]);
|
||||
expect(namespaceColCellTexts).to.eql([
|
||||
defaultNamespace,
|
||||
defaultNamespace,
|
||||
defaultNamespace,
|
||||
productionNamespace,
|
||||
]);
|
||||
|
||||
const degradedDocsCol = cols['Degraded Docs (%)'];
|
||||
const degradedDocsColCellTexts = await degradedDocsCol.getCellTexts();
|
||||
expect(degradedDocsColCellTexts).to.eql(['0%', '0%', '0%']);
|
||||
// Cleaning the sort
|
||||
await datasetNameCol.sort('ascending');
|
||||
});
|
||||
|
||||
it('shows the last activity', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const lastActivityCol = cols['Last Activity'];
|
||||
const lastActivityColCellTexts = await lastActivityCol.getCellTexts();
|
||||
expect(lastActivityColCellTexts).to.eql([
|
||||
const activityCells = await lastActivityCol.getCellTexts();
|
||||
const lastActivityCell = activityCells[activityCells.length - 1];
|
||||
const restActivityCells = activityCells.slice(0, -1);
|
||||
|
||||
// The first cell of lastActivity should have data
|
||||
expect(lastActivityCell).to.not.eql(PageObjects.datasetQuality.texts.noActivityText);
|
||||
// The rest of the rows must show no activity
|
||||
expect(restActivityCells).to.eql([
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
|
@ -59,111 +103,30 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
|
||||
it('shows degraded docs percentage', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
await datasetNameCol.sort('ascending');
|
||||
|
||||
const degradedDocsCol = cols['Degraded Docs (%)'];
|
||||
const degradedDocsColCellTexts = await degradedDocsCol.getCellTexts();
|
||||
expect(degradedDocsColCellTexts).to.eql(['0%', '0%', '0%']);
|
||||
|
||||
// Index malformed document with current timestamp
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
|
||||
// Set time range to Last 5 minute
|
||||
const filtersContainer = await testSubjects.find(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFiltersContainer
|
||||
);
|
||||
await PageObjects.datasetQuality.setDatePickerLastXUnits(filtersContainer, 5, 'm');
|
||||
|
||||
const updatedDegradedDocsColCellTexts = await degradedDocsCol.getCellTexts();
|
||||
expect(updatedDegradedDocsColCellTexts[2]).to.not.eql('0%');
|
||||
expect(degradedDocsColCellTexts).to.eql(['0%', '0%', '0%', '100%']);
|
||||
});
|
||||
|
||||
it('shows the updated size of the index', async () => {
|
||||
const testDatasetIndex = 2;
|
||||
it('shows the value in the size column', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
await datasetNameCol.sort('ascending');
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
|
||||
const datasetToUpdateRowIndex = datasetNameColCellTexts.findIndex(
|
||||
(dName: string) => dName === datasetNames[testDatasetIndex]
|
||||
);
|
||||
|
||||
const sizeColCellTexts = await cols.Size.getCellTexts();
|
||||
const beforeSize = sizeColCellTexts[datasetToUpdateRowIndex];
|
||||
const sizeGreaterThanZero = sizeColCellTexts[3];
|
||||
const sizeEqualToZero = sizeColCellTexts[2];
|
||||
|
||||
// Index documents with current timestamp
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 4,
|
||||
dataset: datasetNames[testDatasetIndex],
|
||||
isMalformed: false,
|
||||
})
|
||||
);
|
||||
|
||||
const colsAfterUpdate = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
|
||||
// Assert that size has changed
|
||||
await retry.tryForTime(15000, async () => {
|
||||
// Refresh the table
|
||||
await PageObjects.datasetQuality.refreshTable();
|
||||
const updatedSizeColCellTexts = await colsAfterUpdate.Size.getCellTexts();
|
||||
expect(updatedSizeColCellTexts[datasetToUpdateRowIndex]).to.not.eql(beforeSize);
|
||||
});
|
||||
});
|
||||
|
||||
it('sorts by dataset name', async () => {
|
||||
// const header = await PageObjects.datasetQuality.getDatasetTableHeader('Data Set Name');
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
|
||||
// Sort ascending
|
||||
await datasetNameCol.sort('ascending');
|
||||
const cellTexts = await datasetNameCol.getCellTexts();
|
||||
|
||||
const datasetNamesAsc = [...datasetNames].sort();
|
||||
|
||||
expect(cellTexts).to.eql(datasetNamesAsc);
|
||||
expect(sizeGreaterThanZero).to.not.eql('0.0 KB');
|
||||
expect(sizeEqualToZero).to.eql('0.0 B');
|
||||
});
|
||||
|
||||
it('shows dataset from integration', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
|
||||
// Sort ascending
|
||||
await datasetNameCol.sort('ascending');
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
|
||||
const datasetNamesAsc = [...datasetNames, apacheAccessDatasetHumanName].sort();
|
||||
|
||||
// Assert there are 4 rows
|
||||
expect(datasetNameColCellTexts.length).to.eql(4);
|
||||
|
||||
expect(datasetNameColCellTexts).to.eql(datasetNamesAsc);
|
||||
expect(datasetNameColCellTexts[0]).to.eql(apacheAccessDatasetHumanName);
|
||||
});
|
||||
|
||||
it('goes to log explorer page when opened', async () => {
|
||||
|
@ -179,50 +142,12 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.eql(datasetName);
|
||||
});
|
||||
|
||||
it('shows the last activity when in time range', async () => {
|
||||
// Return to Dataset Quality Page
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const lastActivityCol = cols['Last Activity'];
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
|
||||
// Set time range to Last 1 minute
|
||||
const filtersContainer = await testSubjects.find(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFiltersContainer
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.setDatePickerLastXUnits(filtersContainer, 1, 's');
|
||||
const lastActivityColCellTexts = await lastActivityCol.getCellTexts();
|
||||
expect(lastActivityColCellTexts).to.eql([
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
]);
|
||||
|
||||
const datasetToUpdate = datasetNames[0];
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to: new Date().toISOString(), count: 1, dataset: datasetToUpdate })
|
||||
);
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
const datasetToUpdateRowIndex = datasetNameColCellTexts.findIndex(
|
||||
(dName: string) => dName === datasetToUpdate
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.setDatePickerLastXUnits(filtersContainer, 1, 'h');
|
||||
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const updatedLastActivityColCellTexts = await lastActivityCol.getCellTexts();
|
||||
expect(updatedLastActivityColCellTexts[datasetToUpdateRowIndex]).to.not.eql(
|
||||
PageObjects.datasetQuality.texts.noActivityText
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('hides inactive datasets', async () => {
|
||||
await PageObjects.datasetQuality.waitUntilTableLoaded();
|
||||
|
||||
// Get number of rows with Last Activity not equal to "No activity in the selected timeframe"
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const lastActivityCol = cols['Last Activity'];
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import { DatasetQualityFtrProviderContext } from './config';
|
||||
import { datasetNames, defaultNamespace, getInitialTestLogs, getLogsForDataset } from './data';
|
||||
import { datasetNames, getInitialTestLogs, getLogsForDataset, productionNamespace } from './data';
|
||||
|
||||
export default function ({ getService, getPageObjects }: DatasetQualityFtrProviderContext) {
|
||||
const PageObjects = getPageObjects([
|
||||
|
@ -19,55 +19,73 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
const synthtrace = getService('logSynthtraceEsClient');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const to = '2024-01-01T12:00:00.000Z';
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const apacheIntegrationName = 'Apache HTTP Server';
|
||||
const pkg = {
|
||||
name: 'apache',
|
||||
version: '1.14.0',
|
||||
};
|
||||
const allDatasetNames = [apacheAccessDatasetHumanName, ...datasetNames];
|
||||
|
||||
describe('Dataset quality table filters', () => {
|
||||
before(async () => {
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
// Install Integration and ingest logs for it
|
||||
await PageObjects.observabilityLogsExplorer.installPackage(pkg);
|
||||
// Ingest basic logs
|
||||
await synthtrace.index([
|
||||
// Ingest basic logs
|
||||
getInitialTestLogs({ to, count: 4 }),
|
||||
// Ingest Degraded Logs
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
}),
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
getLogsForDataset({
|
||||
to,
|
||||
count: 10,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: productionNamespace,
|
||||
}),
|
||||
]);
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await PageObjects.observabilityLogsExplorer.uninstallPackage(pkg);
|
||||
await synthtrace.clean();
|
||||
await PageObjects.observabilityLogsExplorer.removeInstalledPackages();
|
||||
});
|
||||
|
||||
it('hides inactive datasets when toggled', async () => {
|
||||
const initialRows = await PageObjects.datasetQuality.getDatasetTableRows();
|
||||
expect(initialRows.length).to.eql(3);
|
||||
|
||||
await PageObjects.datasetQuality.toggleShowInactiveDatasets();
|
||||
|
||||
const afterToggleRows = await PageObjects.datasetQuality.getDatasetTableRows();
|
||||
expect(afterToggleRows.length).to.eql(1);
|
||||
|
||||
await PageObjects.datasetQuality.toggleShowInactiveDatasets();
|
||||
|
||||
const afterReToggleRows = await PageObjects.datasetQuality.getDatasetTableRows();
|
||||
expect(afterReToggleRows.length).to.eql(3);
|
||||
});
|
||||
|
||||
it('shows full dataset names when toggled', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTexts).to.eql(datasetNames);
|
||||
expect(datasetNameColCellTexts).to.eql(allDatasetNames);
|
||||
|
||||
await PageObjects.datasetQuality.toggleShowFullDatasetNames();
|
||||
|
||||
const datasetNameColCellTextsAfterToggle = await datasetNameCol.getCellTexts();
|
||||
const duplicateNames = datasetNames.map((name) => `${name}\n${name}`);
|
||||
const duplicateNames = [
|
||||
`${apacheAccessDatasetHumanName}\n${apacheAccessDatasetName}`,
|
||||
...datasetNames.map((name) => `${name}\n${name}`),
|
||||
];
|
||||
|
||||
expect(datasetNameColCellTextsAfterToggle).to.eql(duplicateNames);
|
||||
|
||||
// resetting the toggle
|
||||
await PageObjects.datasetQuality.toggleShowFullDatasetNames();
|
||||
const datasetNameColCellTextsAfterReToggle = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTextsAfterReToggle).to.eql(datasetNames);
|
||||
expect(datasetNameColCellTextsAfterReToggle).to.eql(allDatasetNames);
|
||||
});
|
||||
|
||||
it('searches the datasets', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTexts).to.eql(datasetNames);
|
||||
expect(datasetNameColCellTexts).to.eql(allDatasetNames);
|
||||
|
||||
// Search for a dataset
|
||||
await testSubjects.setValue(
|
||||
|
@ -79,33 +97,16 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
const datasetNameColAfterSearch = colsAfterSearch['Data Set Name'];
|
||||
const datasetNameColCellTextsAfterSearch = await datasetNameColAfterSearch.getCellTexts();
|
||||
expect(datasetNameColCellTextsAfterSearch).to.eql([datasetNames[2]]);
|
||||
await testSubjects.setValue(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFilterBarFieldSearch,
|
||||
''
|
||||
);
|
||||
|
||||
// Reset the search field
|
||||
await testSubjects.click('clearSearchButton');
|
||||
});
|
||||
|
||||
it('filters for integration', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const apacheIntegrationName = 'Apache HTTP Server';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTexts).to.eql([apacheAccessDatasetHumanName, ...datasetNames]);
|
||||
expect(datasetNameColCellTexts).to.eql(allDatasetNames);
|
||||
|
||||
// Filter for integration
|
||||
await PageObjects.datasetQuality.filterForIntegrations([apacheIntegrationName]);
|
||||
|
@ -113,65 +114,33 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
const colsAfterFilter = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameColAfterFilter = colsAfterFilter['Data Set Name'];
|
||||
const datasetNameColCellTextsAfterFilter = await datasetNameColAfterFilter.getCellTexts();
|
||||
|
||||
expect(datasetNameColCellTextsAfterFilter).to.eql([apacheAccessDatasetHumanName]);
|
||||
// Reset the filter by selecting from the dropdown again
|
||||
await PageObjects.datasetQuality.filterForIntegrations([apacheIntegrationName]);
|
||||
});
|
||||
|
||||
it('filters for namespace', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const datasetNamespace = 'prod';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to,
|
||||
count: 10,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: datasetNamespace,
|
||||
})
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
// Get default namespaces
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const namespaceCol = cols.Namespace;
|
||||
const namespaceColCellTexts = await namespaceCol.getCellTexts();
|
||||
expect(namespaceColCellTexts).to.contain(defaultNamespace);
|
||||
expect(namespaceColCellTexts).to.contain(productionNamespace);
|
||||
|
||||
// Filter for prod namespace
|
||||
await PageObjects.datasetQuality.filterForNamespaces([datasetNamespace]);
|
||||
// Filter for production namespace
|
||||
await PageObjects.datasetQuality.filterForNamespaces([productionNamespace]);
|
||||
|
||||
const colsAfterFilter = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const namespaceColAfterFilter = colsAfterFilter.Namespace;
|
||||
const namespaceColCellTextsAfterFilter = await namespaceColAfterFilter.getCellTexts();
|
||||
|
||||
expect(namespaceColCellTextsAfterFilter).to.eql([datasetNamespace]);
|
||||
expect(namespaceColCellTextsAfterFilter).to.eql([productionNamespace]);
|
||||
// Reset the namespace by selecting from the dropdown again
|
||||
await PageObjects.datasetQuality.filterForNamespaces([productionNamespace]);
|
||||
});
|
||||
|
||||
it('filters for quality', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const expectedQuality = 'Poor';
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 10,
|
||||
dataset: apacheAccessDatasetName,
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
// Get default quality
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetQuality = cols['Data Set Quality'];
|
||||
|
@ -186,6 +155,9 @@ export default function ({ getService, getPageObjects }: DatasetQualityFtrProvid
|
|||
const datasetQualityCellTextsAfterFilter = await datasetQualityAfterFilter.getCellTexts();
|
||||
|
||||
expect(datasetQualityCellTextsAfterFilter).to.eql([expectedQuality]);
|
||||
|
||||
// Reset the namespace by selecting from the dropdown again
|
||||
await PageObjects.datasetQuality.filterForQualities([expectedQuality]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -51,12 +51,26 @@ type SummaryPanelKpi = Record<
|
|||
|
||||
type FlyoutKpi = Record<'docsCountTotal' | 'size' | 'services' | 'hosts' | 'degradedDocs', string>;
|
||||
|
||||
const texts = {
|
||||
noActivityText: 'No activity in the selected timeframe',
|
||||
datasetHealthPoor: 'Poor',
|
||||
datasetHealthDegraded: 'Degraded',
|
||||
datasetHealthGood: 'Good',
|
||||
activeDatasets: 'Active Data Sets',
|
||||
estimatedData: 'Estimated Data',
|
||||
docsCountTotal: 'Docs count (total)',
|
||||
size: 'Size',
|
||||
services: 'Services',
|
||||
hosts: 'Hosts',
|
||||
degradedDocs: 'Degraded docs',
|
||||
};
|
||||
|
||||
export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProviderContext) {
|
||||
const PageObjects = getPageObjects(['common']);
|
||||
const testSubjects = getService('testSubjects');
|
||||
const euiSelectable = getService('selectable');
|
||||
const retry = getService('retry');
|
||||
const find = getService('find');
|
||||
const retry = getService('retry');
|
||||
|
||||
const selectors = {
|
||||
datasetQualityTable: '[data-test-subj="datasetQualityTable"]',
|
||||
|
@ -80,6 +94,8 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv
|
|||
datasetQualitySparkPlot: 'datasetQualitySparkPlot',
|
||||
datasetQualityHeaderButton: 'datasetQualityHeaderButton',
|
||||
datasetQualityFlyoutFieldValue: 'datasetQualityFlyoutFieldValue',
|
||||
datasetQualityFlyoutFieldsListIntegrationDetails:
|
||||
'datasetQualityFlyoutFieldsList-integration_details',
|
||||
datasetQualityFlyoutIntegrationActionsButton: 'datasetQualityFlyoutIntegrationActionsButton',
|
||||
datasetQualityFlyoutIntegrationAction: (action: string) =>
|
||||
`datasetQualityFlyoutIntegrationAction${action}`,
|
||||
|
@ -147,13 +163,17 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv
|
|||
await find.waitForDeletedByCssSelector('.euiFlyoutBody .euiBasicTable-loading', 20 * 1000);
|
||||
},
|
||||
|
||||
async waitUntilSummaryPanelLoaded() {
|
||||
async waitUntilSummaryPanelLoaded(isStateful: boolean = true) {
|
||||
await testSubjects.missingOrFail(`datasetQuality-${texts.activeDatasets}-loading`);
|
||||
await testSubjects.missingOrFail(`datasetQuality-${texts.estimatedData}-loading`);
|
||||
if (isStateful) {
|
||||
await testSubjects.missingOrFail(`datasetQuality-${texts.estimatedData}-loading`);
|
||||
}
|
||||
},
|
||||
|
||||
async parseSummaryPanel(excludeKeys: string[] = []): Promise<SummaryPanelKpi> {
|
||||
await this.waitUntilSummaryPanelLoaded();
|
||||
const isStateful = !excludeKeys.includes('estimatedData');
|
||||
|
||||
await this.waitUntilSummaryPanelLoaded(isStateful);
|
||||
|
||||
const kpiTitleAndKeys = [
|
||||
{ title: texts.datasetHealthPoor, key: 'datasetHealthPoor' },
|
||||
|
@ -221,8 +241,9 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv
|
|||
},
|
||||
|
||||
async parseDatasetTable() {
|
||||
await this.waitUntilTableLoaded();
|
||||
const table = await this.getDatasetsTable();
|
||||
return parseDatasetTable(table, [
|
||||
return this.parseTable(table, [
|
||||
'0',
|
||||
'Data Set Name',
|
||||
'Namespace',
|
||||
|
@ -235,8 +256,9 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv
|
|||
},
|
||||
|
||||
async parseDegradedFieldTable() {
|
||||
await this.waitUntilTableInFlyoutLoaded();
|
||||
const table = await this.getDatasetQualityFlyoutDegradedFieldTable();
|
||||
return parseDatasetTable(table, ['Field', 'Docs count', 'Last Occurrence']);
|
||||
return this.parseTable(table, ['Field', 'Docs count', 'Last Occurrence']);
|
||||
},
|
||||
|
||||
async filterForIntegrations(integrations: string[]) {
|
||||
|
@ -272,29 +294,32 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv
|
|||
},
|
||||
|
||||
async openDatasetFlyout(datasetName: string) {
|
||||
await this.waitUntilTableLoaded();
|
||||
const cols = await this.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
const testDatasetRowIndex = datasetNameColCellTexts.findIndex(
|
||||
(dName) => dName === datasetName
|
||||
);
|
||||
const expanderColumn = cols['0'];
|
||||
let expanderButtons: WebElementWrapper[];
|
||||
await retry.try(async () => {
|
||||
expanderButtons = await expanderColumn.getCellChildren(
|
||||
`[data-test-subj=${testSubjectSelectors.datasetQualityExpandButton}]`
|
||||
);
|
||||
expect(expanderButtons.length).to.be.greaterThan(0);
|
||||
|
||||
// Check if 'title' attribute is "Expand" or "Collapse"
|
||||
const isCollapsed =
|
||||
(await expanderButtons[testDatasetRowIndex].getAttribute('title')) === 'Expand';
|
||||
expect(testDatasetRowIndex).to.be.greaterThan(-1);
|
||||
|
||||
// Open if collapsed
|
||||
if (isCollapsed) {
|
||||
await expanderButtons[testDatasetRowIndex].click();
|
||||
}
|
||||
});
|
||||
const expandColumn = cols['0'];
|
||||
const expandButtons = await expandColumn.getCellChildren(
|
||||
`[data-test-subj=${testSubjectSelectors.datasetQualityExpandButton}]`
|
||||
);
|
||||
|
||||
expect(expandButtons.length).to.be.greaterThan(0);
|
||||
|
||||
const datasetExpandButton = expandButtons[testDatasetRowIndex];
|
||||
|
||||
// Check if 'title' attribute is "Expand" or "Collapse"
|
||||
const isCollapsed = (await datasetExpandButton.getAttribute('title')) === 'Expand';
|
||||
|
||||
// Open if collapsed
|
||||
if (isCollapsed) {
|
||||
await datasetExpandButton.click();
|
||||
}
|
||||
},
|
||||
|
||||
async closeFlyout() {
|
||||
|
@ -430,6 +455,85 @@ export function DatasetQualityPageObject({ getPageObjects, getService }: FtrProv
|
|||
fieldText
|
||||
);
|
||||
},
|
||||
|
||||
async parseTable(tableWrapper: WebElementWrapper, columnNamesOrIndexes: string[]) {
|
||||
const headerElementWrappers = await tableWrapper.findAllByCssSelector('thead th, thead td');
|
||||
|
||||
const result: Record<
|
||||
string,
|
||||
{
|
||||
columnNameOrIndex: string;
|
||||
sortDirection?: 'ascending' | 'descending';
|
||||
headerElement: WebElementWrapper;
|
||||
cellElements: WebElementWrapper[];
|
||||
cellContentElements: WebElementWrapper[];
|
||||
getSortDirection: () => Promise<'ascending' | 'descending' | undefined>;
|
||||
sort: (sortDirection: 'ascending' | 'descending') => Promise<void>;
|
||||
getCellTexts: (selector?: string) => Promise<string[]>;
|
||||
getCellChildren: (selector: string) => Promise<WebElementWrapper[]>;
|
||||
}
|
||||
> = {};
|
||||
|
||||
for (let i = 0; i < headerElementWrappers.length; i++) {
|
||||
const tdSelector = `table > tbody > tr td:nth-child(${i + 1})`;
|
||||
const cellContentSelector = `${tdSelector} .euiTableCellContent`;
|
||||
const thWrapper = headerElementWrappers[i];
|
||||
const columnName = await thWrapper.getVisibleText();
|
||||
const columnIndex = `${i}`;
|
||||
const columnNameOrIndex = columnNamesOrIndexes.includes(columnName)
|
||||
? columnName
|
||||
: columnNamesOrIndexes.includes(columnIndex)
|
||||
? columnIndex
|
||||
: undefined;
|
||||
|
||||
if (columnNameOrIndex) {
|
||||
const headerElement = thWrapper;
|
||||
|
||||
const tdWrappers = await tableWrapper.findAllByCssSelector(tdSelector);
|
||||
const cellContentWrappers = await tableWrapper.findAllByCssSelector(cellContentSelector);
|
||||
|
||||
const getSortDirection = () =>
|
||||
headerElement.getAttribute('aria-sort') as Promise<
|
||||
'ascending' | 'descending' | undefined
|
||||
>;
|
||||
|
||||
result[columnNameOrIndex] = {
|
||||
columnNameOrIndex,
|
||||
headerElement,
|
||||
cellElements: tdWrappers,
|
||||
cellContentElements: cellContentWrappers,
|
||||
getSortDirection,
|
||||
sort: async (sortDirection: 'ascending' | 'descending') => {
|
||||
await retry.tryForTime(5000, async () => {
|
||||
while ((await getSortDirection()) !== sortDirection) {
|
||||
await headerElement.click();
|
||||
}
|
||||
});
|
||||
},
|
||||
getCellTexts: async (textContainerSelector?: string) => {
|
||||
const cellContentContainerWrappers = textContainerSelector
|
||||
? await tableWrapper.findAllByCssSelector(`${tdSelector} ${textContainerSelector}`)
|
||||
: cellContentWrappers;
|
||||
|
||||
const cellContentContainerWrapperTexts: string[] = [];
|
||||
for (let j = 0; j < cellContentContainerWrappers.length; j++) {
|
||||
const cellContentContainerWrapper = cellContentContainerWrappers[j];
|
||||
const cellContentContainerWrapperText =
|
||||
await cellContentContainerWrapper.getVisibleText();
|
||||
cellContentContainerWrapperTexts.push(cellContentContainerWrapperText);
|
||||
}
|
||||
|
||||
return cellContentContainerWrapperTexts;
|
||||
},
|
||||
getCellChildren: (childSelector: string) => {
|
||||
return tableWrapper.findAllByCssSelector(`${cellContentSelector} ${childSelector}`);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -440,86 +544,6 @@ async function getDatasetTableHeaderTexts(tableWrapper: WebElementWrapper) {
|
|||
);
|
||||
}
|
||||
|
||||
async function parseDatasetTable(tableWrapper: WebElementWrapper, columnNamesOrIndexes: string[]) {
|
||||
const headerElementWrappers = await tableWrapper.findAllByCssSelector('thead th, thead td');
|
||||
|
||||
const result: Record<
|
||||
string,
|
||||
{
|
||||
columnNameOrIndex: string;
|
||||
sortDirection?: 'ascending' | 'descending';
|
||||
headerElement: WebElementWrapper;
|
||||
cellElements: WebElementWrapper[];
|
||||
cellContentElements: WebElementWrapper[];
|
||||
getSortDirection: () => Promise<'ascending' | 'descending' | undefined>;
|
||||
sort: (sortDirection: 'ascending' | 'descending') => Promise<void>;
|
||||
getCellTexts: (selector?: string) => Promise<string[]>;
|
||||
getCellChildren: (selector: string) => Promise<WebElementWrapper[]>;
|
||||
}
|
||||
> = {};
|
||||
|
||||
for (let i = 0; i < headerElementWrappers.length; i++) {
|
||||
const tdSelector = `table > tbody > tr td:nth-child(${i + 1})`;
|
||||
const cellContentSelector = `${tdSelector} .euiTableCellContent`;
|
||||
const thWrapper = headerElementWrappers[i];
|
||||
const columnName = await thWrapper.getVisibleText();
|
||||
const columnIndex = `${i}`;
|
||||
const columnNameOrIndex = columnNamesOrIndexes.includes(columnName)
|
||||
? columnName
|
||||
: columnNamesOrIndexes.includes(columnIndex)
|
||||
? columnIndex
|
||||
: undefined;
|
||||
|
||||
if (columnNameOrIndex) {
|
||||
const headerElement = thWrapper;
|
||||
|
||||
const tdWrappers = await tableWrapper.findAllByCssSelector(tdSelector);
|
||||
const cellContentWrappers = await tableWrapper.findAllByCssSelector(cellContentSelector);
|
||||
|
||||
const getSortDirection = () =>
|
||||
headerElement.getAttribute('aria-sort') as Promise<'ascending' | 'descending' | undefined>;
|
||||
|
||||
result[columnNameOrIndex] = {
|
||||
columnNameOrIndex,
|
||||
headerElement,
|
||||
cellElements: tdWrappers,
|
||||
cellContentElements: cellContentWrappers,
|
||||
getSortDirection,
|
||||
sort: async (sortDirection: 'ascending' | 'descending') => {
|
||||
if ((await getSortDirection()) !== sortDirection) {
|
||||
await headerElement.click();
|
||||
}
|
||||
|
||||
// Sorting twice if the sort was in neutral state
|
||||
if ((await getSortDirection()) !== sortDirection) {
|
||||
await headerElement.click();
|
||||
}
|
||||
},
|
||||
getCellTexts: async (textContainerSelector?: string) => {
|
||||
const cellContentContainerWrappers = textContainerSelector
|
||||
? await tableWrapper.findAllByCssSelector(`${tdSelector} ${textContainerSelector}`)
|
||||
: cellContentWrappers;
|
||||
|
||||
const cellContentContainerWrapperTexts: string[] = [];
|
||||
for (let j = 0; j < cellContentContainerWrappers.length; j++) {
|
||||
const cellContentContainerWrapper = cellContentContainerWrappers[j];
|
||||
const cellContentContainerWrapperText =
|
||||
await cellContentContainerWrapper.getVisibleText();
|
||||
cellContentContainerWrapperTexts.push(cellContentContainerWrapperText);
|
||||
}
|
||||
|
||||
return cellContentContainerWrapperTexts;
|
||||
},
|
||||
getCellChildren: (childSelector: string) => {
|
||||
return tableWrapper.findAllByCssSelector(`${cellContentSelector} ${childSelector}`);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all elements matching the given selector and text
|
||||
* @example
|
||||
|
@ -544,17 +568,3 @@ export async function getAllByText(container: WebElementWrapper, selector: strin
|
|||
|
||||
return matchingElements;
|
||||
}
|
||||
|
||||
const texts = {
|
||||
noActivityText: 'No activity in the selected timeframe',
|
||||
datasetHealthPoor: 'Poor',
|
||||
datasetHealthDegraded: 'Degraded',
|
||||
datasetHealthGood: 'Good',
|
||||
activeDatasets: 'Active Data Sets',
|
||||
estimatedData: 'Estimated Data',
|
||||
docsCountTotal: 'Docs count (total)',
|
||||
size: 'Size',
|
||||
services: 'Services',
|
||||
hosts: 'Hosts',
|
||||
degradedDocs: 'Degraded docs',
|
||||
};
|
||||
|
|
|
@ -191,6 +191,7 @@ export function createDegradedFieldsRecord({
|
|||
|
||||
export const datasetNames = ['synth.1', 'synth.2', 'synth.3'];
|
||||
export const defaultNamespace = 'default';
|
||||
export const productionNamespace = 'production';
|
||||
|
||||
// Logs Data logic
|
||||
const MESSAGE_LOG_LEVELS: MessageWithLevel[] = [
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
getInitialTestLogs,
|
||||
getLogsForDataset,
|
||||
createDegradedFieldsRecord,
|
||||
productionNamespace,
|
||||
} from './data';
|
||||
|
||||
const integrationActions = {
|
||||
|
@ -36,25 +37,72 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const to = '2024-01-01T12:00:00.000Z';
|
||||
const excludeKeysFromServerless = ['size']; // https://github.com/elastic/kibana/issues/178954
|
||||
|
||||
describe('Dataset quality flyout', function () {
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/183771
|
||||
// Added this sub describe block so that the existing flaky tests can be skipped and new ones can be added in the other describe block
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const apacheIntegrationId = 'apache';
|
||||
const apachePkg = {
|
||||
name: 'apache',
|
||||
version: '1.14.0',
|
||||
};
|
||||
|
||||
describe.skip('Other dataset quality flyout tests', () => {
|
||||
this.tags(['failsOnMKI']); // Failing https://github.com/elastic/kibana/issues/183495
|
||||
const bitbucketDatasetName = 'atlassian_bitbucket.audit';
|
||||
const bitbucketDatasetHumanName = 'Bitbucket Audit Logs';
|
||||
const bitbucketPkg = {
|
||||
name: 'atlassian_bitbucket',
|
||||
version: '1.14.0',
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
await PageObjects.svlCommonPage.loginWithRole('admin');
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
const degradedDatasetName = datasetNames[2];
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await PageObjects.observabilityLogsExplorer.removeInstalledPackages();
|
||||
});
|
||||
describe('Flyout', function () {
|
||||
before(async () => {
|
||||
// Install Apache Integration and ingest logs for it
|
||||
await PageObjects.observabilityLogsExplorer.installPackage(apachePkg);
|
||||
|
||||
it('opens the flyout for the right dataset', async () => {
|
||||
// Install Bitbucket Integration (package which does not has Dashboards) and ingest logs for it
|
||||
await PageObjects.observabilityLogsExplorer.installPackage(bitbucketPkg);
|
||||
|
||||
await synthtrace.index([
|
||||
// Ingest basic logs
|
||||
getInitialTestLogs({ to, count: 4 }),
|
||||
// Ingest Degraded Logs
|
||||
createDegradedFieldsRecord({
|
||||
to: new Date().toISOString(),
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
// Index 15 logs for `logs-apache.access` dataset
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 15,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: productionNamespace,
|
||||
}),
|
||||
// Index degraded docs for Apache integration
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 1,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: productionNamespace,
|
||||
isMalformed: true,
|
||||
}),
|
||||
// Index logs for Bitbucket integration
|
||||
getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName }),
|
||||
]);
|
||||
|
||||
await PageObjects.svlCommonPage.loginWithRole('admin');
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await PageObjects.observabilityLogsExplorer.uninstallPackage(apachePkg);
|
||||
await PageObjects.observabilityLogsExplorer.uninstallPackage(bitbucketPkg);
|
||||
await synthtrace.clean();
|
||||
});
|
||||
|
||||
describe('open flyout', () => {
|
||||
it('should open the flyout for the right dataset', async () => {
|
||||
const testDatasetName = datasetNames[1];
|
||||
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
@ -62,44 +110,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutTitle
|
||||
);
|
||||
});
|
||||
|
||||
// Fails on Serverless. TODO: Need to update the UI as well as the test
|
||||
it.skip('shows the correct last activity', async () => {
|
||||
const testDatasetName = datasetNames[0];
|
||||
|
||||
// Update last activity for the dataset
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to: new Date().toISOString(), count: 1, dataset: testDatasetName })
|
||||
);
|
||||
await PageObjects.datasetQuality.refreshTable();
|
||||
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
|
||||
const testDatasetRowIndex = datasetNameColCellTexts.findIndex(
|
||||
(dName: string) => dName === testDatasetName
|
||||
);
|
||||
|
||||
const lastActivityText = (await cols['Last Activity'].getCellTexts())[testDatasetRowIndex];
|
||||
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
||||
const lastActivityTextExists = await PageObjects.datasetQuality.doestTextExistInFlyout(
|
||||
lastActivityText,
|
||||
`[data-test-subj=${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutFieldValue}]`
|
||||
);
|
||||
|
||||
expect(lastActivityTextExists).to.eql(true);
|
||||
});
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/180994
|
||||
it.skip('reflects the breakdown field state in url', async () => {
|
||||
const testDatasetName = datasetNames[0];
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
it('reflects the breakdown field state in url', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const breakdownField = 'service.name';
|
||||
await PageObjects.datasetQuality.selectBreakdownField(breakdownField);
|
||||
|
@ -118,25 +134,25 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const currentUrl = await browser.getCurrentUrl();
|
||||
expect(currentUrl).to.not.contain('breakdownField');
|
||||
});
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the integration details', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const apacheIntegrationId = 'apache';
|
||||
describe('integrations', () => {
|
||||
it('should hide the integration section for non integrations', async () => {
|
||||
const testDatasetName = datasetNames[1];
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
await testSubjects.missingOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors
|
||||
.datasetQualityFlyoutFieldsListIntegrationDetails
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should shows the integration section for integrations', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const integrationNameElements = await PageObjects.datasetQuality.getFlyoutElementsByText(
|
||||
|
@ -144,185 +160,17 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
apacheIntegrationId
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors
|
||||
.datasetQualityFlyoutFieldsListIntegrationDetails
|
||||
);
|
||||
|
||||
expect(integrationNameElements.length).to.eql(1);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('goes to log explorer page when open button is clicked', async () => {
|
||||
const testDatasetName = datasetNames[2];
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
||||
await (await PageObjects.datasetQuality.getFlyoutLogsExplorerButton()).click();
|
||||
|
||||
// Confirm dataset selector text in observability logs explorer
|
||||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.eql(testDatasetName);
|
||||
});
|
||||
|
||||
it('shows summary KPIs', async () => {
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const summary = await PageObjects.datasetQuality.parseFlyoutKpis(excludeKeysFromServerless);
|
||||
expect(summary).to.eql({
|
||||
docsCountTotal: '0',
|
||||
// size: '0.0 B', // `_stats` not available on Serverless
|
||||
services: '0',
|
||||
hosts: '0',
|
||||
degradedDocs: '0',
|
||||
});
|
||||
});
|
||||
|
||||
it('shows the updated KPIs', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const summaryBefore = await PageObjects.datasetQuality.parseFlyoutKpis(
|
||||
excludeKeysFromServerless
|
||||
);
|
||||
|
||||
// Set time range to 3 days ago
|
||||
const flyoutBodyContainer = await testSubjects.find(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutBody
|
||||
);
|
||||
await PageObjects.datasetQuality.setDatePickerLastXUnits(flyoutBodyContainer, 3, 'd');
|
||||
|
||||
// Index 2 doc 2 days ago
|
||||
const time2DaysAgo = Date.now() - 2 * 24 * 60 * 60 * 1000;
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: time2DaysAgo,
|
||||
count: 2,
|
||||
dataset: apacheAccessDatasetName,
|
||||
isMalformed: false,
|
||||
})
|
||||
);
|
||||
|
||||
// Index 5 degraded docs 2 days ago
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: time2DaysAgo,
|
||||
count: 5,
|
||||
dataset: apacheAccessDatasetName,
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.refreshFlyout();
|
||||
const summaryAfter = await PageObjects.datasetQuality.parseFlyoutKpis(
|
||||
excludeKeysFromServerless
|
||||
);
|
||||
|
||||
expect(parseInt(summaryAfter.docsCountTotal, 10)).to.be.greaterThan(
|
||||
parseInt(summaryBefore.docsCountTotal, 10)
|
||||
);
|
||||
|
||||
expect(parseInt(summaryAfter.degradedDocs, 10)).to.be.greaterThan(
|
||||
parseInt(summaryBefore.degradedDocs, 10)
|
||||
);
|
||||
|
||||
// `_stats` not available on Serverless so we can't compare size // https://github.com/elastic/kibana/issues/178954
|
||||
// expect(parseInt(summaryAfter.size, 10)).to.be.greaterThan(parseInt(summaryBefore.size, 10));
|
||||
|
||||
expect(parseInt(summaryAfter.services, 10)).to.be.greaterThan(
|
||||
parseInt(summaryBefore.services, 10)
|
||||
);
|
||||
expect(parseInt(summaryAfter.hosts, 10)).to.be.greaterThan(
|
||||
parseInt(summaryBefore.hosts, 10)
|
||||
);
|
||||
});
|
||||
|
||||
it('shows the right number of services', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const summaryBefore = await PageObjects.datasetQuality.parseFlyoutKpis(
|
||||
excludeKeysFromServerless
|
||||
);
|
||||
const testServices = ['test-srv-1', 'test-srv-2'];
|
||||
|
||||
// Index 2 docs with different services
|
||||
const timeNow = Date.now();
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: timeNow,
|
||||
count: 2,
|
||||
dataset: apacheAccessDatasetName,
|
||||
isMalformed: false,
|
||||
services: testServices,
|
||||
})
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.refreshFlyout();
|
||||
const summaryAfter = await PageObjects.datasetQuality.parseFlyoutKpis(
|
||||
excludeKeysFromServerless
|
||||
);
|
||||
|
||||
expect(parseInt(summaryAfter.services, 10)).to.eql(
|
||||
parseInt(summaryBefore.services, 10) + testServices.length
|
||||
);
|
||||
});
|
||||
|
||||
it('goes to log explorer for degraded docs when show all is clicked', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const degradedDocsShowAllSelector = `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutKpiLink}-${PageObjects.datasetQuality.texts.degradedDocs}`;
|
||||
await testSubjects.click(degradedDocsShowAllSelector);
|
||||
await browser.switchTab(1);
|
||||
|
||||
// Confirm dataset selector text in observability logs explorer
|
||||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.contain(apacheAccessDatasetName);
|
||||
|
||||
await browser.closeCurrentWindow();
|
||||
await browser.switchTab(0);
|
||||
});
|
||||
|
||||
// Blocked by https://github.com/elastic/kibana/issues/181705
|
||||
it.skip('goes to infra hosts for hosts when show all is clicked', async () => {
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const hostsShowAllSelector = `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutKpiLink}-${PageObjects.datasetQuality.texts.hosts}`;
|
||||
await testSubjects.click(hostsShowAllSelector);
|
||||
await browser.switchTab(1);
|
||||
|
||||
// Confirm url contains metrics/hosts
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
expect(parsedUrl.pathname).to.contain('/app/metrics/hosts');
|
||||
});
|
||||
|
||||
await browser.closeCurrentWindow();
|
||||
await browser.switchTab(0);
|
||||
});
|
||||
|
||||
it('Integration actions menu is present with correct actions', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
it('should show the integration actions menu with correct actions', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
|
@ -333,25 +181,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
);
|
||||
|
||||
expect(actions.length).to.eql(3);
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('Integration dashboard action hidden for integrations without dashboards', async () => {
|
||||
const bitbucketDatasetName = 'atlassian_bitbucket.audit';
|
||||
const bitbucketDatasetHumanName = 'Bitbucket Audit Logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.installPackage({
|
||||
name: 'atlassian_bitbucket',
|
||||
version: '1.14.0',
|
||||
});
|
||||
|
||||
// Index 10 logs for `atlassian_bitbucket.audit` dataset
|
||||
await synthtrace.index(getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName }));
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
it('should hide integration dashboard for integrations without dashboards', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(bitbucketDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
|
@ -360,25 +193,10 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
integrationActions.viewDashboards
|
||||
)
|
||||
);
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('Integration overview action should navigate to the integration overview page', async () => {
|
||||
const bitbucketDatasetName = 'atlassian_bitbucket.audit';
|
||||
const bitbucketDatasetHumanName = 'Bitbucket Audit Logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.installPackage({
|
||||
name: 'atlassian_bitbucket',
|
||||
version: '1.14.0',
|
||||
});
|
||||
|
||||
// Index 10 logs for `atlassian_bitbucket.audit` dataset
|
||||
await synthtrace.index(getLogsForDataset({ to, count: 10, dataset: bitbucketDatasetName }));
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
it('Should navigate to integration overview page on clicking integration overview action', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(bitbucketDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
|
@ -394,58 +212,31 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
expect(parsedUrl.pathname).to.contain('/app/integrations/detail/atlassian_bitbucket');
|
||||
});
|
||||
});
|
||||
|
||||
it('Integration template action should navigate to the index template page', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
it('should navigate to index template page in clicking Integration template', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction(
|
||||
integrationActions.template
|
||||
);
|
||||
|
||||
await action.click();
|
||||
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const action = await PageObjects.datasetQuality.getIntegrationActionButtonByAction(
|
||||
integrationActions.template
|
||||
);
|
||||
|
||||
await action.click();
|
||||
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
expect(parsedUrl.pathname).to.contain(
|
||||
`/app/management/data/index_management/templates/logs-${apacheAccessDatasetName}`
|
||||
);
|
||||
});
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
it('Integration dashboard action should navigate to the selected dashboard', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
it('should navigate to the selected dashboard on clicking integration dashboard action ', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
await PageObjects.datasetQuality.openIntegrationActionsMenu();
|
||||
|
||||
|
@ -464,149 +255,206 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const breadcrumbText = await testSubjects.getVisibleText('breadcrumb last');
|
||||
|
||||
expect(breadcrumbText).to.eql(dashboardText);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
});
|
||||
|
||||
// The above describe block has some failing/flaky tests which will
|
||||
// be fixed as part of the tech debt mentioned here
|
||||
// https://github.com/elastic/kibana/issues/184145
|
||||
// Until then, the below describe block is added to cover the tests for the
|
||||
// newly added degraded Fields Table. This must be merged under the above
|
||||
// describe block once the tech debt is fixed.
|
||||
//
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/184438
|
||||
describe.skip('Dataset quality flyout with degraded fields', () => {
|
||||
const goodDatasetName = 'good';
|
||||
const degradedDatasetName = 'degraded';
|
||||
const today = new Date().toISOString();
|
||||
describe('Degraded Fields Table with common data', () => {
|
||||
before(async () => {
|
||||
await PageObjects.svlCommonPage.loginWithRole('admin');
|
||||
await synthtrace.index([
|
||||
getLogsForDataset({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: goodDatasetName,
|
||||
isMalformed: false,
|
||||
}),
|
||||
createDegradedFieldsRecord({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
]);
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
describe('summary panel', () => {
|
||||
it('should show summary KPIs', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
});
|
||||
it('shows the degraded fields table with no data when no degraded fields are present', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(goodDatasetName);
|
||||
const { docsCountTotal, degradedDocs, services, hosts } =
|
||||
await PageObjects.datasetQuality.parseFlyoutKpis(excludeKeysFromServerless);
|
||||
expect(parseInt(docsCountTotal, 10)).to.be(226);
|
||||
expect(parseInt(degradedDocs, 10)).to.be(1);
|
||||
expect(parseInt(services, 10)).to.be(3);
|
||||
expect(parseInt(hosts, 10)).to.be(52);
|
||||
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutDegradedTableNoData
|
||||
);
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
});
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
describe('navigation', () => {
|
||||
it('should go to log explorer page when the open in log explorer button is clicked', async () => {
|
||||
const testDatasetName = datasetNames[2];
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(testDatasetName);
|
||||
|
||||
it('should load the degraded fields table with data', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
const logExplorerButton = await PageObjects.datasetQuality.getFlyoutLogsExplorerButton();
|
||||
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutDegradedFieldTable
|
||||
);
|
||||
await logExplorerButton.click();
|
||||
|
||||
const rows =
|
||||
await PageObjects.datasetQuality.getDatasetQualityFlyoutDegradedFieldTableRows();
|
||||
// Confirm dataset selector text in observability logs explorer
|
||||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.eql(testDatasetName);
|
||||
|
||||
expect(rows.length).to.eql(2);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should display Spark Plot for every row of degraded fields', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const rows =
|
||||
await PageObjects.datasetQuality.getDatasetQualityFlyoutDegradedFieldTableRows();
|
||||
|
||||
const sparkPlots = await testSubjects.findAll(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualitySparkPlot
|
||||
);
|
||||
|
||||
expect(rows.length).to.be(sparkPlots.length);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should sort the table when the count table header is clicked', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
|
||||
const countColumn = table['Docs count'];
|
||||
const cellTexts = await countColumn.getCellTexts();
|
||||
|
||||
await countColumn.sort('ascending');
|
||||
const sortedCellTexts = await countColumn.getCellTexts();
|
||||
|
||||
expect(cellTexts.reverse()).to.eql(sortedCellTexts);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
// Should bring back the test to the dataset quality page
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
describe('Degraded Fields Table with data ingestion', () => {
|
||||
before(async () => {
|
||||
await PageObjects.svlCommonPage.loginWithRole('admin');
|
||||
await synthtrace.index([
|
||||
getLogsForDataset({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: goodDatasetName,
|
||||
isMalformed: false,
|
||||
}),
|
||||
createDegradedFieldsRecord({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
]);
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
it('should go log explorer for degraded docs when the show all button is clicked', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const degradedDocsShowAllSelector = `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutKpiLink}-${PageObjects.datasetQuality.texts.degradedDocs}`;
|
||||
await testSubjects.click(degradedDocsShowAllSelector);
|
||||
await browser.switchTab(1);
|
||||
|
||||
// Confirm dataset selector text in observability logs explorer
|
||||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.contain(apacheAccessDatasetName);
|
||||
|
||||
await browser.closeCurrentWindow();
|
||||
await browser.switchTab(0);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
// Blocked by https://github.com/elastic/kibana/issues/181705
|
||||
// Its a test written ahead of its time.
|
||||
it.skip('goes to infra hosts for hosts when show all is clicked', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(apacheAccessDatasetHumanName);
|
||||
|
||||
const hostsShowAllSelector = `${PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutKpiLink}-${PageObjects.datasetQuality.texts.hosts}`;
|
||||
await testSubjects.click(hostsShowAllSelector);
|
||||
await browser.switchTab(1);
|
||||
|
||||
// Confirm url contains metrics/hosts
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
expect(parsedUrl.pathname).to.contain('/app/metrics/hosts');
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await browser.closeCurrentWindow();
|
||||
await browser.switchTab(0);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
});
|
||||
|
||||
describe('degraded fields table', () => {
|
||||
it(' should show empty degraded fields table when no degraded fields are present', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(datasetNames[0]);
|
||||
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutDegradedTableNoData
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should show the degraded fields table with data when present', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
await testSubjects.existOrFail(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFlyoutDegradedFieldTable
|
||||
);
|
||||
|
||||
const rows =
|
||||
await PageObjects.datasetQuality.getDatasetQualityFlyoutDegradedFieldTableRows();
|
||||
|
||||
expect(rows.length).to.eql(2);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should display Spark Plot for every row of degraded fields', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const rows =
|
||||
await PageObjects.datasetQuality.getDatasetQualityFlyoutDegradedFieldTableRows();
|
||||
|
||||
const sparkPlots = await testSubjects.findAll(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualitySparkPlot
|
||||
);
|
||||
|
||||
expect(rows.length).to.be(sparkPlots.length);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should sort the table when the count table header is clicked', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
|
||||
const countColumn = table['Docs count'];
|
||||
const cellTexts = await countColumn.getCellTexts();
|
||||
|
||||
await countColumn.sort('ascending');
|
||||
const sortedCellTexts = await countColumn.getCellTexts();
|
||||
|
||||
expect(cellTexts.reverse()).to.eql(sortedCellTexts);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
it('should update the URL when the table is sorted', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
const countColumn = table['Docs count'];
|
||||
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
const pageState = parsedUrl.searchParams.get('pageState');
|
||||
|
||||
expect(decodeURIComponent(pageState as string)).to.contain(
|
||||
'sort:(direction:desc,field:count)'
|
||||
);
|
||||
});
|
||||
it('should update the table when new data is ingested and the flyout is refreshed using the time selector', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
countColumn.sort('ascending');
|
||||
|
||||
const countColumn = table['Docs count'];
|
||||
const cellTexts = await countColumn.getCellTexts();
|
||||
const singleValuePreviously = parseInt(cellTexts[0], 10);
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const currentUrl = await browser.getCurrentUrl();
|
||||
const parsedUrl = new URL(currentUrl);
|
||||
const pageState = parsedUrl.searchParams.get('pageState');
|
||||
|
||||
await synthtrace.index([
|
||||
createDegradedFieldsRecord({
|
||||
to: today,
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
]);
|
||||
|
||||
await PageObjects.datasetQuality.refreshFlyout();
|
||||
|
||||
const updatedCellTexts = await countColumn.getCellTexts();
|
||||
|
||||
const singleValueNow = parseInt(updatedCellTexts[0], 10);
|
||||
|
||||
expect(singleValueNow).to.be(singleValuePreviously * 2);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
expect(decodeURIComponent(pageState as string)).to.contain(
|
||||
'sort:(direction:asc,field:count)'
|
||||
);
|
||||
});
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
|
||||
// This is the only test which ingest data during the test.
|
||||
// This block tests the refresh behavior of the degraded fields table.
|
||||
// Even though this test ingest data, it can also be freely moved inside
|
||||
// this describe block, and it won't affect any of the existing tests
|
||||
it('should update the table when new data is ingested and the flyout is refreshed using the time selector', async () => {
|
||||
await PageObjects.datasetQuality.openDatasetFlyout(degradedDatasetName);
|
||||
|
||||
const table = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
|
||||
const countColumn = table['Docs count'];
|
||||
const cellTexts = await countColumn.getCellTexts();
|
||||
|
||||
await synthtrace.index([
|
||||
createDegradedFieldsRecord({
|
||||
to: new Date().toISOString(),
|
||||
count: 2,
|
||||
dataset: degradedDatasetName,
|
||||
}),
|
||||
]);
|
||||
|
||||
await PageObjects.datasetQuality.refreshFlyout();
|
||||
|
||||
const updatedTable = await PageObjects.datasetQuality.parseDegradedFieldTable();
|
||||
const updatedCountColumn = updatedTable['Docs count'];
|
||||
|
||||
const updatedCellTexts = await updatedCountColumn.getCellTexts();
|
||||
|
||||
const singleValuePreviously = parseInt(cellTexts[0], 10);
|
||||
const singleValueNow = parseInt(updatedCellTexts[0], 10);
|
||||
|
||||
expect(singleValueNow).to.be.greaterThan(singleValuePreviously);
|
||||
|
||||
await PageObjects.datasetQuality.closeFlyout();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -17,11 +17,37 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
'svlCommonPage',
|
||||
]);
|
||||
const synthtrace = getService('svlLogsSynthtraceClient');
|
||||
const browser = getService('browser');
|
||||
const retry = getService('retry');
|
||||
const to = '2024-01-01T12:00:00.000Z';
|
||||
const excludeKeysFromServerless = ['estimatedData']; // https://github.com/elastic/kibana/issues/178954
|
||||
|
||||
const ingestDataForSummary = async () => {
|
||||
// Ingest documents for 3 type of datasets
|
||||
return synthtrace.index([
|
||||
// Ingest good data to all 3 datasets
|
||||
getInitialTestLogs({ to, count: 4 }),
|
||||
// Ingesting poor data to one dataset
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[1],
|
||||
isMalformed: true,
|
||||
}),
|
||||
// Ingesting degraded docs into another dataset by ingesting malformed 1st and then good data
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
}),
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 10,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: false,
|
||||
}),
|
||||
]);
|
||||
};
|
||||
|
||||
describe('Dataset quality summary', () => {
|
||||
before(async () => {
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
|
@ -29,110 +55,31 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
afterEach(async () => {
|
||||
await synthtrace.clean();
|
||||
});
|
||||
|
||||
it('shows poor, degraded and good count', async () => {
|
||||
it('shows poor, degraded and good count as 0 and all dataset as healthy', async () => {
|
||||
await PageObjects.datasetQuality.refreshTable();
|
||||
const summary = await PageObjects.datasetQuality.parseSummaryPanel(excludeKeysFromServerless);
|
||||
expect(summary).to.eql({
|
||||
datasetHealthPoor: '0',
|
||||
datasetHealthDegraded: '0',
|
||||
datasetHealthGood: '3',
|
||||
activeDatasets: '0 of 3',
|
||||
// estimatedData: '0.0 B', https://github.com/elastic/kibana/issues/178954
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the poor count when degraded docs are ingested', async () => {
|
||||
// Index malformed document with current timestamp
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
it('shows updated count for poor, degraded and good datasets and updates active datasets', async () => {
|
||||
await ingestDataForSummary();
|
||||
await PageObjects.datasetQuality.refreshTable();
|
||||
|
||||
await browser.refresh();
|
||||
await PageObjects.datasetQuality.waitUntilSummaryPanelLoaded();
|
||||
|
||||
await retry.try(async () => {
|
||||
const summary = await PageObjects.datasetQuality.parseSummaryPanel(
|
||||
excludeKeysFromServerless
|
||||
);
|
||||
const { estimatedData, ...restOfSummary } = summary;
|
||||
expect(restOfSummary).to.eql({
|
||||
datasetHealthPoor: '1',
|
||||
datasetHealthDegraded: '0',
|
||||
datasetHealthGood: '2',
|
||||
activeDatasets: '1 of 3',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the degraded count when degraded docs are ingested', async () => {
|
||||
// Index malformed document with current timestamp
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[1],
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
|
||||
// Index healthy documents
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 10,
|
||||
dataset: datasetNames[1],
|
||||
isMalformed: false,
|
||||
})
|
||||
);
|
||||
|
||||
await browser.refresh();
|
||||
await PageObjects.datasetQuality.waitUntilSummaryPanelLoaded();
|
||||
|
||||
await retry.try(async () => {
|
||||
const { estimatedData, ...restOfSummary } =
|
||||
await PageObjects.datasetQuality.parseSummaryPanel(excludeKeysFromServerless);
|
||||
expect(restOfSummary).to.eql({
|
||||
datasetHealthPoor: '1',
|
||||
datasetHealthDegraded: '1',
|
||||
datasetHealthGood: '1',
|
||||
activeDatasets: '2 of 3',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('updates active datasets and estimated data KPIs', async () => {
|
||||
const { estimatedData: _existingEstimatedData } =
|
||||
await PageObjects.datasetQuality.parseSummaryPanel(excludeKeysFromServerless);
|
||||
|
||||
// Index document at current time to mark dataset as active
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 4,
|
||||
dataset: datasetNames[0],
|
||||
isMalformed: false,
|
||||
})
|
||||
);
|
||||
|
||||
await browser.refresh(); // Summary panel doesn't update reactively
|
||||
await PageObjects.datasetQuality.waitUntilSummaryPanelLoaded();
|
||||
|
||||
await retry.try(async () => {
|
||||
const { activeDatasets: updatedActiveDatasets, estimatedData: _updatedEstimatedData } =
|
||||
await PageObjects.datasetQuality.parseSummaryPanel(excludeKeysFromServerless);
|
||||
|
||||
expect(updatedActiveDatasets).to.eql('3 of 3');
|
||||
|
||||
// TODO: `_stats` not available on Serverless. // https://github.com/elastic/kibana/issues/178954
|
||||
// expect(_updatedEstimatedData).to.not.eql(_existingEstimatedData);
|
||||
const summary = await PageObjects.datasetQuality.parseSummaryPanel(excludeKeysFromServerless);
|
||||
expect(summary).to.eql({
|
||||
datasetHealthPoor: '1',
|
||||
datasetHealthDegraded: '1',
|
||||
datasetHealthGood: '1',
|
||||
activeDatasets: '2 of 3',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { datasetNames, defaultNamespace, getInitialTestLogs, getLogsForDataset } from './data';
|
||||
import {
|
||||
datasetNames,
|
||||
defaultNamespace,
|
||||
getInitialTestLogs,
|
||||
getLogsForDataset,
|
||||
productionNamespace,
|
||||
} from './data';
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const PageObjects = getPageObjects([
|
||||
|
@ -19,42 +25,79 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
'svlCommonPage',
|
||||
]);
|
||||
const synthtrace = getService('svlLogsSynthtraceClient');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const retry = getService('retry');
|
||||
const to = '2024-01-01T12:00:00.000Z';
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const pkg = {
|
||||
name: 'apache',
|
||||
version: '1.14.0',
|
||||
};
|
||||
|
||||
describe('Dataset quality table', function () {
|
||||
this.tags(['failsOnMKI']); // Failing https://github.com/elastic/kibana/issues/183495
|
||||
|
||||
before(async () => {
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
// Install Integration and ingest logs for it
|
||||
await PageObjects.observabilityLogsExplorer.installPackage(pkg);
|
||||
// Ingest basic logs
|
||||
await synthtrace.index([
|
||||
// Ingest basic logs
|
||||
getInitialTestLogs({ to, count: 4 }),
|
||||
// Ingest Degraded Logs
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
}),
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
getLogsForDataset({
|
||||
to,
|
||||
count: 10,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: productionNamespace,
|
||||
}),
|
||||
]);
|
||||
await PageObjects.svlCommonPage.loginWithRole('admin');
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await synthtrace.clean();
|
||||
await PageObjects.observabilityLogsExplorer.removeInstalledPackages();
|
||||
await PageObjects.observabilityLogsExplorer.uninstallPackage(pkg);
|
||||
});
|
||||
|
||||
it('shows the right number of rows in correct order', async () => {
|
||||
it('shows sort by dataset name and show namespace', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
await datasetNameCol.sort('descending');
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTexts).to.eql([...datasetNames].reverse());
|
||||
expect(datasetNameColCellTexts).to.eql(
|
||||
[apacheAccessDatasetHumanName, ...datasetNames].reverse()
|
||||
);
|
||||
|
||||
const namespaceCol = cols.Namespace;
|
||||
const namespaceColCellTexts = await namespaceCol.getCellTexts();
|
||||
expect(namespaceColCellTexts).to.eql([defaultNamespace, defaultNamespace, defaultNamespace]);
|
||||
expect(namespaceColCellTexts).to.eql([
|
||||
defaultNamespace,
|
||||
defaultNamespace,
|
||||
defaultNamespace,
|
||||
productionNamespace,
|
||||
]);
|
||||
|
||||
const degradedDocsCol = cols['Degraded Docs (%)'];
|
||||
const degradedDocsColCellTexts = await degradedDocsCol.getCellTexts();
|
||||
expect(degradedDocsColCellTexts).to.eql(['0%', '0%', '0%']);
|
||||
// Cleaning the sort
|
||||
await datasetNameCol.sort('ascending');
|
||||
});
|
||||
|
||||
it('shows the last activity', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const lastActivityCol = cols['Last Activity'];
|
||||
const lastActivityColCellTexts = await lastActivityCol.getCellTexts();
|
||||
expect(lastActivityColCellTexts).to.eql([
|
||||
const activityCells = await lastActivityCol.getCellTexts();
|
||||
const lastActivityCell = activityCells[activityCells.length - 1];
|
||||
const restActivityCells = activityCells.slice(0, -1);
|
||||
|
||||
// The first cell of lastActivity should have data
|
||||
expect(lastActivityCell).to.not.eql(PageObjects.datasetQuality.texts.noActivityText);
|
||||
// The rest of the rows must show no activity
|
||||
expect(restActivityCells).to.eql([
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
|
@ -63,112 +106,19 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
|
||||
it('shows degraded docs percentage', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
await datasetNameCol.sort('ascending');
|
||||
|
||||
const degradedDocsCol = cols['Degraded Docs (%)'];
|
||||
const degradedDocsColCellTexts = await degradedDocsCol.getCellTexts();
|
||||
expect(degradedDocsColCellTexts).to.eql(['0%', '0%', '0%']);
|
||||
|
||||
// Index malformed document with current timestamp
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
|
||||
// Set time range to Last 5 minute
|
||||
const filtersContainer = await testSubjects.find(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFiltersContainer
|
||||
);
|
||||
await PageObjects.datasetQuality.setDatePickerLastXUnits(filtersContainer, 5, 'm');
|
||||
|
||||
const updatedDegradedDocsColCellTexts = await degradedDocsCol.getCellTexts();
|
||||
expect(updatedDegradedDocsColCellTexts[2]).to.not.eql('0%');
|
||||
});
|
||||
|
||||
// https://github.com/elastic/kibana/issues/178954
|
||||
it.skip('shows the updated size of the index', async () => {
|
||||
const testDatasetIndex = 2;
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
await datasetNameCol.sort('ascending');
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
|
||||
const datasetToUpdateRowIndex = datasetNameColCellTexts.findIndex(
|
||||
(dName: string) => dName === datasetNames[testDatasetIndex]
|
||||
);
|
||||
|
||||
const sizeColCellTexts = await cols.Size.getCellTexts();
|
||||
const beforeSize = sizeColCellTexts[datasetToUpdateRowIndex];
|
||||
|
||||
// Index documents with current timestamp
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: Date.now(),
|
||||
count: 4,
|
||||
dataset: datasetNames[testDatasetIndex],
|
||||
isMalformed: false,
|
||||
})
|
||||
);
|
||||
|
||||
const colsAfterUpdate = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
|
||||
// Assert that size has changed
|
||||
await retry.tryForTime(15000, async () => {
|
||||
// Refresh the table
|
||||
await PageObjects.datasetQuality.refreshTable();
|
||||
const updatedSizeColCellTexts = await colsAfterUpdate.Size.getCellTexts();
|
||||
expect(updatedSizeColCellTexts[datasetToUpdateRowIndex]).to.not.eql(beforeSize);
|
||||
});
|
||||
});
|
||||
|
||||
it('sorts by dataset name', async () => {
|
||||
// const header = await PageObjects.datasetQuality.getDatasetTableHeader('Dataset Name');
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
|
||||
// Sort ascending
|
||||
await datasetNameCol.sort('ascending');
|
||||
const cellTexts = await datasetNameCol.getCellTexts();
|
||||
|
||||
const datasetNamesAsc = [...datasetNames].sort();
|
||||
|
||||
expect(cellTexts).to.eql(datasetNamesAsc);
|
||||
expect(degradedDocsColCellTexts).to.eql(['0%', '0%', '0%', '100%']);
|
||||
});
|
||||
|
||||
it('shows dataset from integration', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
|
||||
// Sort ascending
|
||||
await datasetNameCol.sort('ascending');
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
|
||||
const datasetNamesAsc = [...datasetNames, apacheAccessDatasetHumanName].sort();
|
||||
|
||||
// Assert there are 4 rows
|
||||
expect(datasetNameColCellTexts.length).to.eql(4);
|
||||
|
||||
expect(datasetNameColCellTexts).to.eql(datasetNamesAsc);
|
||||
expect(datasetNameColCellTexts[0]).to.eql(apacheAccessDatasetHumanName);
|
||||
});
|
||||
|
||||
it('goes to log explorer page when opened', async () => {
|
||||
|
@ -184,50 +134,12 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const datasetSelectorText =
|
||||
await PageObjects.observabilityLogsExplorer.getDataSourceSelectorButtonText();
|
||||
expect(datasetSelectorText).to.eql(datasetName);
|
||||
});
|
||||
|
||||
it('shows the last activity when in time range', async () => {
|
||||
// Return to Dataset Quality Page
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const lastActivityCol = cols['Last Activity'];
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
|
||||
// Set time range to Last 1 minute
|
||||
const filtersContainer = await testSubjects.find(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFiltersContainer
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.setDatePickerLastXUnits(filtersContainer, 1, 's');
|
||||
const lastActivityColCellTexts = await lastActivityCol.getCellTexts();
|
||||
expect(lastActivityColCellTexts).to.eql([
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
PageObjects.datasetQuality.texts.noActivityText,
|
||||
]);
|
||||
|
||||
const datasetToUpdate = datasetNames[0];
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to: new Date().toISOString(), count: 1, dataset: datasetToUpdate })
|
||||
);
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
const datasetToUpdateRowIndex = datasetNameColCellTexts.findIndex(
|
||||
(dName: string) => dName === datasetToUpdate
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.setDatePickerLastXUnits(filtersContainer, 1, 'h');
|
||||
|
||||
await retry.tryForTime(5000, async () => {
|
||||
const updatedLastActivityColCellTexts = await lastActivityCol.getCellTexts();
|
||||
expect(updatedLastActivityColCellTexts[datasetToUpdateRowIndex]).to.not.eql(
|
||||
PageObjects.datasetQuality.texts.noActivityText
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('hides inactive datasets', async () => {
|
||||
await PageObjects.datasetQuality.waitUntilTableLoaded();
|
||||
|
||||
// Get number of rows with Last Activity not equal to "No activity in the selected timeframe"
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const lastActivityCol = cols['Last Activity'];
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../../ftr_provider_context';
|
||||
import { datasetNames, defaultNamespace, getInitialTestLogs, getLogsForDataset } from './data';
|
||||
import { datasetNames, getInitialTestLogs, getLogsForDataset, productionNamespace } from './data';
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const PageObjects = getPageObjects([
|
||||
|
@ -20,58 +20,73 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const synthtrace = getService('svlLogsSynthtraceClient');
|
||||
const testSubjects = getService('testSubjects');
|
||||
const to = '2024-01-01T12:00:00.000Z';
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const apacheIntegrationName = 'Apache HTTP Server';
|
||||
const pkg = {
|
||||
name: 'apache',
|
||||
version: '1.14.0',
|
||||
};
|
||||
const allDatasetNames = [apacheAccessDatasetHumanName, ...datasetNames];
|
||||
|
||||
describe('Dataset quality table filters', function () {
|
||||
this.tags(['failsOnMKI']); // Failing https://github.com/elastic/kibana/issues/183495
|
||||
|
||||
before(async () => {
|
||||
await synthtrace.index(getInitialTestLogs({ to, count: 4 }));
|
||||
// Install Integration and ingest logs for it
|
||||
await PageObjects.observabilityLogsExplorer.installPackage(pkg);
|
||||
// Ingest basic logs
|
||||
await synthtrace.index([
|
||||
// Ingest basic logs
|
||||
getInitialTestLogs({ to, count: 4 }),
|
||||
// Ingest Degraded Logs
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 1,
|
||||
dataset: datasetNames[2],
|
||||
isMalformed: true,
|
||||
}),
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
getLogsForDataset({
|
||||
to,
|
||||
count: 10,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: productionNamespace,
|
||||
}),
|
||||
]);
|
||||
await PageObjects.svlCommonPage.loginWithRole('admin');
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await PageObjects.observabilityLogsExplorer.uninstallPackage(pkg);
|
||||
await synthtrace.clean();
|
||||
await PageObjects.observabilityLogsExplorer.removeInstalledPackages();
|
||||
});
|
||||
|
||||
it('hides inactive datasets when toggled', async () => {
|
||||
const initialRows = await PageObjects.datasetQuality.getDatasetTableRows();
|
||||
expect(initialRows.length).to.eql(3);
|
||||
|
||||
await PageObjects.datasetQuality.toggleShowInactiveDatasets();
|
||||
|
||||
const afterToggleRows = await PageObjects.datasetQuality.getDatasetTableRows();
|
||||
expect(afterToggleRows.length).to.eql(1);
|
||||
|
||||
await PageObjects.datasetQuality.toggleShowInactiveDatasets();
|
||||
|
||||
const afterReToggleRows = await PageObjects.datasetQuality.getDatasetTableRows();
|
||||
expect(afterReToggleRows.length).to.eql(3);
|
||||
});
|
||||
|
||||
it('shows full dataset names when toggled', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTexts).to.eql(datasetNames);
|
||||
expect(datasetNameColCellTexts).to.eql(allDatasetNames);
|
||||
|
||||
await PageObjects.datasetQuality.toggleShowFullDatasetNames();
|
||||
|
||||
const datasetNameColCellTextsAfterToggle = await datasetNameCol.getCellTexts();
|
||||
const duplicateNames = datasetNames.map((name) => `${name}\n${name}`);
|
||||
const duplicateNames = [
|
||||
`${apacheAccessDatasetHumanName}\n${apacheAccessDatasetName}`,
|
||||
...datasetNames.map((name) => `${name}\n${name}`),
|
||||
];
|
||||
expect(datasetNameColCellTextsAfterToggle).to.eql(duplicateNames);
|
||||
|
||||
// resetting the toggle
|
||||
await PageObjects.datasetQuality.toggleShowFullDatasetNames();
|
||||
const datasetNameColCellTextsAfterReToggle = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTextsAfterReToggle).to.eql(datasetNames);
|
||||
expect(datasetNameColCellTextsAfterReToggle).to.eql(allDatasetNames);
|
||||
});
|
||||
|
||||
it('searches the datasets', async () => {
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTexts).to.eql(datasetNames);
|
||||
expect(datasetNameColCellTexts).to.eql(allDatasetNames);
|
||||
|
||||
// Search for a dataset
|
||||
await testSubjects.setValue(
|
||||
|
@ -83,33 +98,15 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const datasetNameColAfterSearch = colsAfterSearch['Data Set Name'];
|
||||
const datasetNameColCellTextsAfterSearch = await datasetNameColAfterSearch.getCellTexts();
|
||||
expect(datasetNameColCellTextsAfterSearch).to.eql([datasetNames[2]]);
|
||||
await testSubjects.setValue(
|
||||
PageObjects.datasetQuality.testSubjectSelectors.datasetQualityFilterBarFieldSearch,
|
||||
''
|
||||
);
|
||||
// Reset the search field
|
||||
await testSubjects.click('clearSearchButton');
|
||||
});
|
||||
|
||||
it('filters for integration', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const apacheAccessDatasetHumanName = 'Apache access logs';
|
||||
const apacheIntegrationName = 'Apache HTTP Server';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({ to, count: 10, dataset: apacheAccessDatasetName })
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetNameCol = cols['Data Set Name'];
|
||||
const datasetNameColCellTexts = await datasetNameCol.getCellTexts();
|
||||
expect(datasetNameColCellTexts).to.eql([apacheAccessDatasetHumanName, ...datasetNames]);
|
||||
expect(datasetNameColCellTexts).to.eql(allDatasetNames);
|
||||
|
||||
// Filter for integration
|
||||
await PageObjects.datasetQuality.filterForIntegrations([apacheIntegrationName]);
|
||||
|
@ -118,64 +115,31 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const datasetNameColAfterFilter = colsAfterFilter['Data Set Name'];
|
||||
const datasetNameColCellTextsAfterFilter = await datasetNameColAfterFilter.getCellTexts();
|
||||
expect(datasetNameColCellTextsAfterFilter).to.eql([apacheAccessDatasetHumanName]);
|
||||
// Reset the filter by selecting from the dropdown again
|
||||
await PageObjects.datasetQuality.filterForIntegrations([apacheIntegrationName]);
|
||||
});
|
||||
|
||||
it('filters for namespace', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const datasetNamespace = 'prod';
|
||||
|
||||
await PageObjects.observabilityLogsExplorer.navigateTo();
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to,
|
||||
count: 10,
|
||||
dataset: apacheAccessDatasetName,
|
||||
namespace: datasetNamespace,
|
||||
})
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
// Get default namespaces
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const namespaceCol = cols.Namespace;
|
||||
const namespaceColCellTexts = await namespaceCol.getCellTexts();
|
||||
expect(namespaceColCellTexts).to.contain(defaultNamespace);
|
||||
expect(namespaceColCellTexts).to.contain(productionNamespace);
|
||||
|
||||
// Filter for prod namespace
|
||||
await PageObjects.datasetQuality.filterForNamespaces([datasetNamespace]);
|
||||
await PageObjects.datasetQuality.filterForNamespaces([productionNamespace]);
|
||||
|
||||
const colsAfterFilter = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const namespaceColAfterFilter = colsAfterFilter.Namespace;
|
||||
const namespaceColCellTextsAfterFilter = await namespaceColAfterFilter.getCellTexts();
|
||||
|
||||
expect(namespaceColCellTextsAfterFilter).to.eql([datasetNamespace]);
|
||||
expect(namespaceColCellTextsAfterFilter).to.eql([productionNamespace]);
|
||||
// Reset the namespace by selecting from the dropdown again
|
||||
await PageObjects.datasetQuality.filterForNamespaces([productionNamespace]);
|
||||
});
|
||||
|
||||
it('filters for quality', async () => {
|
||||
const apacheAccessDatasetName = 'apache.access';
|
||||
const expectedQuality = 'Poor';
|
||||
|
||||
// Add initial integrations
|
||||
await PageObjects.observabilityLogsExplorer.setupInitialIntegrations();
|
||||
|
||||
// Index 10 logs for `logs-apache.access` dataset
|
||||
await synthtrace.index(
|
||||
getLogsForDataset({
|
||||
to: new Date().toISOString(),
|
||||
count: 10,
|
||||
dataset: apacheAccessDatasetName,
|
||||
isMalformed: true,
|
||||
})
|
||||
);
|
||||
|
||||
await PageObjects.datasetQuality.navigateTo();
|
||||
|
||||
// Get default quality
|
||||
const cols = await PageObjects.datasetQuality.parseDatasetTable();
|
||||
const datasetQuality = cols['Data Set Quality'];
|
||||
|
@ -190,6 +154,9 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const datasetQualityCellTextsAfterFilter = await datasetQualityAfterFilter.getCellTexts();
|
||||
|
||||
expect(datasetQualityCellTextsAfterFilter).to.eql([expectedQuality]);
|
||||
|
||||
// Reset the namespace by selecting from the dropdown again
|
||||
await PageObjects.datasetQuality.filterForQualities([expectedQuality]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue