[page_objects/common_page] convert to ts (#50771)

* [page_objects/common_page] convert to ts

* fix lint errors

* descrease navigation timeouts

* use template literal for log messages
This commit is contained in:
Dmitry Lemeshko 2019-11-18 20:08:22 +01:00 committed by GitHub
parent 04425ff6cd
commit 6bc43bb9fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 181 additions and 196 deletions

View file

@ -58,7 +58,7 @@ export class Config {
this[$values] = value;
}
public has(key: string) {
public has(key: string | string[]) {
function recursiveHasCheck(
remainingPath: string[],
values: Record<string, any>,
@ -109,7 +109,7 @@ export class Config {
return recursiveHasCheck(path, this[$values], schema);
}
public get(key: string, defaultValue?: any) {
public get(key: string | string[], defaultValue?: any) {
if (!this.has(key)) {
throw new Error(`Unknown config key "${key}"`);
}

View file

@ -19,10 +19,13 @@
import { delay } from 'bluebird';
import expect from '@kbn/expect';
// @ts-ignore
import fetch from 'node-fetch';
import { FtrProviderContext } from '../ftr_provider_context';
// @ts-ignore not TS yet
import getUrl from '../../../src/test_utils/get_url';
export function CommonPageProvider({ getService, getPageObjects }) {
export function CommonPageProvider({ getService, getPageObjects }: FtrProviderContext) {
const log = getService('log');
const config = getService('config');
const browser = getService('browser');
@ -30,19 +33,22 @@ export function CommonPageProvider({ getService, getPageObjects }) {
const find = getService('find');
const globalNav = getService('globalNav');
const testSubjects = getService('testSubjects');
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['shield']);
const defaultTryTimeout = config.get('timeouts.try');
const defaultFindTimeout = config.get('timeouts.find');
class CommonPage {
static async navigateToUrlAndHandleAlert(url, shouldAcceptAlert) {
/**
* Navigates the browser window to provided URL
* @param url URL
* @param shouldAcceptAlert pass 'true' if browser alert should be accepted
*/
private static async navigateToUrlAndHandleAlert(url: string, shouldAcceptAlert: boolean) {
log.debug('Navigate to: ' + url);
try {
await browser.get(url);
} catch(navigationError) {
} catch (navigationError) {
log.debug('Error navigating to url');
const alert = await browser.getAlert();
if (alert && alert.accept) {
@ -50,7 +56,7 @@ export function CommonPageProvider({ getService, getPageObjects }) {
log.debug('Should accept alert');
try {
await alert.accept();
} catch(alertException) {
} catch (alertException) {
log.debug('Error accepting alert');
throw alertException;
}
@ -64,85 +70,43 @@ export function CommonPageProvider({ getService, getPageObjects }) {
}
}
getHostPort() {
/**
* Returns Kibana host URL
*/
public getHostPort() {
return getUrl.baseUrl(config.get('servers.kibana'));
}
getEsHostPort() {
/**
* Returns ES host URL
*/
public getEsHostPort() {
return getUrl.baseUrl(config.get('servers.elasticsearch'));
}
/**
* @param {string} appName As defined in the apps config
* @param {string} subUrl The route after the hash (#)
* Logins to Kibana as default user and navigates to provided app
* @param appUrl Kibana URL
*/
async navigateToUrl(appName, subUrl, {
basePath = '',
ensureCurrentUrl = true,
shouldLoginIfPrompted = true,
shouldAcceptAlert = true
} = {}) {
// we onlt use the pathname from the appConfig and use the subUrl as the hash
const appConfig = {
pathname: `${basePath}${config.get(['apps', appName]).pathname}`,
hash: `/${appName}/${subUrl}`,
};
const appUrl = getUrl.noAuth(config.get('servers.kibana'), appConfig);
await retry.try(async () => {
await CommonPage.navigateToUrlAndHandleAlert(appUrl, shouldAcceptAlert);
const currentUrl = shouldLoginIfPrompted ? await this.loginIfPrompted(appUrl) : await browser.getCurrentUrl();
if (ensureCurrentUrl && !currentUrl.includes(appUrl)) {
throw new Error(`expected ${currentUrl}.includes(${appUrl})`);
}
});
}
/**
* @param {string} appName As defined in the apps config
* @param {string} hash The route after the hash (#)
*/
async navigateToActualUrl(appName, hash, {
basePath = '',
ensureCurrentUrl = true,
shouldLoginIfPrompted = true
} = {}) {
// we only use the apps config to get the application path
const appConfig = {
pathname: `${basePath}${config.get(['apps', appName]).pathname}`,
hash,
};
const appUrl = getUrl.noAuth(config.get('servers.kibana'), appConfig);
await retry.try(async () => {
log.debug(`navigateToActualUrl ${appUrl}`);
await browser.get(appUrl);
const currentUrl = shouldLoginIfPrompted ? await this.loginIfPrompted(appUrl) : await browser.getCurrentUrl();
if (ensureCurrentUrl && !currentUrl.includes(appUrl)) {
throw new Error(`expected ${currentUrl}.includes(${appUrl})`);
}
});
}
async loginIfPrompted(appUrl) {
private async loginIfPrompted(appUrl: string) {
let currentUrl = await browser.getCurrentUrl();
log.debug(`currentUrl = ${currentUrl}\n appUrl = ${appUrl}`);
await find.byCssSelector('[data-test-subj="kibanaChrome"]', defaultTryTimeout * 2);
await find.byCssSelector('[data-test-subj="kibanaChrome"]', 6 * defaultFindTimeout); // 60 sec waiting
const loginPage = currentUrl.includes('/login');
const wantedLoginPage = appUrl.includes('/login') || appUrl.includes('/logout');
if (loginPage && !wantedLoginPage) {
log.debug(`Found login page. Logging in with username = ${config.get('servers.kibana.username')}`);
log.debug(
`Found login page. Logging in with username = ${config.get('servers.kibana.username')}`
);
await PageObjects.shield.login(
config.get('servers.kibana.username'),
config.get('servers.kibana.password')
);
await find.byCssSelector('[data-test-subj="kibanaChrome"] nav:not(.ng-hide)', 20000);
await find.byCssSelector(
'[data-test-subj="kibanaChrome"] nav:not(.ng-hide)',
2 * defaultFindTimeout
);
await browser.get(appUrl);
currentUrl = await browser.getCurrentUrl();
log.debug(`Finished login process currentUrl = ${currentUrl}`);
@ -150,10 +114,84 @@ export function CommonPageProvider({ getService, getPageObjects }) {
return currentUrl;
}
navigateToApp(appName, { basePath = '', shouldLoginIfPrompted = true, shouldAcceptAlert = true, hash = '' } = {}) {
const self = this;
/**
* Navigates browser using the pathname from the appConfig and subUrl as the hash
* @param appName As defined in the apps config, e.g. 'home'
* @param subUrl The route after the hash (#), e.g. 'tutorial_directory/sampleData'
* @param args additional arguments
*/
public async navigateToUrl(
appName: string,
subUrl?: string,
{
basePath = '',
ensureCurrentUrl = true,
shouldLoginIfPrompted = true,
shouldAcceptAlert = true,
useActualUrl = false,
} = {}
) {
const appConfig = {
pathname: `${basePath}${config.get(['apps', appName]).pathname}`,
hash: useActualUrl ? subUrl : `/${appName}/${subUrl}`,
};
let appUrl;
const appUrl = getUrl.noAuth(config.get('servers.kibana'), appConfig);
await retry.try(async () => {
if (useActualUrl) {
log.debug(`navigateToActualUrl ${appUrl}`);
await browser.get(appUrl);
} else {
await CommonPage.navigateToUrlAndHandleAlert(appUrl, shouldAcceptAlert);
}
const currentUrl = shouldLoginIfPrompted
? await this.loginIfPrompted(appUrl)
: await browser.getCurrentUrl();
if (ensureCurrentUrl && !currentUrl.includes(appUrl)) {
throw new Error(`expected ${currentUrl}.includes(${appUrl})`);
}
});
}
/**
* Navigates browser using only the pathname from the appConfig
* @param appName As defined in the apps config, e.g. 'kibana'
* @param hash The route after the hash (#), e.g. 'management/kibana/settings'
* @param args additional arguments
*/
async navigateToActualUrl(
appName: string,
hash?: string,
{
basePath = '',
ensureCurrentUrl = true,
shouldLoginIfPrompted = true,
shouldAcceptAlert = true,
} = {}
) {
await this.navigateToUrl(appName, hash, {
basePath,
ensureCurrentUrl,
shouldLoginIfPrompted,
shouldAcceptAlert,
useActualUrl: true,
});
}
async sleep(sleepMilliseconds: number) {
log.debug(`... sleep(${sleepMilliseconds}) start`);
await delay(sleepMilliseconds);
log.debug(`... sleep(${sleepMilliseconds}) end`);
}
async navigateToApp(
appName: string,
{ basePath = '', shouldLoginIfPrompted = true, shouldAcceptAlert = true, hash = '' } = {}
) {
let appUrl: string;
if (config.has(['apps', appName])) {
// Legacy applications
const appConfig = config.get(['apps', appName]);
@ -164,116 +202,73 @@ export function CommonPageProvider({ getService, getPageObjects }) {
} else {
appUrl = getUrl.noAuth(config.get('servers.kibana'), {
pathname: `${basePath}/app/${appName}`,
hash
hash,
});
}
log.debug('navigating to ' + appName + ' url: ' + appUrl);
function navigateTo(url) {
return retry.try(function () {
await retry.tryForTime(defaultTryTimeout * 2, async () => {
let lastUrl = await retry.try(async () => {
// since we're using hash URLs, always reload first to force re-render
return kibanaServer.uiSettings.getDefaultIndex()
.then(async function () {
return await CommonPage.navigateToUrlAndHandleAlert(url, shouldAcceptAlert);
})
.then(function () {
return self.sleep(700);
})
.then(function () {
log.debug('returned from get, calling refresh');
return browser.refresh();
})
.then(async function () {
const currentUrl = shouldLoginIfPrompted ? await self.loginIfPrompted(appUrl) : browser.getCurrentUrl();
await CommonPage.navigateToUrlAndHandleAlert(appUrl, shouldAcceptAlert);
await this.sleep(700);
log.debug('returned from get, calling refresh');
await browser.refresh();
let currentUrl = shouldLoginIfPrompted
? await this.loginIfPrompted(appUrl)
: await browser.getCurrentUrl();
if (currentUrl.includes('app/kibana')) {
await testSubjects.find('kibanaChrome');
}
})
.then(async function () {
if (currentUrl.includes('app/kibana')) {
await testSubjects.find('kibanaChrome');
}
const currentUrl = (await browser.getCurrentUrl()).replace(/\/\/\w+:\w+@/, '//');
const maxAdditionalLengthOnNavUrl = 230;
// On several test failures at the end of the TileMap test we try to navigate back to
// Visualize so we can create the next Vertical Bar Chart, but we can see from the
// logging and the screenshot that it's still on the TileMap page. Why didn't the "get"
// with a new timestamped URL go? I thought that sleep(700) between the get and the
// refresh would solve the problem but didn't seem to always work.
// So this hack fails the navSuccessful check if the currentUrl doesn't match the
// appUrl plus up to 230 other chars.
// Navigating to Settings when there is a default index pattern has a URL length of 196
// (from debug output). Some other tabs may also be long. But a rather simple configured
// visualization is about 1000 chars long. So at least we catch that case.
currentUrl = (await browser.getCurrentUrl()).replace(/\/\/\w+:\w+@/, '//');
const maxAdditionalLengthOnNavUrl = 230;
// Browsers don't show the ':port' if it's 80 or 443 so we have to
// remove that part so we can get a match in the tests.
const navSuccessful = new RegExp(appUrl.replace(':80/', '/').replace(':443/', '/')
+ '.{0,' + maxAdditionalLengthOnNavUrl + '}$')
.test(currentUrl);
// On several test failures at the end of the TileMap test we try to navigate back to
// Visualize so we can create the next Vertical Bar Chart, but we can see from the
// logging and the screenshot that it's still on the TileMap page. Why didn't the "get"
// with a new timestamped URL go? I thought that sleep(700) between the get and the
// refresh would solve the problem but didn't seem to always work.
// So this hack fails the navSuccessful check if the currentUrl doesn't match the
// appUrl plus up to 230 other chars.
// Navigating to Settings when there is a default index pattern has a URL length of 196
// (from debug output). Some other tabs may also be long. But a rather simple configured
// visualization is about 1000 chars long. So at least we catch that case.
if (!navSuccessful) {
const msg = 'App failed to load: ' + appName +
' in ' + defaultFindTimeout + 'ms' +
' appUrl = ' + appUrl +
' currentUrl = ' + currentUrl;
log.debug(msg);
throw new Error(msg);
}
// Browsers don't show the ':port' if it's 80 or 443 so we have to
// remove that part so we can get a match in the tests.
const navSuccessful = new RegExp(
appUrl.replace(':80/', '/').replace(':443/', '/') +
`.{0,${maxAdditionalLengthOnNavUrl}}$`
).test(currentUrl);
return currentUrl;
});
if (!navSuccessful) {
const msg = `App failed to load: ${appName} in ${defaultFindTimeout}ms appUrl=${appUrl} currentUrl=${currentUrl}`;
log.debug(msg);
throw new Error(msg);
}
return currentUrl;
});
}
return retry.tryForTime(defaultTryTimeout * 3, () => {
return navigateTo(appUrl)
.then(function (currentUrl) {
let lastUrl = currentUrl;
return retry.try(function () {
// give the app time to update the URL
return self.sleep(501)
.then(function () {
return browser.getCurrentUrl();
})
.then(function (currentUrl) {
log.debug('in navigateTo url = ' + currentUrl);
if (lastUrl !== currentUrl) {
lastUrl = currentUrl;
throw new Error('URL changed, waiting for it to settle');
}
});
});
})
.then(async () => {
if (appName === 'status_page') return;
if (await testSubjects.exists('statusPageContainer')) {
throw new Error('Navigation ended up at the status page.');
}
});
await retry.try(async () => {
await this.sleep(501);
const currentUrl = await browser.getCurrentUrl();
log.debug('in navigateTo url = ' + currentUrl);
if (lastUrl !== currentUrl) {
lastUrl = currentUrl;
throw new Error('URL changed, waiting for it to settle');
}
});
if (appName === 'status_page') return;
if (await testSubjects.exists('statusPageContainer')) {
throw new Error('Navigation ended up at the status page.');
}
});
}
async sleep(sleepMilliseconds) {
log.debug('... sleep(' + sleepMilliseconds + ') start');
await delay(sleepMilliseconds);
log.debug('... sleep(' + sleepMilliseconds + ') end');
}
createErrorHandler(testObj) {
const testName = (testObj.parent) ? [testObj.parent.name, testObj.name].join('_') : testObj.name;
return error => {
const now = Date.now();
const fileName = `failure_${now}_${testName}`;
return this.saveScreenshot(fileName, true)
.then(function () {
throw error;
});
};
}
async waitUntilUrlIncludes(path) {
async waitUntilUrlIncludes(path: string) {
await retry.try(async () => {
const url = await browser.getCurrentUrl();
if (!url.includes(path)) {
@ -288,13 +283,10 @@ export function CommonPageProvider({ getService, getPageObjects }) {
return {
title: await element.getAttribute('data-title'),
description: await element.getAttribute('data-description')
description: await element.getAttribute('data-description'),
};
}
/**
* Makes sure the modal overlay is not showing, tries a few times in case it is in the process of hiding.
*/
async ensureModalOverlayHidden() {
return retry.try(async () => {
const shown = await testSubjects.exists('confirmModalTitleText');
@ -316,9 +308,11 @@ export function CommonPageProvider({ getService, getPageObjects }) {
await browser.pressKeys(browser.keys.ENTER);
}
// pass in true if your test will show multiple modals
// in succession
async clickCancelOnModal(overlayWillStay = false) {
/**
* Clicks cancel button on modal
* @param overlayWillStay pass in true if your test will show multiple modals in succession
*/
async clickCancelOnModal(overlayWillStay = true) {
log.debug('Clicking modal cancel');
await testSubjects.click('confirmModalCancelButton');
if (!overlayWillStay) {
@ -326,19 +320,17 @@ export function CommonPageProvider({ getService, getPageObjects }) {
}
}
async expectConfirmModalOpenState(state) {
if (typeof state !== 'boolean') {
throw new Error('pass true or false to expectConfirmModalOpenState()');
}
async expectConfirmModalOpenState(state: boolean) {
log.debug(`expectConfirmModalOpenState(${state})`);
// we use retry here instead of a simple .exists() check because the modal
// fades in/out, which takes time, and we really only care that at some point
// the modal is either open or closed
await retry.try(async () => {
const actualState = await testSubjects.exists('confirmModalCancelButton');
expect(actualState).to.be(state);
expect(actualState).to.equal(
state,
state ? 'Confirm modal should be present' : 'Confirm modal should be hidden'
);
});
}
@ -364,16 +356,10 @@ export function CommonPageProvider({ getService, getPageObjects }) {
}
async closeToast() {
let toast;
await retry.try(async () => {
toast = await find.byCssSelector('.euiToast');
if (!toast) {
throw new Error('Toast is not visible yet');
}
});
const toast = await find.byCssSelector('.euiToast', 2 * defaultFindTimeout);
await toast.moveMouseTo();
const title = await (await find.byCssSelector('.euiToastHeader__title')).getVisibleText();
log.debug(title);
log.debug(`Toast title: ${title}`);
await find.clickByCssSelector('.euiToast__closeButton');
return title;
}
@ -392,7 +378,7 @@ export function CommonPageProvider({ getService, getPageObjects }) {
}
async getBodyText() {
if (await find.existsByCssSelector('a[id=rawdata-tab]', 10000)) {
if (await find.existsByCssSelector('a[id=rawdata-tab]', defaultFindTimeout)) {
// Firefox has 3 tabs and requires navigation to see Raw output
await find.clickByCssSelector('a[id=rawdata-tab]');
}
@ -418,7 +404,7 @@ export function CommonPageProvider({ getService, getPageObjects }) {
method: 'get',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Basic ' + Buffer.from(username + ':' + password).toString('base64')
Authorization: 'Basic ' + Buffer.from(username + ':' + password).toString('base64'),
},
});
return response.status !== 200;

View file

@ -17,7 +17,6 @@
* under the License.
*/
// @ts-ignore not TS yet
import { CommonPageProvider } from './common_page';
// @ts-ignore not TS yet
import { ConsolePageProvider } from './console_page';

View file

@ -86,7 +86,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
it(`allows a workpad to be created`, async () => {
await PageObjects.common.navigateToActualUrl('canvas', 'workpad/create', {
ensureCurrentUrl: true,
showLoginIfPrompted: false,
shouldLoginIfPrompted: false,
});
await PageObjects.canvas.expectAddElementButton();
@ -98,7 +98,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
'workpad/workpad-1705f884-6224-47de-ba49-ca224fe6ec31',
{
ensureCurrentUrl: true,
showLoginIfPrompted: false,
shouldLoginIfPrompted: false,
}
);

View file

@ -115,7 +115,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
'kibana',
'/management/kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed',
{
loginIfPrompted: false,
shouldLoginIfPrompted: false,
}
);
});
@ -233,7 +233,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
'kibana',
'/management/kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed',
{
loginIfPrompted: false,
shouldLoginIfPrompted: false,
}
);
await testSubjects.existOrFail('savedObjectsEdit');
@ -316,7 +316,7 @@ export default function({ getPageObjects, getService }: FtrProviderContext) {
'kibana',
'/management/kibana/objects/savedVisualizations/75c3e060-1e7c-11e9-8488-65449e65d0ed',
{
loginIfPrompted: false,
shouldLoginIfPrompted: false,
ensureCurrentUrl: false,
}
);