mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Improve ui applications
navigation functional tests (#171314)
## Summary Fix https://github.com/elastic/kibana/issues/53356 The PR brings a couple of main improvements: * `apps_menu.clickLink()` the logic is sometimes not able to find the right element "by partial link name". Adding a `category` parameter helped narrow it down, but in some scenarios our applications are not under any section, so we can't leverage this parameter. I have rewritten the case where no `category` is specified, in order to fetch all side nav links first, and then filter for the matching ones. This seems to behave correctly 100% of times (at least confirmed by FTR below). * Adds the extra `category` parameter for those scenarios where it can be passed. It helps disambiguate when multiple links might match the text (e.g. _Recent_), and reduces the odds of tests being flaky. --- ➡️ Flaky Test Runner Pipeline - 200x 🔴 https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4004 ➡️ Rewrite [apps_menu.clickLink()](5014947f6e
) ➡️ Flaky Test Runner Pipeline - 150x 🟢 https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4011 ➡️ CI [failed](https://buildkite.com/elastic/kibana-pull-request/builds/176461), so I pushed [this commit](943d533156
). ➡️ One last Flaky Test Runner Pipeline - 200x 🟢 https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/4028
This commit is contained in:
parent
543e8659d7
commit
1bc5b6f43e
4 changed files with 79 additions and 34 deletions
|
@ -107,7 +107,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
"to:'2015-09-23T18:31:44.000Z'))&_a=(columns:!(),filters:!(),index:'logstash-*'," +
|
||||
"interval:auto,query:(language:kuery,query:''),sort:!(!('@timestamp',desc)))"
|
||||
);
|
||||
await appsMenu.clickLink('Discover');
|
||||
await appsMenu.clickLink('Discover', { category: 'kibana' });
|
||||
await PageObjects.header.waitUntilLoadingHasFinished();
|
||||
expect(await filterBar.hasFilter('extension.raw', '', undefined, true)).to.be(true);
|
||||
expect(await filterBar.isFilterPinned('extension.raw')).to.be(true);
|
||||
|
|
|
@ -36,13 +36,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
const homeUrl = await browser.getCurrentUrl();
|
||||
|
||||
// Navigate to discover app
|
||||
await appsMenu.clickLink('Discover');
|
||||
await appsMenu.clickLink('Discover', { category: 'kibana' });
|
||||
const discoverUrl = await browser.getCurrentUrl();
|
||||
await PageObjects.timePicker.setDefaultAbsoluteRange();
|
||||
const modifiedTimeDiscoverUrl = await browser.getCurrentUrl();
|
||||
|
||||
// Navigate to dashboard app
|
||||
await appsMenu.clickLink('Dashboard');
|
||||
await appsMenu.clickLink('Dashboard', { category: 'kibana' });
|
||||
|
||||
// Navigating back to discover
|
||||
await browser.goBack();
|
||||
|
|
|
@ -116,23 +116,55 @@ export class AppsMenuService extends FtrService {
|
|||
category,
|
||||
}: { closeCollapsibleNav?: boolean; category?: string } = {}
|
||||
) {
|
||||
try {
|
||||
this.log.debug(`click "${name}" app link`);
|
||||
await this.openCollapsibleNav();
|
||||
let nav;
|
||||
if (typeof category === 'string') {
|
||||
nav = await this.testSubjects.find(`collapsibleNavGroup-${category}`);
|
||||
} else {
|
||||
nav = await this.testSubjects.find('collapsibleNav');
|
||||
}
|
||||
const link = await nav.findByPartialLinkText(name);
|
||||
await link.click();
|
||||
await this.waitUntilLoadingHasFinished();
|
||||
|
||||
if (closeCollapsibleNav) {
|
||||
await this.closeCollapsibleNav();
|
||||
await this.ctx.getService('retry').try(async () => {
|
||||
this.log.debug(`click "${name}" app link`);
|
||||
|
||||
try {
|
||||
await this.openCollapsibleNav();
|
||||
let nav;
|
||||
if (typeof category === 'string') {
|
||||
// we can search within a specific section of the side nav
|
||||
nav = await this.testSubjects.find(`collapsibleNavGroup-${category}`);
|
||||
const link = await nav.findByPartialLinkText(name);
|
||||
await link.click();
|
||||
} else {
|
||||
// we need to search our app link in the whole side nav
|
||||
// first, we get all the links, along with their inner text
|
||||
const allLinks = await this.testSubjects.findAll('collapsibleNavAppLink');
|
||||
const allLinksTexts = await Promise.all(
|
||||
allLinks.map((link) => link.getVisibleText().then((text) => ({ link, text })))
|
||||
);
|
||||
|
||||
// then, filter out those that don't have a matching text
|
||||
const matchingLinks = allLinksTexts.filter(({ text }) => text.includes(name));
|
||||
if (matchingLinks.length === 0) {
|
||||
this.log.debug(
|
||||
`Found ${allLinks.length} links on the side nav: ${allLinksTexts.map(
|
||||
({ text }) => text
|
||||
)}`
|
||||
);
|
||||
throw new Error(
|
||||
`Could not find the '${name}' application on the side nav (${allLinks.length} links found)`
|
||||
);
|
||||
} else if (matchingLinks.length > 1) {
|
||||
throw new Error(
|
||||
`Multiple apps exist in the side nav with the specified name: '${name}'. Consider using the "category" parameter to disambiguate`
|
||||
);
|
||||
}
|
||||
|
||||
this.log.debug(`Found "${name}" app link on the side nav!`);
|
||||
// if we click on a stale element (e.g. re-rendered) it'll throw an Error and we will retry
|
||||
await matchingLinks.pop()!.link.click();
|
||||
}
|
||||
|
||||
if (closeCollapsibleNav) {
|
||||
await this.closeCollapsibleNav();
|
||||
}
|
||||
} finally {
|
||||
// Intentionally empty
|
||||
}
|
||||
} finally {
|
||||
// Intentionally empty
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { PluginFunctionalProviderContext } from '../../services';
|
||||
import type { PluginFunctionalProviderContext } from '../../services';
|
||||
|
||||
export default function ({ getService, getPageObject }: PluginFunctionalProviderContext) {
|
||||
const common = getPageObject('common');
|
||||
|
@ -17,10 +17,24 @@ export default function ({ getService, getPageObject }: PluginFunctionalProvider
|
|||
const find = getService('find');
|
||||
const deployment = getService('deployment');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const log = getService('log');
|
||||
|
||||
const clickAppLink = async (app: string) => {
|
||||
const appLink = `fooNav${app}`;
|
||||
if (!(await testSubjects.exists(appLink))) {
|
||||
log.debug(`App ${app} not found on side nav`);
|
||||
}
|
||||
await testSubjects.click(appLink);
|
||||
};
|
||||
|
||||
const loadingScreenNotShown = async () =>
|
||||
expect(await testSubjects.exists('kbnLoadingMessage')).to.be(false);
|
||||
|
||||
const checkAppVisible = async (app: string) => {
|
||||
const appContainer = `fooApp${app}`;
|
||||
await testSubjects.existOrFail(appContainer);
|
||||
};
|
||||
|
||||
const getAppWrapperHeight = async () => {
|
||||
const wrapper = await find.byClassName('kbnAppWrapper');
|
||||
return (await wrapper.getSize()).height;
|
||||
|
@ -29,8 +43,7 @@ export default function ({ getService, getPageObject }: PluginFunctionalProvider
|
|||
const navigateTo = async (path: string) =>
|
||||
await browser.navigateTo(`${deployment.getHostPort()}${path}`);
|
||||
|
||||
// FLAKY: https://github.com/elastic/kibana/issues/53356
|
||||
describe.skip('ui applications', function describeIndexTests() {
|
||||
describe('ui applications', function describeIndexTests() {
|
||||
before(async () => {
|
||||
await esArchiver.emptyKibanaIndex();
|
||||
await common.navigateToApp('foo');
|
||||
|
@ -38,48 +51,48 @@ export default function ({ getService, getPageObject }: PluginFunctionalProvider
|
|||
});
|
||||
|
||||
it('starts on home page', async () => {
|
||||
await testSubjects.existOrFail('fooAppHome');
|
||||
await checkAppVisible('Home');
|
||||
});
|
||||
|
||||
it('redirects and renders correctly regardless of trailing slash', async () => {
|
||||
await navigateTo(`/app/foo`);
|
||||
await browser.waitForUrlToBe('/app/foo/home');
|
||||
await testSubjects.existOrFail('fooAppHome');
|
||||
await checkAppVisible('Home');
|
||||
await navigateTo(`/app/foo/`);
|
||||
await browser.waitForUrlToBe('/app/foo/home');
|
||||
await testSubjects.existOrFail('fooAppHome');
|
||||
await checkAppVisible('Home');
|
||||
});
|
||||
|
||||
it('navigates to its own pages', async () => {
|
||||
// Go to page A
|
||||
await testSubjects.click('fooNavPageA');
|
||||
await clickAppLink('PageA');
|
||||
await browser.waitForUrlToBe('/app/foo/page-a');
|
||||
await loadingScreenNotShown();
|
||||
await testSubjects.existOrFail('fooAppPageA');
|
||||
await checkAppVisible('PageA');
|
||||
|
||||
// Go to home page
|
||||
await testSubjects.click('fooNavHome');
|
||||
await clickAppLink('Home');
|
||||
await browser.waitForUrlToBe('/app/foo/home');
|
||||
await loadingScreenNotShown();
|
||||
await testSubjects.existOrFail('fooAppHome');
|
||||
await checkAppVisible('Home');
|
||||
});
|
||||
|
||||
it('can use the back button to navigate within an app', async () => {
|
||||
await browser.goBack();
|
||||
await browser.waitForUrlToBe('/app/foo/page-a');
|
||||
await loadingScreenNotShown();
|
||||
await testSubjects.existOrFail('fooAppPageA');
|
||||
await checkAppVisible('PageA');
|
||||
});
|
||||
|
||||
it('navigates to app root when navlink is clicked', async () => {
|
||||
await appsMenu.clickLink('Foo', { category: 'kibana' });
|
||||
await appsMenu.clickLink('Foo');
|
||||
await browser.waitForUrlToBe('/app/foo/home');
|
||||
await loadingScreenNotShown();
|
||||
await testSubjects.existOrFail('fooAppHome');
|
||||
await checkAppVisible('Home');
|
||||
});
|
||||
|
||||
it('navigates to other apps', async () => {
|
||||
await testSubjects.click('fooNavBarPageB');
|
||||
await clickAppLink('BarPageB');
|
||||
await loadingScreenNotShown();
|
||||
await testSubjects.existOrFail('barAppPageB');
|
||||
await browser.waitForUrlToBe('/app/bar/page-b?query=here');
|
||||
|
@ -94,7 +107,7 @@ export default function ({ getService, getPageObject }: PluginFunctionalProvider
|
|||
await browser.goBack();
|
||||
await browser.waitForUrlToBe('/app/foo/home');
|
||||
await loadingScreenNotShown();
|
||||
await testSubjects.existOrFail('fooAppHome');
|
||||
await checkAppVisible('Home');
|
||||
});
|
||||
|
||||
it('chromeless applications are not visible in apps list', async () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue