Skip to content

Commit

Permalink
[Observability] Set observable initial value to prevent rerender (ela…
Browse files Browse the repository at this point in the history
…stic#184652)

## 📓 Summary

Adding an initial value to the `useObservable` call prevents its value
from flapping and rendering twice the whole child application.

This issue is similar to what was reported in elastic#181968 and also its
solution lies on a similar error.

I added a functional test for both stateful/serverless environments to
assert this behaviour doesn't break anymore.

---------

Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co>
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
  • Loading branch information
3 people authored and rohanxz committed Jun 4, 2024
1 parent 0c5ad8f commit ab3f8e2
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function createLazyObservabilityPageTemplate({
...injectedDeps
}: ObservabilityPageTemplateDependencies) {
return (pageTemplateProps: LazyObservabilityPageTemplateProps) => {
const isSidebarEnabled = useObservable(isSidebarEnabled$);
const isSidebarEnabled = useObservable(isSidebarEnabled$, isSidebarEnabled$.getValue());
const { showSolutionNav: showSolutionNavProp, ...props } = pageTemplateProps;
const showSolutionNav = Boolean(!!showSolutionNavProp || isSidebarEnabled);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./filter_controls'));
loadTestFile(require.resolve('./flyout'));
loadTestFile(require.resolve('./header_menu'));
loadTestFile(require.resolve('./navigation'));
});
}
126 changes: 126 additions & 0 deletions x-pack/test/functional/apps/observability_logs_explorer/navigation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import moment from 'moment/moment';
import { log, timerange } from '@kbn/apm-synthtrace-client';
import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services';
import { FtrProviderContext } from './config';

export default function ({ getService, getPageObjects }: FtrProviderContext) {
const browser = getService('browser');
const retry = getService('retry');
const PageObjects = getPageObjects(['discover', 'observabilityLogsExplorer']);
const synthtrace = getService('logSynthtraceEsClient');
const kibanaServer = getService('kibanaServer');
const from = '2023-12-27T10:24:14.035Z';
const to = '2023-12-27T10:25:14.091Z';

const navigateToLogsExplorer = () =>
PageObjects.observabilityLogsExplorer.navigateTo({
pageState: {
time: {
from,
to,
mode: 'absolute',
},
},
});

describe('Navigation', () => {
before(async () => {
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
await synthtrace.index(generateLogsData({ to }));
await navigateToLogsExplorer();
});

after(async () => {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
await synthtrace.clean();
});

it('should correctly restore the previous selection and data when navigating back from another page', async () => {
await PageObjects.observabilityLogsExplorer.openDataSourceSelector();
await PageObjects.observabilityLogsExplorer
.getUncategorizedTab()
.then((tab: WebElementWrapper) => tab.click());

await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogsExplorer
.getUncategorizedContextMenu()
.then((menu: WebElementWrapper) =>
PageObjects.observabilityLogsExplorer.getPanelEntries(menu)
);

expect(await menuEntries[0].getVisibleText()).to.be('synth');
menuEntries[0].click();
});

// Assert selection is loaded correctly
const rows = await PageObjects.discover.getDocTableRows();
expect(rows.length).to.equal(1);

// Navigate to Discover
const discoverLink = await PageObjects.observabilityLogsExplorer.getDiscoverFallbackLink();
await discoverLink.click();
await PageObjects.discover.waitForDocTableLoadingComplete();

// Navigate back to Logs Explorer using browser navigation
await browser.goBack();
await PageObjects.discover.waitForDocTableLoadingComplete();

// Assert selection data is restored correctly
const restoredRows = await PageObjects.discover.getDocTableRows();
expect(restoredRows.length).to.equal(1);

// Change selection to all logs to assert its data are loaded
await PageObjects.observabilityLogsExplorer.openDataSourceSelector();
const allLogsButton = await PageObjects.observabilityLogsExplorer.getAllLogsButton();
await allLogsButton.click();
await PageObjects.discover.waitForDocTableLoadingComplete();

// Assert new selection data is loaded correctly
const allLogsRows = await PageObjects.discover.getDocTableRows();
expect(allLogsRows.length).to.equal(2);
});
});
}

function generateLogsData({ to, count = 1 }: { to: string; count?: number }) {
const logsSynth = timerange(moment(to).subtract(1, 'second'), moment(to))
.interval('1m')
.rate(1)
.generator((timestamp) =>
Array(count)
.fill(0)
.map(() => {
return log
.create()
.message('A sample log')
.logLevel('info')
.timestamp(timestamp)
.defaults({ 'service.name': 'synth-service' });
})
);

const logsSystem = timerange(moment(to).subtract(2, 'second'), moment(to).subtract(1, 'second'))
.interval('1m')
.rate(1)
.generator((timestamp) =>
Array(count)
.fill(0)
.map(() => {
return log
.create()
.dataset('system')
.message('A sample log')
.timestamp(timestamp)
.defaults({ 'service.name': 'system-service' });
})
);

return [logsSynth, logsSystem];
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ export default function ({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./filter_controls'));
loadTestFile(require.resolve('./flyout'));
loadTestFile(require.resolve('./header_menu'));
loadTestFile(require.resolve('./navigation'));
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import expect from '@kbn/expect';
import moment from 'moment/moment';
import { log, timerange } from '@kbn/apm-synthtrace-client';
import { WebElementWrapper } from '@kbn/ftr-common-functional-ui-services';
import { FtrProviderContext } from '../../../ftr_provider_context';

export default function ({ getService, getPageObjects }: FtrProviderContext) {
const browser = getService('browser');
const retry = getService('retry');
const PageObjects = getPageObjects(['discover', 'observabilityLogsExplorer', 'svlCommonPage']);
const synthtrace = getService('svlLogsSynthtraceClient');
const kibanaServer = getService('kibanaServer');
const from = '2023-12-27T10:24:14.035Z';
const to = '2023-12-27T10:25:14.091Z';

const navigateToLogsExplorer = () =>
PageObjects.observabilityLogsExplorer.navigateTo({
pageState: {
time: {
from,
to,
mode: 'absolute',
},
},
});

describe('Navigation', () => {
before(async () => {
await kibanaServer.importExport.load('test/functional/fixtures/kbn_archiver/discover');
await synthtrace.index(generateLogsData({ to }));
await PageObjects.svlCommonPage.login();
await navigateToLogsExplorer();
});

after(async () => {
await kibanaServer.importExport.unload('test/functional/fixtures/kbn_archiver/discover');
await synthtrace.clean();
});

it('should correctly restore the previous selection and data when navigating back from another page', async () => {
await PageObjects.observabilityLogsExplorer.openDataSourceSelector();
await PageObjects.observabilityLogsExplorer
.getUncategorizedTab()
.then((tab: WebElementWrapper) => tab.click());

await retry.try(async () => {
const menuEntries = await PageObjects.observabilityLogsExplorer
.getUncategorizedContextMenu()
.then((menu: WebElementWrapper) =>
PageObjects.observabilityLogsExplorer.getPanelEntries(menu)
);

expect(await menuEntries[0].getVisibleText()).to.be('synth');
menuEntries[0].click();
});

// Assert selection is loaded correctly
const rows = await PageObjects.discover.getDocTableRows();
expect(rows.length).to.equal(1);

// Navigate to Discover
const discoverLink = await PageObjects.observabilityLogsExplorer.getDiscoverFallbackLink();
await discoverLink.click();
await PageObjects.discover.waitForDocTableLoadingComplete();

// Navigate back to Logs Explorer using browser navigation
await browser.goBack();
await PageObjects.discover.waitForDocTableLoadingComplete();

// Assert selection data is restored correctly
const restoredRows = await PageObjects.discover.getDocTableRows();
expect(restoredRows.length).to.equal(1);

// Change selection to all logs to assert its data are loaded
await PageObjects.observabilityLogsExplorer.openDataSourceSelector();
const allLogsButton = await PageObjects.observabilityLogsExplorer.getAllLogsButton();
await allLogsButton.click();
await PageObjects.discover.waitForDocTableLoadingComplete();

// Assert new selection data is loaded correctly
const allLogsRows = await PageObjects.discover.getDocTableRows();
expect(allLogsRows.length).to.equal(2);
});
});
}

function generateLogsData({ to, count = 1 }: { to: string; count?: number }) {
const logsSynth = timerange(moment(to).subtract(1, 'second'), moment(to))
.interval('1m')
.rate(1)
.generator((timestamp) =>
Array(count)
.fill(0)
.map(() => {
return log
.create()
.message('A sample log')
.logLevel('info')
.timestamp(timestamp)
.defaults({ 'service.name': 'synth-service' });
})
);

const logsSystem = timerange(moment(to).subtract(2, 'second'), moment(to).subtract(1, 'second'))
.interval('1m')
.rate(1)
.generator((timestamp) =>
Array(count)
.fill(0)
.map(() => {
return log
.create()
.dataset('system')
.message('A sample log')
.timestamp(timestamp)
.defaults({ 'service.name': 'system-service' });
})
);

return [logsSynth, logsSystem];
}

0 comments on commit ab3f8e2

Please sign in to comment.