unskips application leave confirm & application deep links tests (#168741)

fix https://github.com/elastic/kibana/issues/166838
fix https://github.com/elastic/kibana/issues/166893
fix https://github.com/elastic/kibana/issues/75963

I modified the deep links tests because the side nav was overlaying the
in-app nav.
While, theoretically, the side nav should work for the tests, it tends
to be flaky.

I added logs for the url so that if these tests do fail, we'll have a
bit more data to go on for debugging.
These tests pass on local test runs.

latest flaky test runs (50):
https://buildkite.com/elastic/kibana-flaky-test-suite-runner/builds/3604


- [X] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Christiane (Tina) Heiligers 2023-10-19 06:03:24 -07:00 committed by GitHub
parent 60a41ae19b
commit e811b624ff
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 122 additions and 32 deletions

View file

@ -411,9 +411,18 @@ export class CommonPageObject extends FtrService {
* Clicks cancel button on modal
* @param overlayWillStay pass in true if your test will show multiple modals in succession
*/
async clickCancelOnModal(overlayWillStay = true) {
async clickCancelOnModal(overlayWillStay = true, ignorePageLeaveWarning = false) {
this.log.debug('Clicking modal cancel');
await this.testSubjects.click('confirmModalCancelButton');
await this.testSubjects.exists('confirmModalTitleText');
await this.retry.try(async () => {
const warning = await this.testSubjects.exists('confirmModalTitleText');
if (warning) {
await this.testSubjects.click(
ignorePageLeaveWarning ? 'confirmModalConfirmButton' : 'confirmModalCancelButton'
);
}
});
if (!overlayWillStay) {
await this.ensureModalOverlayHidden();
}

View file

@ -59,6 +59,7 @@ export class AppsMenuService extends FtrService {
if (!(await this.testSubjects.exists('collapsibleNav'))) {
await this.testSubjects.click('toggleNavButton');
}
await this.testSubjects.exists('collapsibleNav');
}
/**

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { Plugin, CoreSetup } from '@kbn/core/public';
import { Plugin, CoreSetup, DEFAULT_APP_CATEGORIES } from '@kbn/core/public';
export class CoreAppLeavePlugin
implements Plugin<CoreAppLeavePluginSetup, CoreAppLeavePluginStart>
@ -15,6 +15,8 @@ export class CoreAppLeavePlugin
core.application.register({
id: 'appleave1',
title: 'AppLeave 1',
appRoute: '/app/appleave1',
category: DEFAULT_APP_CATEGORIES.kibana,
async mount(params) {
const { renderApp } = await import('./application');
params.onAppLeave((actions) => actions.confirm('confirm-message', 'confirm-title'));
@ -24,9 +26,11 @@ export class CoreAppLeavePlugin
core.application.register({
id: 'appleave2',
title: 'AppLeave 2',
appRoute: '/app/appleave2',
category: DEFAULT_APP_CATEGORIES.kibana,
async mount(params) {
const { renderApp } = await import('./application');
params.onAppLeave((actions) => actions.default());
params.onAppLeave((actions) => actions.confirm('confirm-message', 'confirm-title'));
return renderApp('AppLeave 2', params);
},
});

View file

@ -50,7 +50,7 @@ const PageA = () => (
<EuiPageHeader>
<EuiPageHeaderSection>
<EuiTitle size="l">
<h1>DL Page A</h1>
<h1>DL page A</h1>
</EuiTitle>
</EuiPageHeaderSection>
</EuiPageHeader>
@ -70,7 +70,7 @@ const PageB = () => (
<EuiPageHeader>
<EuiPageHeaderSection>
<EuiTitle size="l">
<h1>DL Page B</h1>
<h1>DL page B</h1>
</EuiTitle>
</EuiPageHeaderSection>
</EuiPageHeader>

View file

@ -29,7 +29,7 @@ export class CorePluginDeepLinksPlugin
},
{
id: 'pageA',
title: 'DL Page A',
title: 'DL page A',
path: '/page-a',
navLinkStatus: AppNavLinkStatus.visible,
},
@ -39,7 +39,7 @@ export class CorePluginDeepLinksPlugin
deepLinks: [
{
id: 'pageB',
title: 'DL Page B',
title: 'DL page B',
path: '/page-b',
navLinkStatus: AppNavLinkStatus.visible,
},
@ -47,7 +47,7 @@ export class CorePluginDeepLinksPlugin
},
{
id: 'pageC',
title: 'DL Page C',
title: 'DL page C',
path: '/page-c',
// navLinkStatus hidden by default
},

View file

@ -18,6 +18,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
const testSubjects = getService('testSubjects');
const retry = getService('retry');
const esArchiver = getService('esArchiver');
const log = getService('log');
const loadingScreenNotShown = async () =>
expect(await testSubjects.exists('kbnLoadingMessage')).to.be(false);
@ -35,12 +36,20 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
const waitForUrlToBe = (pathname?: string, search?: string) => {
const expectedUrl = getKibanaUrl(pathname, search);
return retry.waitFor(`Url to be ${expectedUrl}`, async () => {
return (await browser.getCurrentUrl()) === expectedUrl;
const currentUrl = await browser.getCurrentUrl();
log?.debug(`waiting for currentUrl ${currentUrl} to be expectedUrl ${expectedUrl}`);
return currentUrl === expectedUrl;
});
};
// Failing: See https://github.com/elastic/kibana/issues/166893
describe.skip('application deep links navigation', function describeDeepLinksTests() {
const navigateToAppLinks = async (subject: string) => {
if (!(await testSubjects.exists(subject))) {
log.debug(`side nav in app not in DOM`);
}
await testSubjects.click(subject);
};
describe('application deep links navigation', function describeDeepLinksTests() {
before(async () => {
await esArchiver.emptyKibanaIndex();
await PageObjects.common.navigateToApp('dl');
@ -51,28 +60,29 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
});
it('should navigate to page A when navlink is clicked', async () => {
await appsMenu.clickLink('DL Page A');
await navigateToAppLinks('dlNavPageA');
await waitForUrlToBe('/app/dl/page-a');
await loadingScreenNotShown();
await testSubjects.existOrFail('dlAppPageA');
await testSubjects.existOrFail('dlNavPageA');
});
it('should be able to use the back button to navigate back to previous deep link', async () => {
await browser.goBack();
await waitForUrlToBe('/app/dl/home');
await loadingScreenNotShown();
await testSubjects.existOrFail('dlAppHome');
await testSubjects.existOrFail('dlNavHome');
});
it('should navigate to nested page B when navlink is clicked', async () => {
await appsMenu.clickLink('DL Page B');
await navigateToAppLinks('dlNavDeepPageB');
await waitForUrlToBe('/app/dl/page-b');
await loadingScreenNotShown();
await testSubjects.existOrFail('dlAppPageB');
await testSubjects.existOrFail('dlNavDeepPageB');
});
it('should navigate to Home when navlink is clicked inside the defined category group', async () => {
await appsMenu.clickLink('DL Home', { category: 'securitySolution' });
await navigateToAppLinks('dlAppHome');
await waitForUrlToBe('/app/dl/home');
await loadingScreenNotShown();
await testSubjects.existOrFail('dlAppHome');
@ -82,14 +92,14 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
await testSubjects.click('dlNavDeepPageB');
await waitForUrlToBe('/app/dl/page-b');
await loadingScreenNotShown();
await testSubjects.existOrFail('dlAppPageB');
await testSubjects.existOrFail('dlNavDeepPageB');
});
it('should navigate to nested page A using navigateToApp deepLinkId', async () => {
await testSubjects.click('dlNavDeepPageAById');
await waitForUrlToBe('/app/dl/page-a');
await loadingScreenNotShown();
await testSubjects.existOrFail('dlAppPageA');
await testSubjects.existOrFail('dlNavPageA');
});
it('should not display hidden deep links', async () => {

View file

@ -6,8 +6,8 @@
* Side Public License, v 1.
*/
import url from 'url';
import expect from '@kbn/expect';
import url from 'url';
import { PluginFunctionalProviderContext } from '../../services';
const getKibanaUrl = (pathname?: string, search?: string) =>
@ -20,30 +20,96 @@ const getKibanaUrl = (pathname?: string, search?: string) =>
});
export default function ({ getService, getPageObjects }: PluginFunctionalProviderContext) {
const PageObjects = getPageObjects(['common']);
const PageObjects = getPageObjects(['common', 'header']);
const browser = getService('browser');
const appsMenu = getService('appsMenu');
const log = getService('log');
const retry = getService('retry');
const testSubjects = getService('testSubjects');
const config = getService('config');
// Failing: See https://github.com/elastic/kibana/issues/75963
// Failing: See https://github.com/elastic/kibana/issues/166838
describe.skip('application using leave confirmation', () => {
const waitForUrlToBe = async (pathname?: string, search?: string) => {
const expectedUrl = getKibanaUrl(pathname, search);
return await retry.waitFor(`Url to be ${expectedUrl}`, async () => {
const currentUrl = await browser.getCurrentUrl();
log.debug(`waiting for currentUrl ${currentUrl} to be expectedUrl ${expectedUrl}`);
return currentUrl === expectedUrl;
});
};
const ensureModalOpen = async (
defaultTryTimeout: number,
attempts: number,
timeMultiplier: number,
action: 'cancel' | 'confirm',
linkText: string = 'home'
): Promise<void> => {
let isConfirmCancelModalOpenState = false;
await retry.tryForTime(defaultTryTimeout * timeMultiplier, async () => {
await appsMenu.clickLink(linkText);
isConfirmCancelModalOpenState = await testSubjects.exists('confirmModalTitleText', {
allowHidden: true,
timeout: defaultTryTimeout * timeMultiplier,
});
});
if (isConfirmCancelModalOpenState) {
log.debug(`defaultTryTimeout * ${timeMultiplier} is long enough`);
return action === 'cancel'
? await PageObjects.common.clickCancelOnModal(true, false)
: await PageObjects.common.clickConfirmOnModal();
} else {
log.debug(`defaultTryTimeout * ${timeMultiplier} is not long enough`);
return await ensureModalOpen(
defaultTryTimeout,
(attempts = attempts > 0 ? attempts - 1 : 0),
(timeMultiplier = timeMultiplier < 10 ? timeMultiplier + 1 : 10),
action,
linkText
);
}
};
describe('application using leave confirmation', () => {
const defaultTryTimeout = config.get('timeouts.try');
const attempts = 5;
describe('when navigating to another app', () => {
const timeMultiplier = 10;
beforeEach(async () => {
await PageObjects.common.navigateToApp('home');
});
it('prevents navigation if user click cancel on the confirmation dialog', async () => {
await PageObjects.common.navigateToApp('appleave1');
await appsMenu.clickLink('AppLeave 2');
await PageObjects.header.waitUntilLoadingHasFinished();
await waitForUrlToBe('/app/appleave1');
await testSubjects.existOrFail('appLeaveConfirmModal');
await PageObjects.common.clickCancelOnModal(false);
expect(await browser.getCurrentUrl()).to.eql(getKibanaUrl('/app/appleave1'));
await ensureModalOpen(defaultTryTimeout, attempts, timeMultiplier, 'cancel', 'AppLeave 2');
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.waitFor('navigate to appleave1', async () => {
const currentUrl = await browser.getCurrentUrl();
log.debug(`currentUrl ${currentUrl}`);
return currentUrl.includes('appleave1');
});
const currentUrl = await browser.getCurrentUrl();
expect(currentUrl).to.contain('appleave1');
await PageObjects.common.navigateToApp('home');
});
it('allows navigation if user click confirm on the confirmation dialog', async () => {
await PageObjects.common.navigateToApp('appleave1');
await appsMenu.clickLink('AppLeave 2');
await PageObjects.header.waitUntilLoadingHasFinished();
await waitForUrlToBe('/app/appleave1');
await testSubjects.existOrFail('appLeaveConfirmModal');
await PageObjects.common.clickConfirmOnModal();
expect(await browser.getCurrentUrl()).to.eql(getKibanaUrl('/app/appleave2'));
await ensureModalOpen(defaultTryTimeout, attempts, timeMultiplier, 'confirm', 'AppLeave 2');
await PageObjects.header.waitUntilLoadingHasFinished();
await retry.waitFor('navigate to appleave1', async () => {
const currentUrl = await browser.getCurrentUrl();
log.debug(`currentUrl ${currentUrl}`);
return currentUrl.includes('appleave2');
});
const currentUrl = await browser.getCurrentUrl();
expect(currentUrl).to.contain('appleave2');
await PageObjects.common.navigateToApp('home');
});
});
});