Skip to content

Commit

Permalink
feat: added support for extracting and passing header and footer heig…
Browse files Browse the repository at this point in the history
…hts (#1305)

* feat: added support for extracting and passing header and footer heights

* test: getHeaderFooter

* chore: lint using fix

* fix: use header and footer height for calculating tag

* test: updated percy-cli tests

* test: update expectations for header and footer heights for getTiles;

* feat: updated getTiles function to use header and footer params, so that it is more testable

* feat: update comparison tag logic

* test: updated getTag spec

* chore: lint using yarn

* feat: add support for header and footer extraction;

* test: update test for extracting header and footer

* chore: lint using fix

* fix: use metadata.osName to detect android

* test: update generaticProvider tests

* chore: added some logging

* fix: webdriver-utils/index logging

* chore: lint using fix

* feat: added caching support for devicesJson

* chore: lint using fix

* test: updated getHeaderFooter spec

* chore: lint fix

* feat: change desktop deviceName format

* test: increase coverage

* chore: add comments around skipping request.test from browser tests

* chore: add webdriver-utils as dependency for core package

* chore: resolve TODO comments
  • Loading branch information
nilshah98 authored and rishigupta1599 committed Aug 7, 2023
1 parent 22a1a26 commit 247b755
Show file tree
Hide file tree
Showing 9 changed files with 92 additions and 16 deletions.
4 changes: 1 addition & 3 deletions packages/core/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import { createRequire } from 'module';
import logger from '@percy/logger';
import { normalize } from '@percy/config/utils';
import { getPackageJSON, Server, percyAutomateRequestHandler } from './utils.js';
// TODO Remove below esline disable once we publish webdriver-util
import WebdriverUtils from '@percy/webdriver-utils'; // eslint-disable-line import/no-extraneous-dependencies

import WebdriverUtils from '@percy/webdriver-utils';
// need require.resolve until import.meta.resolve can be transpiled
export const PERCY_DOM = createRequire(import.meta.url).resolve('@percy/dom');

Expand Down
1 change: 1 addition & 0 deletions packages/sdk-utils/test/request.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import http from 'http';
import fs from 'fs';
import path from 'path';

// NOTE: Although sdk-utils test run in browser as well, we do not run sdk-utils/request test in browsers as we require creation of https server for this test
const ssl = {
cert: fs.readFileSync(path.join(__dirname, 'assets', 'certs', 'test.crt')),
key: fs.readFileSync(path.join(__dirname, 'assets', 'certs', 'test.key'))
Expand Down
1 change: 1 addition & 0 deletions packages/webdriver-utils/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default class WebdriverUtils {
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);
}
}
2 changes: 1 addition & 1 deletion packages/webdriver-utils/src/metadata/desktopMetaData.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default class DesktopMetaData {

// combination of browserName + browserVersion + osVersion + osName
deviceName() {
return this.browserName() + '_' + this.browserVersion() + '_' + this.osVersion() + '_' + this.osName();
return this.osName() + '_' + this.osVersion() + '_' + this.browserName() + '_' + this.browserVersion();
}

orientation() {
Expand Down
57 changes: 50 additions & 7 deletions packages/webdriver-utils/src/providers/genericProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ import utils from '@percy/sdk-utils';
import MetaDataResolver from '../metadata/metaDataResolver.js';
import Tile from '../util/tile.js';
import Driver from '../driver.js';
import Cache from '../util/cache.js';
const { request } = utils;

const DEVICES_CONFIG_URL = 'https://storage.googleapis.com/percy-utils/devices.json';
const log = utils.logger('webdriver-utils:genericProvider');

export default class GenericProvider {
Expand All @@ -29,11 +32,45 @@ export default class GenericProvider {
this.driver = null;
this.metaData = null;
this.debugUrl = null;
this.header = 0;
this.footer = 0;
}

addDefaultOptions() {
this.options.freezeAnimation = this.options.freezeAnimation || false;
}

defaultPercyCSS() {
return `*, *::before, *::after {
-moz-transition: none !important;
transition: none !important;
-moz-animation: none !important;
animation: none !important;
animation-duration: 0 !important;
caret-color: transparent !important;
content-visibility: visible !important;
}
html{
scrollbar-width: auto !important;
}
svg {
shape-rendering: geometricPrecision !important;
}
scrollbar, scrollcorner, scrollbar thumb, scrollbar scrollbarbutton {
pointer-events: none !important;
-moz-appearance: none !important;
display: none !important;
}
video::-webkit-media-controls {
display: none !important;
}`;
}

async createDriver() {
this.driver = new Driver(this.sessionId, this.commandExecutorUrl);
log.debug(`Passed capabilities -> ${JSON.stringify(this.capabilities)}`);
const caps = await this.driver.getCapabilites();
log.debug(`Fetched capabilities -> ${JSON.stringify(caps)}`);
this.metaData = await MetaDataResolver.resolve(this.driver, caps, this.capabilities);
}

Expand Down Expand Up @@ -75,20 +112,23 @@ export default class GenericProvider {
}) {
let fullscreen = false;

const percyCSS = this.options.percyCSS || '';
this.addDefaultOptions();

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

log.debug('Fetching comparisong tag ...');
const tag = await this.getTag();

const tiles = await this.getTiles(fullscreen);
const ignoreRegions = await this.findIgnoredRegions(
ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions
);
await this.setDebugUrl();
await this.removePercyCSS();

log.debug(`${name} : Tag ${JSON.stringify(tag)}`);
log.debug(`${name} : Tiles ${JSON.stringify(tiles)}`);
log.debug(`${name} : Debug url ${this.debugUrl}`);

await this.removePercyCSS();
return {
name,
tag,
Expand All @@ -108,7 +148,7 @@ export default class GenericProvider {
return 'dummyValue';
}

async getTiles(fullscreen) {
async getTiles(headerHeight, footerHeight, fullscreen) {
if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
const base64content = await this.driver.takeScreenshot();
log.debug('Tiles captured successfully');
Expand All @@ -132,6 +172,9 @@ export default class GenericProvider {
if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
const { width, height } = await this.metaData.windowSize();
const orientation = this.metaData.orientation();
[this.header, this.footer] = await this.getHeaderFooter();
// for android window size only constitutes of browser viewport, hence adding nav / status / url bar heights
height = this.metaData.osName() === 'android' ? height + this.header + this.footer : height;
return {
name: this.metaData.deviceName(),
osName: this.metaData.osName(),
Expand All @@ -144,7 +187,7 @@ export default class GenericProvider {
};
}

// TODO: Add Debugging Url
// TODO: Add Debugging Url for non-automate
async setDebugUrl() {
this.debugUrl = 'https://localhost/v1';
}
Expand Down
1 change: 1 addition & 0 deletions packages/webdriver-utils/src/util/cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export default class Cache {
static caps = 'caps';
static bstackSessionDetails = 'bstackSessionDetails';
static systemBars = 'systemBars';
static devicesConfig = 'devicesConfig';

// maintainance
static lastTime = Date.now();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('DesktopMetaData', () => {

describe('deviceName', () => {
it('calculates deviceName', () => {
expect(desktopMetaData.deviceName()).toEqual('chrome_111_10_win');
expect(desktopMetaData.deviceName()).toEqual('win_10_chrome_111');
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ describe('AutomateProvider', () => {

beforeEach(async () => {
spyOn(Driver.prototype, 'getCapabilites');
spyOn(GenericProvider.prototype, 'getHeaderFooter').and.returnValue(Promise.resolve([123, 456]));
browserstackExecutorSpy = spyOn(AutomateProvider.prototype, 'browserstackExecutor')
.and.returnValue(Promise.resolve({ value: '{ "result": "{\\"dom_sha\\": \\"abc\\", \\"sha\\": [\\"abc-1\\", \\"xyz-2\\"]}", "success":true }' }));
executeScriptSpy = spyOn(Driver.prototype, 'executeScript')
Expand All @@ -172,7 +173,7 @@ describe('AutomateProvider', () => {

it('should return tiles when success', async () => {
await automateProvider.createDriver();
const res = await automateProvider.getTiles(false);
const res = await automateProvider.getTiles(123, 456, false);
expect(browserstackExecutorSpy).toHaveBeenCalledTimes(1);
expect(executeScriptSpy).toHaveBeenCalledTimes(1);
expect(Object.keys(res).length).toEqual(2);
Expand All @@ -182,6 +183,10 @@ describe('AutomateProvider', () => {
expect(res.tiles[1]).toBeInstanceOf(Tile);
expect(res.tiles[0].sha).toEqual('abc');
expect(res.tiles[1].sha).toEqual('xyz');
expect(res.tiles[0].headerHeight).toEqual(123);
expect(res.tiles[0].footerHeight).toEqual(456);
expect(res.tiles[0].navBarHeight).toEqual(0);
expect(res.tiles[0].statusBarHeight).toEqual(0);
});

it('throws error when response is false', async () => {
Expand Down
33 changes: 30 additions & 3 deletions packages/webdriver-utils/test/providers/genericProvider.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import GenericProvider from '../../src/providers/genericProvider.js';
import Driver from '../../src/driver.js';
import MetaDataResolver from '../../src/metadata/metaDataResolver.js';
import DesktopMetaData from '../../src/metadata/desktopMetaData.js';
import Cache from '../../src/util/cache.js';
import MobileMetaData from '../../src/metadata/mobileMetaData.js';
import utils from '@percy/sdk-utils';

describe('GenericProvider', () => {
let genericProvider;
Expand Down Expand Up @@ -33,13 +36,18 @@ describe('GenericProvider', () => {
describe('getTiles', () => {
beforeEach(() => {
spyOn(Driver.prototype, 'takeScreenshot').and.returnValue(Promise.resolve('123b='));
spyOn(GenericProvider.prototype, 'getHeaderFooter').and.returnValue(Promise.resolve([123, 456]));
});

it('creates tiles from screenshot', async () => {
genericProvider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}, 'local-poc-poa', 'staging-poc-poa', {});
genericProvider.createDriver();
const tiles = await genericProvider.getTiles(false);
const tiles = await genericProvider.getTiles(123, 456, false);
expect(tiles.tiles.length).toEqual(1);
expect(tiles.tiles[0].navBarHeight).toEqual(0);
expect(tiles.tiles[0].statusBarHeight).toEqual(0);
expect(tiles.tiles[0].footerHeight).toEqual(456);
expect(tiles.tiles[0].headerHeight).toEqual(123);
expect(Object.keys(tiles)).toContain('domInfoSha');
});

Expand Down Expand Up @@ -67,8 +75,27 @@ describe('GenericProvider', () => {
.and.returnValue('111');
});

it('returns correct tag', async () => {
it('returns correct tag for android', async () => {
genericProvider = new GenericProvider('123', 'http:executorUrl', { platform: 'android', platformName: 'android' }, {}, 'local-poc-poa', 'staging-poc-poa', {});
spyOn(MobileMetaData.prototype, 'osName').and.returnValue('android');
await genericProvider.createDriver();
const tag = await genericProvider.getTag();
expect(tag).toEqual({
name: 'mockDeviceName',
osName: 'android',
osVersion: 'mockOsVersion',
width: 1000,
height: 1000 + 123 + 456,
orientation: 'landscape',
browserName: 'mockBrowserName',
browserVersion: '111',
resolution: '1980 x 1080'
});
});

it('returns correct tag for others', async () => {
genericProvider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}, 'local-poc-poa', 'staging-poc-poa', {});
spyOn(MobileMetaData.prototype, 'osName').and.returnValue('mockOsName');
await genericProvider.createDriver();
const tag = await genericProvider.getTag();
expect(tag).toEqual({
Expand Down Expand Up @@ -110,7 +137,7 @@ describe('GenericProvider', () => {
let res = await genericProvider.screenshot('mock-name', {});
expect(addPercyCSSSpy).toHaveBeenCalledTimes(1);
expect(getTagSpy).toHaveBeenCalledTimes(1);
expect(getTilesSpy).toHaveBeenCalledOnceWith(false);
expect(getTilesSpy).toHaveBeenCalledOnceWith(0, 0, false);
expect(removePercyCSSSpy).toHaveBeenCalledTimes(1);
expect(res).toEqual({
name: 'mock-name',
Expand Down

0 comments on commit 247b755

Please sign in to comment.