Skip to content

Commit

Permalink
Adding support for legacy protocol (#1323)
Browse files Browse the repository at this point in the history
  • Loading branch information
Amit3200 authored and rishigupta1599 committed Aug 7, 2023
1 parent 395582f commit b758d24
Show file tree
Hide file tree
Showing 12 changed files with 303 additions and 56 deletions.
15 changes: 11 additions & 4 deletions packages/webdriver-utils/src/driver.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
import utils from '@percy/sdk-utils';
import Cache from './util/cache.js';
const { request } = utils;
const log = utils.logger('webdriver-utils:driver');

export default class Driver {
constructor(sessionId, executorUrl) {
constructor(sessionId, executorUrl, passedCapabilities) {
this.sessionId = sessionId;
this.executorUrl = executorUrl.includes('@') ? `https://${executorUrl.split('@')[1]}` : executorUrl;
this.passedCapabilities = passedCapabilities;
}

async getCapabilites() {
return await Cache.withCache(Cache.caps, this.sessionId, async () => {
const baseUrl = `${this.executorUrl}/session/${this.sessionId}`;
const caps = JSON.parse((await request(baseUrl)).body);
return caps.value;
try {
const baseUrl = `${this.executorUrl}/session/${this.sessionId}`;
const caps = JSON.parse((await request(baseUrl)).body);
return caps.value;
} catch (err) {
log.warn(`Falling back to legacy protocol, Error: ${err.message}`);
return this.passedCapabilities;
}
});
}

Expand Down
16 changes: 11 additions & 5 deletions packages/webdriver-utils/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,16 @@ export default class WebdriverUtils {
}

async automateScreenshot() {
this.log.info('Starting automate screenshot');
const automate = ProviderResolver.resolve(this.sessionId, this.commandExecutorUrl, this.capabilities, this.sessionCapabilites, this.clientInfo, this.environmentInfo, this.options);
await automate.createDriver();
this.log.debug('Created driver ...');
return await automate.screenshot(this.snapshotName, this.options);
try {
this.log.info(`[${this.snapshotName}] : Starting automate screenshot ...`);
const automate = ProviderResolver.resolve(this.sessionId, this.commandExecutorUrl, this.capabilities, this.sessionCapabilites, this.clientInfo, this.environmentInfo, this.options, this.buildInfo);
this.log.debug(`[${this.snapshotName}] : Resolved provider ...`);
await automate.createDriver();
this.log.debug(`[${this.snapshotName}] : Created driver ...`);
return await automate.screenshot(this.snapshotName, this.options);
} catch (e) {
this.log.error(`[${this.snapshotName}] : Error - ${e.message}`);
this.log.error(`[${this.snapshotName}] : Error Log - ${e.toString()}`);
}
}
}
32 changes: 24 additions & 8 deletions packages/webdriver-utils/src/metadata/desktopMetaData.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
import Cache from '../util/cache.js';

export default class DesktopMetaData {
constructor(driver, opts) {
this.driver = driver;
this.capabilities = opts;
}

device() {
return false;
}

browserName() {
return this.capabilities.browserName.toLowerCase();
return this.capabilities?.browserName?.toLowerCase();
}

browserVersion() {
return this.capabilities.browserVersion.split('.')[0];
return this.capabilities?.browserVersion?.split('.')[0];
}

osName() {
let osName = this.capabilities.os;
if (osName) return osName.toLowerCase();
let osName = this.capabilities?.os;
if (osName) return osName?.toLowerCase();

osName = this.capabilities.platform;
osName = this.capabilities?.platform;
return osName;
}

// showing major version
osVersion() {
return this.capabilities.osVersion.toLowerCase();
return this.capabilities?.osVersion?.toLowerCase();
}

// combination of browserName + browserVersion + osVersion + osName
Expand All @@ -41,8 +47,18 @@ export default class DesktopMetaData {
return { width, height };
}

async screenResolution() {
return await Cache.withCache(Cache.resolution, this.driver.sessionId, async () => {
const data = await this.driver.executeScript({ script: 'return [(window.screen.width * window.devicePixelRatio).toString(), (window.screen.height * window.devicePixelRatio).toString()];', args: [] });
const screenInfo = data.value;
return `${screenInfo[0]} x ${screenInfo[1]}`;
});
}

async devicePixelRatio() {
const devicePixelRatio = await this.driver.executeScript({ script: 'return window.devicePixelRatio;', args: [] });
return devicePixelRatio.value;
return await Cache.withCache(Cache.dpr, this.driver.sessionId, async () => {
const devicePixelRatio = await this.driver.executeScript({ script: 'return window.devicePixelRatio;', args: [] });
return devicePixelRatio.value;
});
}
}
4 changes: 3 additions & 1 deletion packages/webdriver-utils/src/metadata/metaDataResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ export default class MetaDataResolver {
if (!driver) throw new Error('Please pass a Driver object');

const platform = opts.platformName || opts.platform;
if (['ios', 'android'].includes(platform.toLowerCase())) {
if (['ios', 'android'].includes(platform.toLowerCase()) ||
['ios', 'android'].includes(capabilities?.platformName?.toLowerCase()) ||
['ipad', 'iphone'].includes(capabilities?.device?.toString()?.toLowerCase())) {
return new MobileMetaData(driver, capabilities);
} else {
return new DesktopMetaData(driver, capabilities);
Expand Down
30 changes: 23 additions & 7 deletions packages/webdriver-utils/src/metadata/mobileMetaData.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import Cache from '../util/cache.js';
// Todo: Implement a base metadata for the common functions.
export default class MobileMetaData {
constructor(driver, opts) {
this.driver = driver;
this.capabilities = opts;
}

device() {
return true;
}

browserName() {
return this.capabilities.browserName.toLowerCase();
return this.capabilities?.browserName?.toLowerCase();
}

browserVersion() {
Expand All @@ -17,23 +23,23 @@ export default class MobileMetaData {
}

osName() {
let osName = this.capabilities.os.toLowerCase();
let osName = this.capabilities?.os?.toLowerCase();
if (osName === 'mac' && this.browserName() === 'iphone') {
osName = 'ios';
}
return osName;
}

osVersion() {
return this.capabilities.osVersion.split('.')[0];
return this.capabilities?.osVersion?.split('.')[0];
}

deviceName() {
return this.capabilities.deviceName.split('-')[0];
return this.capabilities?.deviceName?.split('-')[0];
}

orientation() {
return this.capabilities.orientation;
return this.capabilities?.orientation;
}

async windowSize() {
Expand All @@ -43,8 +49,18 @@ export default class MobileMetaData {
return { width, height };
}

async screenResolution() {
return await Cache.withCache(Cache.resolution, this.driver.sessionId, async () => {
const data = await this.driver.executeScript({ script: 'return [parseInt(window.screen.width * window.devicePixelRatio).toString(), parseInt(window.screen.height * window.devicePixelRatio).toString()];', args: [] });
const screenInfo = data.value;
return `${screenInfo[0]} x ${screenInfo[1]}`;
});
}

async devicePixelRatio() {
const devicePixelRatio = await this.driver.executeScript({ script: 'return window.devicePixelRatio;', args: [] });
return devicePixelRatio.value;
return await Cache.withCache(Cache.dpr, this.driver.sessionId, async () => {
const devicePixelRatio = await this.driver.executeScript({ script: 'return window.devicePixelRatio;', args: [] });
return devicePixelRatio.value;
});
}
}
43 changes: 41 additions & 2 deletions packages/webdriver-utils/src/providers/automateProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import utils from '@percy/sdk-utils';
import GenericProvider from './genericProvider.js';
import Cache from '../util/cache.js';
import Tile from '../util/tile.js';
import NormalizeData from '../metadata/normalizeData.js';

const log = utils.logger('webdriver-utils:automateProvider');
const { TimeIt } = utils;
Expand All @@ -26,6 +27,7 @@ export default class AutomateProvider extends GenericProvider {
options
);
this._markedPercy = false;
this.automateResults = null;
}

static supports(commandExecutorUrl) {
Expand Down Expand Up @@ -154,8 +156,45 @@ export default class AutomateProvider extends GenericProvider {
if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
this.debugUrl = await Cache.withCache(Cache.bstackSessionDetails, this.driver.sessionId,
async () => {
const sessionDetails = await this.browserstackExecutor('getSessionDetails');
return JSON.parse(sessionDetails.value).browser_url;
return `https://automate.browserstack.com/builds/${this.automateResults.buildHash}/sessions/${this.automateResults.sessionHash}`;
});
}

async getTag() {
if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
if (!this.automateResults) throw new Error('Comparison tag details not available');

const automateCaps = this.automateResults.capabilities;
const normalizeTags = new NormalizeData();

let deviceName = this.automateResults.deviceName;
const osName = normalizeTags.osRollUp(automateCaps.os);
const osVersion = automateCaps.os_version?.split('.')[0];
const browserName = normalizeTags.browserRollUp(automateCaps.browserName, this.metaData.device());
const browserVersion = normalizeTags.browserVersionOrDeviceNameRollup(automateCaps.browserVersion, deviceName, this.metaData.device());

if (!this.metaData.device()) {
deviceName = `${osName}_${osVersion}_${browserName}_${browserVersion}`;
}

let { width, height } = await this.metaData.windowSize();
const resolution = await this.metaData.screenResolution();
const orientation = (this.metaData.orientation() || automateCaps.deviceOrientation)?.toLowerCase();

// for android window size only constitutes of browser viewport, hence adding nav / status / url bar heights
[this.header, this.footer] = await this.getHeaderFooter(deviceName, osVersion, browserName);
height = this.metaData.device() && osName?.toLowerCase() === 'android' ? height + this.header + this.footer : height;

return {
name: deviceName,
osName,
osVersion,
width,
height,
orientation,
browserName,
browserVersion,
resolution
};
}
}
15 changes: 9 additions & 6 deletions packages/webdriver-utils/src/providers/genericProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default class GenericProvider {
}

async createDriver() {
this.driver = new Driver(this.sessionId, this.commandExecutorUrl);
this.driver = new Driver(this.sessionId, this.commandExecutorUrl, this.capabilities);
log.debug(`Passed capabilities -> ${JSON.stringify(this.capabilities)}`);
const caps = await this.driver.getCapabilites();
log.debug(`Fetched capabilities -> ${JSON.stringify(caps)}`);
Expand Down Expand Up @@ -119,21 +119,24 @@ export default class GenericProvider {
this.addDefaultOptions();

const percyCSS = (this.defaultPercyCSS() + (this.options.percyCSS || '')).split('\n').join('');
log.debug(`Applying the percyCSS - ${this.options.percyCSS}`);
log.debug(`[${name}] : Applying the percyCSS - ${this.options.percyCSS}`);
await this.addPercyCSS(percyCSS);

log.debug('Fetching comparisong tag ...');
const tag = await this.getTag();
log.debug(`[${name}] : Tag ${JSON.stringify(tag)}`);

const tiles = await this.getTiles(fullscreen);
const ignoreRegions = await this.findIgnoredRegions(
const tiles = await this.getTiles(this.header, this.footer, fullscreen);
log.debug(`[${name}] : Tiles ${JSON.stringify(tiles)}`);

const ignoreRegions = await this.findRegions(
ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions
);
const considerRegions = await this.findRegions(
considerRegionXpaths, considerRegionSelectors, considerRegionElements, customConsiderRegions
);
await this.setDebugUrl();
log.debug(`${name} : Debug url ${this.debugUrl}`);
log.debug(`[${name}] : Debug url ${this.debugUrl}`);

await this.removePercyCSS();
return {
Expand Down Expand Up @@ -296,7 +299,7 @@ export default class GenericProvider {
return ignoredElementsArray;
}

async getHeaderFooter(deviceName, osVersion, browserName) {
async getHeaderFooter(deviceName, osVersion, browserNamedeviceName, osVersion, browserName) {
// passing 0 as key, since across different pages and tests, this config will remain same
const devicesConfig = await Cache.withCache(Cache.devicesConfig, 0, async () => {
return (await request(DEVICES_CONFIG_URL)).body;
Expand Down
2 changes: 2 additions & 0 deletions packages/webdriver-utils/src/util/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ export default class Cache {
static bstackSessionDetails = 'bstackSessionDetails';
static systemBars = 'systemBars';
static devicesConfig = 'devicesConfig';
static dpr = 'dpr';
static resolution = 'resolution';

// maintainance
static lastTime = Date.now();
Expand Down
29 changes: 28 additions & 1 deletion packages/webdriver-utils/test/driver.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ describe('Driver', () => {
};
let sessionId = '123';
let executorUrl = 'http://localhost/wd/hub';
let passedCapabilities = {
browser: 'chrome',
platform: 'win'
};
let driver;

beforeEach(() => {
requestSpy = spyOn(utils.request, 'fetch').and.returnValue(
Promise.resolve(mockResponseObject)
);
driver = new Driver(sessionId, executorUrl);
driver = new Driver(sessionId, executorUrl, passedCapabilities);
});

describe('constructor', () => {
Expand All @@ -34,6 +38,29 @@ describe('Driver', () => {
});
});

describe('getCapabilities fallback', () => {
const mockFailedResponse = {
body: '{"value": {"message" : "Internal Server Error"}',
status: 500,
headers: { 'content-type': 'application/text' }
};
let requestFailedSpy;
const sessionId = '1234';
const newDriver = new Driver(sessionId, executorUrl, passedCapabilities);

beforeEach(() => {
requestFailedSpy = spyOn(utils.request, 'fetch').and.returnValue(
Promise.resolve(mockFailedResponse)
);
});

it('falls back to passed capabilites', async () => {
let res = await newDriver.getCapabilites();
expect(requestFailedSpy).toHaveBeenCalledOnceWith(`${executorUrl}/session/${sessionId}`, Object({}));
expect(res).toBe(passedCapabilities);
});
});

describe('getWindowsize', () => {
it('calls requests', async () => {
let res = await driver.getWindowSize();
Expand Down
Loading

0 comments on commit b758d24

Please sign in to comment.