Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FTR] Implement browser network condition utils #163633

Merged
merged 2 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
expect(event.properties.value4).to.be.a('number');
expect(event.properties.value5).to.be.a('number');

if (browser.isChromium) {
if (browser.isChromium()) {
// Kibana Loaded memory
expect(meta).to.have.property('jsHeapSizeLimit');
expect(meta.jsHeapSizeLimit).to.be.a('number');
Expand Down
75 changes: 70 additions & 5 deletions test/functional/services/common/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@
*/

import { setTimeout as setTimeoutAsync } from 'timers/promises';
import { cloneDeepWith } from 'lodash';
import { cloneDeepWith, isString } from 'lodash';
import { Key, Origin, WebDriver } from 'selenium-webdriver';
import { Driver as ChromiumWebDriver } from 'selenium-webdriver/chrome';
import { modifyUrl } from '@kbn/std';

import sharp from 'sharp';
import { NoSuchSessionError } from 'selenium-webdriver/lib/error';
import { WebElementWrapper } from '../lib/web_element_wrapper';
import { FtrProviderContext, FtrService } from '../../ftr_provider_context';
import { Browsers } from '../remote/browsers';
import { NetworkOptions, NetworkProfile, NETWORK_PROFILES } from '../remote/network_profiles';

export type Browser = BrowserService;

Expand All @@ -25,19 +27,20 @@ class BrowserService extends FtrService {
*/
public readonly keys = Key;
public readonly isFirefox: boolean;
public readonly isChromium: boolean;

private readonly log = this.ctx.getService('log');

constructor(
ctx: FtrProviderContext,
public readonly browserType: string,
private readonly driver: WebDriver
protected readonly driver: WebDriver | ChromiumWebDriver
) {
super(ctx);
this.isFirefox = this.browserType === Browsers.Firefox;
this.isChromium =
this.browserType === Browsers.Chrome || this.browserType === Browsers.ChromiumEdge;
}

public isChromium(): this is { driver: ChromiumWebDriver } {
return this.driver instanceof ChromiumWebDriver;
}

/**
Expand Down Expand Up @@ -661,6 +664,68 @@ class BrowserService extends FtrService {
}
}
}

/**
* Get the network simulation for chromium browsers if available.
* https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/chrome_exports_Driver.html#getNetworkConditions
*
* @return {Promise<NetworkOptions>}
*/
public async getNetworkConditions() {
if (this.isChromium()) {
return this.driver.getNetworkConditions().catch(() => undefined); // Return undefined instead of throwing if no conditions are set.
} else {
const message =
'WebDriver does not support the .getNetworkConditions method.\nProbably the browser in used is not chromium based.';
this.log.error(message);
throw new Error(message);
}
}

/**
* Delete the network simulation for chromium browsers if available.
*
* @return {Promise<void>}
*/
public async restoreNetworkConditions() {
this.log.debug('Restore network conditions simulation.');
return this.setNetworkConditions('NO_THROTTLING');
}

/**
* Set the network conditions for chromium browsers if available.
*
* __Sample Usage:__
*
* browser.setNetworkConditions('FAST_3G')
* browser.setNetworkConditions('SLOW_3G')
* browser.setNetworkConditions('OFFLINE')
* browser.setNetworkConditions({
* offline: false,
* latency: 5, // Additional latency (ms).
* download_throughput: 500 * 1024, // Maximal aggregated download throughput.
* upload_throughput: 500 * 1024, // Maximal aggregated upload throughput.
* });
*
* https://www.selenium.dev/selenium/docs/api/javascript/module/selenium-webdriver/chrome_exports_Driver.html#setNetworkConditions
*
* @return {Promise<void>}
*/
public async setNetworkConditions(profileOrOptions: NetworkProfile | NetworkOptions) {
const networkOptions = isString(profileOrOptions)
? NETWORK_PROFILES[profileOrOptions]
: profileOrOptions;

if (this.isChromium()) {
this.log.debug(`Set network conditions with profile "${profileOrOptions}".`);
return this.driver.setNetworkConditions(networkOptions);
} else {
const message =
'WebDriver does not support the .setNetworkCondition method.\nProbably the browser in used is not chromium based.';
this.log.error(message);
throw new Error(message);
}
}
}

export async function BrowserProvider(ctx: FtrProviderContext) {
Expand Down
45 changes: 39 additions & 6 deletions test/functional/services/remote/network_profiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,50 @@
* Side Public License, v 1.
*/

interface NetworkOptions {
DOWNLOAD: number;
UPLOAD: number;
LATENCY: number;
export type NetworkProfile = 'NO_THROTTLING' | 'FAST_3G' | 'SLOW_3G' | 'OFFLINE' | 'CLOUD_USER';

export interface NetworkOptions {
offline: boolean;
latency: number;
download_throughput: number;
upload_throughput: number;
}

const sec = 10 ** 3;
const MBps = 10 ** 6 / 8; // megabyte per second (MB/s) (can be abbreviated as MBps)

// Selenium uses B/s (bytes) for network throttling
// Download (B/s) Upload (B/s) Latency (ms)
export const NETWORK_PROFILES: { [key: string]: NetworkOptions } = {
CLOUD_USER: { DOWNLOAD: 6 * MBps, UPLOAD: 6 * MBps, LATENCY: 0.1 * sec },

export const NETWORK_PROFILES: Record<NetworkProfile, NetworkOptions> = {
NO_THROTTLING: {
offline: false,
latency: 0,
download_throughput: -1,
upload_throughput: -1,
},
FAST_3G: {
offline: false,
latency: 0.56 * sec,
download_throughput: 1.44 * MBps,
upload_throughput: 0.7 * MBps,
},
SLOW_3G: {
offline: false,
latency: 2 * sec,
download_throughput: 0.4 * MBps,
upload_throughput: 0.4 * MBps,
},
OFFLINE: {
offline: true,
latency: 0,
download_throughput: 0,
upload_throughput: 0,
},
CLOUD_USER: {
offline: false,
latency: 0.1 * sec,
download_throughput: 6 * MBps,
upload_throughput: 6 * MBps,
},
};
24 changes: 7 additions & 17 deletions test/functional/services/remote/webdriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { createStdoutSocket } from './create_stdout_stream';
import { preventParallelCalls } from './prevent_parallel_calls';

import { Browsers } from './browsers';
import { NETWORK_PROFILES } from './network_profiles';
import { NetworkProfile, NETWORK_PROFILES } from './network_profiles';

const throttleOption: string = process.env.TEST_THROTTLE_NETWORK as string;
const headlessBrowser: string = process.env.TEST_BROWSER_HEADLESS as string;
Expand Down Expand Up @@ -300,22 +300,17 @@ async function attemptToCreateCommand(
const { session, consoleLog$ } = await buildDriverInstance();

if (throttleOption === '1' && browserType === 'chrome') {
const { KBN_NETWORK_TEST_PROFILE = 'CLOUD_USER' } = process.env;
const KBN_NETWORK_TEST_PROFILE = (process.env.KBN_NETWORK_TEST_PROFILE ??
'CLOUD_USER') as NetworkProfile;

const profile =
KBN_NETWORK_TEST_PROFILE in Object.keys(NETWORK_PROFILES)
? KBN_NETWORK_TEST_PROFILE
: 'CLOUD_USER';
KBN_NETWORK_TEST_PROFILE in NETWORK_PROFILES ? KBN_NETWORK_TEST_PROFILE : 'CLOUD_USER';

const {
DOWNLOAD: downloadThroughput,
UPLOAD: uploadThroughput,
LATENCY: latency,
} = NETWORK_PROFILES[`${profile}`];
const networkProfileOptions = NETWORK_PROFILES[profile];

// Only chrome supports this option.
log.debug(
`NETWORK THROTTLED with profile ${profile}: ${downloadThroughput} B/s down, ${uploadThroughput} B/s up, ${latency} ms latency.`
`NETWORK THROTTLED with profile ${profile}: ${networkProfileOptions.download_throughput} B/s down, ${networkProfileOptions.upload_throughput} B/s up, ${networkProfileOptions.latency} ms latency.`
);

if (noCache) {
Expand All @@ -326,12 +321,7 @@ async function attemptToCreateCommand(
}

// @ts-expect-error
session.setNetworkConditions({
offline: false,
latency,
download_throughput: downloadThroughput,
upload_throughput: uploadThroughput,
});
session.setNetworkConditions(networkProfileOptions);
}

if (attemptId !== attemptCounter) {
Expand Down