@percy/webdriver-utils 1.27.0-beta.0 → 1.27.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/driver.js CHANGED
@@ -3,16 +3,23 @@ import Cache from './util/cache.js';
3
3
  const {
4
4
  request
5
5
  } = utils;
6
+ const log = utils.logger('webdriver-utils:driver');
6
7
  export default class Driver {
7
- constructor(sessionId, executorUrl) {
8
+ constructor(sessionId, executorUrl, passedCapabilities) {
8
9
  this.sessionId = sessionId;
9
10
  this.executorUrl = executorUrl.includes('@') ? `https://${executorUrl.split('@')[1]}` : executorUrl;
11
+ this.passedCapabilities = passedCapabilities;
10
12
  }
11
13
  async getCapabilites() {
12
14
  return await Cache.withCache(Cache.caps, this.sessionId, async () => {
13
- const baseUrl = `${this.executorUrl}/session/${this.sessionId}`;
14
- const caps = JSON.parse((await request(baseUrl)).body);
15
- return caps.value;
15
+ try {
16
+ const baseUrl = `${this.executorUrl}/session/${this.sessionId}`;
17
+ const caps = JSON.parse((await request(baseUrl)).body);
18
+ return caps.value;
19
+ } catch (err) {
20
+ log.warn(`Falling back to legacy protocol, Error: ${err.message}`);
21
+ return this.passedCapabilities;
22
+ }
16
23
  });
17
24
  }
18
25
  async getWindowSize() {
@@ -26,6 +33,11 @@ export default class Driver {
26
33
  if (!command.constructor === Object || !(Object.keys(command).length === 2 && Object.keys(command).includes('script') && Object.keys(command).includes('args'))) {
27
34
  throw new Error('Please pass command as {script: "", args: []}');
28
35
  }
36
+ // browser_executor is custom BS executor script, if there is anything extra it breaks
37
+ // percy_automate_script is an anchor comment to identify percy automate scripts
38
+ if (!command.script.includes('browserstack_executor')) {
39
+ command.script = `/* percy_automate_script */ \n ${command.script}`;
40
+ }
29
41
  const options = {
30
42
  method: 'POST',
31
43
  headers: {
package/dist/index.js CHANGED
@@ -30,11 +30,16 @@ export default class WebdriverUtils {
30
30
  this.buildInfo = buildInfo;
31
31
  }
32
32
  async automateScreenshot() {
33
- this.log.info('Starting automate screenshot ...');
34
- const automate = ProviderResolver.resolve(this.sessionId, this.commandExecutorUrl, this.capabilities, this.sessionCapabilites, this.clientInfo, this.environmentInfo, this.options, this.buildInfo);
35
- this.log.debug('Resolved provider ...');
36
- await automate.createDriver();
37
- this.log.debug('Created driver ...');
38
- return await automate.screenshot(this.snapshotName, this.options);
33
+ try {
34
+ this.log.info(`[${this.snapshotName}] : Starting automate screenshot ...`);
35
+ const automate = ProviderResolver.resolve(this.sessionId, this.commandExecutorUrl, this.capabilities, this.sessionCapabilites, this.clientInfo, this.environmentInfo, this.options, this.buildInfo);
36
+ this.log.debug(`[${this.snapshotName}] : Resolved provider ...`);
37
+ await automate.createDriver();
38
+ this.log.debug(`[${this.snapshotName}] : Created driver ...`);
39
+ return await automate.screenshot(this.snapshotName, this.options);
40
+ } catch (e) {
41
+ this.log.error(`[${this.snapshotName}] : Error - ${e.message}`);
42
+ this.log.error(`[${this.snapshotName}] : Error Log - ${e.toString()}`);
43
+ }
39
44
  }
40
45
  }
@@ -1,24 +1,32 @@
1
+ import Cache from '../util/cache.js';
1
2
  export default class DesktopMetaData {
2
3
  constructor(driver, opts) {
3
4
  this.driver = driver;
4
5
  this.capabilities = opts;
5
6
  }
7
+ device() {
8
+ return false;
9
+ }
6
10
  browserName() {
7
- return this.capabilities.browserName.toLowerCase();
11
+ var _this$capabilities, _this$capabilities$br;
12
+ return (_this$capabilities = this.capabilities) === null || _this$capabilities === void 0 ? void 0 : (_this$capabilities$br = _this$capabilities.browserName) === null || _this$capabilities$br === void 0 ? void 0 : _this$capabilities$br.toLowerCase();
8
13
  }
9
14
  browserVersion() {
10
- return this.capabilities.browserVersion.split('.')[0];
15
+ var _this$capabilities2, _this$capabilities2$b;
16
+ return (_this$capabilities2 = this.capabilities) === null || _this$capabilities2 === void 0 ? void 0 : (_this$capabilities2$b = _this$capabilities2.browserVersion) === null || _this$capabilities2$b === void 0 ? void 0 : _this$capabilities2$b.split('.')[0];
11
17
  }
12
18
  osName() {
13
- let osName = this.capabilities.os;
14
- if (osName) return osName.toLowerCase();
15
- osName = this.capabilities.platform;
19
+ var _this$capabilities3, _osName, _this$capabilities4;
20
+ let osName = (_this$capabilities3 = this.capabilities) === null || _this$capabilities3 === void 0 ? void 0 : _this$capabilities3.os;
21
+ if (osName) return (_osName = osName) === null || _osName === void 0 ? void 0 : _osName.toLowerCase();
22
+ osName = (_this$capabilities4 = this.capabilities) === null || _this$capabilities4 === void 0 ? void 0 : _this$capabilities4.platform;
16
23
  return osName;
17
24
  }
18
25
 
19
26
  // showing major version
20
27
  osVersion() {
21
- return this.capabilities.osVersion.toLowerCase();
28
+ var _this$capabilities5, _this$capabilities5$o;
29
+ return (_this$capabilities5 = this.capabilities) === null || _this$capabilities5 === void 0 ? void 0 : (_this$capabilities5$o = _this$capabilities5.osVersion) === null || _this$capabilities5$o === void 0 ? void 0 : _this$capabilities5$o.toLowerCase();
22
30
  }
23
31
 
24
32
  // combination of browserName + browserVersion + osVersion + osName
@@ -39,18 +47,22 @@ export default class DesktopMetaData {
39
47
  };
40
48
  }
41
49
  async screenResolution() {
42
- const data = await this.driver.executeScript({
43
- script: 'return [(window.screen.width * window.devicePixelRatio).toString(), (window.screen.height * window.devicePixelRatio).toString()];',
44
- args: []
50
+ return await Cache.withCache(Cache.resolution, this.driver.sessionId, async () => {
51
+ const data = await this.driver.executeScript({
52
+ script: 'return [(window.screen.width * window.devicePixelRatio).toString(), (window.screen.height * window.devicePixelRatio).toString()];',
53
+ args: []
54
+ });
55
+ const screenInfo = data.value;
56
+ return `${screenInfo[0]} x ${screenInfo[1]}`;
45
57
  });
46
- const screenInfo = data.value;
47
- return `${screenInfo[0]} x ${screenInfo[1]}`;
48
58
  }
49
59
  async devicePixelRatio() {
50
- const devicePixelRatio = await this.driver.executeScript({
51
- script: 'return window.devicePixelRatio;',
52
- args: []
60
+ return await Cache.withCache(Cache.dpr, this.driver.sessionId, async () => {
61
+ const devicePixelRatio = await this.driver.executeScript({
62
+ script: 'return window.devicePixelRatio;',
63
+ args: []
64
+ });
65
+ return devicePixelRatio.value;
53
66
  });
54
- return devicePixelRatio.value;
55
67
  }
56
68
  }
@@ -2,9 +2,10 @@ import DesktopMetaData from './desktopMetaData.js';
2
2
  import MobileMetaData from './mobileMetaData.js';
3
3
  export default class MetaDataResolver {
4
4
  static resolve(driver, capabilities, opts) {
5
+ var _capabilities$platfor, _capabilities$device, _capabilities$device$;
5
6
  if (!driver) throw new Error('Please pass a Driver object');
6
7
  const platform = opts.platformName || opts.platform;
7
- if (['ios', 'android'].includes(platform.toLowerCase())) {
8
+ if (['ios', 'android'].includes(platform.toLowerCase()) || ['ios', 'android'].includes(capabilities === null || capabilities === void 0 ? void 0 : (_capabilities$platfor = capabilities.platformName) === null || _capabilities$platfor === void 0 ? void 0 : _capabilities$platfor.toLowerCase()) || ['ipad', 'iphone'].includes(capabilities === null || capabilities === void 0 ? void 0 : (_capabilities$device = capabilities.device) === null || _capabilities$device === void 0 ? void 0 : (_capabilities$device$ = _capabilities$device.toString()) === null || _capabilities$device$ === void 0 ? void 0 : _capabilities$device$.toLowerCase())) {
8
9
  return new MobileMetaData(driver, capabilities);
9
10
  } else {
10
11
  return new DesktopMetaData(driver, capabilities);
@@ -1,34 +1,44 @@
1
+ import Cache from '../util/cache.js';
2
+ // Todo: Implement a base metadata for the common functions.
1
3
  export default class MobileMetaData {
2
4
  constructor(driver, opts) {
3
5
  this.driver = driver;
4
6
  this.capabilities = opts;
5
7
  }
8
+ device() {
9
+ return true;
10
+ }
6
11
  browserName() {
7
- return this.capabilities.browserName.toLowerCase();
12
+ var _this$capabilities, _this$capabilities$br;
13
+ return (_this$capabilities = this.capabilities) === null || _this$capabilities === void 0 ? void 0 : (_this$capabilities$br = _this$capabilities.browserName) === null || _this$capabilities$br === void 0 ? void 0 : _this$capabilities$br.toLowerCase();
8
14
  }
9
15
  browserVersion() {
10
- var _this$capabilities$br;
11
- const bsVersion = (_this$capabilities$br = this.capabilities.browserVersion) === null || _this$capabilities$br === void 0 ? void 0 : _this$capabilities$br.split('.');
16
+ var _this$capabilities$br2, _this$capabilities2, _this$capabilities2$v;
17
+ const bsVersion = (_this$capabilities$br2 = this.capabilities.browserVersion) === null || _this$capabilities$br2 === void 0 ? void 0 : _this$capabilities$br2.split('.');
12
18
  if ((bsVersion === null || bsVersion === void 0 ? void 0 : bsVersion.length) > 0) {
13
19
  return bsVersion[0];
14
20
  }
15
- return this.capabilities.version.split('.')[0];
21
+ return (_this$capabilities2 = this.capabilities) === null || _this$capabilities2 === void 0 ? void 0 : (_this$capabilities2$v = _this$capabilities2.version) === null || _this$capabilities2$v === void 0 ? void 0 : _this$capabilities2$v.split('.')[0];
16
22
  }
17
23
  osName() {
18
- let osName = this.capabilities.os.toLowerCase();
24
+ var _this$capabilities3, _this$capabilities3$o;
25
+ let osName = (_this$capabilities3 = this.capabilities) === null || _this$capabilities3 === void 0 ? void 0 : (_this$capabilities3$o = _this$capabilities3.os) === null || _this$capabilities3$o === void 0 ? void 0 : _this$capabilities3$o.toLowerCase();
19
26
  if (osName === 'mac' && this.browserName() === 'iphone') {
20
27
  osName = 'ios';
21
28
  }
22
29
  return osName;
23
30
  }
24
31
  osVersion() {
25
- return this.capabilities.osVersion.split('.')[0];
32
+ var _this$capabilities4, _this$capabilities4$o;
33
+ return (_this$capabilities4 = this.capabilities) === null || _this$capabilities4 === void 0 ? void 0 : (_this$capabilities4$o = _this$capabilities4.osVersion) === null || _this$capabilities4$o === void 0 ? void 0 : _this$capabilities4$o.split('.')[0];
26
34
  }
27
35
  deviceName() {
28
- return this.capabilities.deviceName.split('-')[0];
36
+ var _this$capabilities5, _this$capabilities5$d;
37
+ return (_this$capabilities5 = this.capabilities) === null || _this$capabilities5 === void 0 ? void 0 : (_this$capabilities5$d = _this$capabilities5.deviceName) === null || _this$capabilities5$d === void 0 ? void 0 : _this$capabilities5$d.split('-')[0];
29
38
  }
30
39
  orientation() {
31
- return this.capabilities.orientation;
40
+ var _this$capabilities6;
41
+ return (_this$capabilities6 = this.capabilities) === null || _this$capabilities6 === void 0 ? void 0 : _this$capabilities6.orientation;
32
42
  }
33
43
  async windowSize() {
34
44
  const dpr = await this.devicePixelRatio();
@@ -41,18 +51,22 @@ export default class MobileMetaData {
41
51
  };
42
52
  }
43
53
  async screenResolution() {
44
- const data = await this.driver.executeScript({
45
- script: 'return [(window.screen.width * window.devicePixelRatio).toString(), (window.screen.height * window.devicePixelRatio).toString()];',
46
- args: []
54
+ return await Cache.withCache(Cache.resolution, this.driver.sessionId, async () => {
55
+ const data = await this.driver.executeScript({
56
+ script: 'return [parseInt(window.screen.width * window.devicePixelRatio).toString(), parseInt(window.screen.height * window.devicePixelRatio).toString()];',
57
+ args: []
58
+ });
59
+ const screenInfo = data.value;
60
+ return `${screenInfo[0]} x ${screenInfo[1]}`;
47
61
  });
48
- const screenInfo = data.value;
49
- return `${screenInfo[0]} x ${screenInfo[1]}`;
50
62
  }
51
63
  async devicePixelRatio() {
52
- const devicePixelRatio = await this.driver.executeScript({
53
- script: 'return window.devicePixelRatio;',
54
- args: []
64
+ return await Cache.withCache(Cache.dpr, this.driver.sessionId, async () => {
65
+ const devicePixelRatio = await this.driver.executeScript({
66
+ script: 'return window.devicePixelRatio;',
67
+ args: []
68
+ });
69
+ return devicePixelRatio.value;
55
70
  });
56
- return devicePixelRatio.value;
57
71
  }
58
72
  }
@@ -0,0 +1,34 @@
1
+ export default class NormalizeData {
2
+ osRollUp(os) {
3
+ if (os.toLowerCase().startsWith('win')) {
4
+ return 'Windows';
5
+ } else if (os.toLowerCase().startsWith('mac')) {
6
+ return 'OS X';
7
+ } else if (os.toLowerCase().includes('iphone') || os.toLowerCase().startsWith('ios')) {
8
+ return 'iOS';
9
+ } else if (os.toLowerCase().startsWith('android')) {
10
+ return 'Android';
11
+ }
12
+ return os;
13
+ }
14
+ browserRollUp(browserName, device) {
15
+ if (device) {
16
+ if (browserName !== null && browserName !== void 0 && browserName.toLowerCase().includes('chrome')) {
17
+ return 'chrome';
18
+ } else if (browserName !== null && browserName !== void 0 && browserName.toLowerCase().includes('iphone') || browserName !== null && browserName !== void 0 && browserName.toLowerCase().includes('ipad')) {
19
+ return 'safari';
20
+ }
21
+ }
22
+ return browserName;
23
+ }
24
+
25
+ // Responses for browser version differ for devices and desktops from capabilities
26
+ // Differenences in selenium and appium responses causes inconsistency
27
+ // So to tackle for devices on UI we will show device names else browser versions
28
+ browserVersionOrDeviceNameRollup(browserVersion, deviceName, device) {
29
+ if (device) {
30
+ return deviceName;
31
+ }
32
+ return browserVersion === null || browserVersion === void 0 ? void 0 : browserVersion.split('.')[0];
33
+ }
34
+ }
@@ -2,6 +2,7 @@ import utils from '@percy/sdk-utils';
2
2
  import GenericProvider from './genericProvider.js';
3
3
  import Cache from '../util/cache.js';
4
4
  import Tile from '../util/tile.js';
5
+ import NormalizeData from '../metadata/normalizeData.js';
5
6
  const log = utils.logger('webdriver-utils:automateProvider');
6
7
  const {
7
8
  TimeIt
@@ -10,6 +11,7 @@ export default class AutomateProvider extends GenericProvider {
10
11
  constructor(sessionId, commandExecutorUrl, capabilities, sessionCapabilites, clientInfo, environmentInfo, options, buildInfo) {
11
12
  super(sessionId, commandExecutorUrl, capabilities, sessionCapabilites, clientInfo, environmentInfo, options, buildInfo);
12
13
  this._markedPercy = false;
14
+ this.automateResults = null;
13
15
  }
14
16
  static supports(commandExecutorUrl) {
15
17
  return commandExecutorUrl.includes(process.env.AA_DOMAIN || 'browserstack');
@@ -18,21 +20,30 @@ export default class AutomateProvider extends GenericProvider {
18
20
  ignoreRegionXpaths = [],
19
21
  ignoreRegionSelectors = [],
20
22
  ignoreRegionElements = [],
21
- customIgnoreRegions = []
23
+ customIgnoreRegions = [],
24
+ considerRegionXpaths = [],
25
+ considerRegionSelectors = [],
26
+ considerRegionElements = [],
27
+ customConsiderRegions = []
22
28
  }) {
23
29
  let response = null;
24
30
  let error;
25
- log.info('Preparing to capture screenshots on automate ...');
31
+ log.debug(`[${name}] : Preparing to capture screenshots on automate ...`);
26
32
  try {
27
- log.debug('Marking automate session as percy ...');
28
- let result = await this.percyScreenshotBegin(name);
29
- log.debug('Fetching the debug url ...');
30
- this.setDebugUrl(result);
33
+ log.debug(`[${name}] : Marking automate session as percy ...`);
34
+ const result = await this.percyScreenshotBegin(name);
35
+ this.automateResults = JSON.parse(result.value);
36
+ log.debug(`[${name}] : Fetching the debug url ...`);
37
+ this.setDebugUrl();
31
38
  response = await super.screenshot(name, {
32
39
  ignoreRegionXpaths,
33
40
  ignoreRegionSelectors,
34
41
  ignoreRegionElements,
35
- customIgnoreRegions
42
+ customIgnoreRegions,
43
+ considerRegionXpaths,
44
+ considerRegionSelectors,
45
+ considerRegionElements,
46
+ customConsiderRegions
36
47
  });
37
48
  } catch (e) {
38
49
  error = e;
@@ -55,8 +66,8 @@ export default class AutomateProvider extends GenericProvider {
55
66
  this._markedPercy = result.success;
56
67
  return result;
57
68
  } catch (e) {
58
- log.debug(`[${name}] Could not mark Automate session as percy`);
59
- log.error(`[${name}] error: ${e.toString()}`);
69
+ log.debug(`[${name}] : Could not mark Automate session as percy`);
70
+ log.error(`[${name}] : error: ${e.toString()}`);
60
71
  return null;
61
72
  }
62
73
  });
@@ -72,23 +83,20 @@ export default class AutomateProvider extends GenericProvider {
72
83
  state: 'end'
73
84
  });
74
85
  } catch (e) {
75
- log.debug(`[${name}] Could not execute percyScreenshot command for Automate`);
86
+ log.debug(`[${name}] : Could not execute percyScreenshot command for Automate`);
76
87
  log.error(e);
77
88
  }
78
89
  });
79
90
  }
80
91
  async getTiles(headerHeight, footerHeight, fullscreen) {
81
92
  if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
82
- log.info('Starting actual screenshotting phase');
93
+ log.debug('Starting actual screenshotting phase');
83
94
  const response = await TimeIt.run('percyScreenshot:screenshot', async () => {
84
95
  return await this.browserstackExecutor('percyScreenshot', {
85
96
  state: 'screenshot',
86
97
  percyBuildId: this.buildInfo.id,
87
98
  screenshotType: 'singlepage',
88
- scaleFactor: await this.driver.executeScript({
89
- script: 'return window.devicePixelRatio;',
90
- args: []
91
- }),
99
+ scaleFactor: await this.metaData.devicePixelRatio(),
92
100
  options: this.options
93
101
  });
94
102
  });
@@ -132,8 +140,43 @@ export default class AutomateProvider extends GenericProvider {
132
140
  async setDebugUrl() {
133
141
  if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
134
142
  this.debugUrl = await Cache.withCache(Cache.bstackSessionDetails, this.driver.sessionId, async () => {
135
- const sessionDetails = await this.browserstackExecutor('getSessionDetails');
136
- return JSON.parse(sessionDetails.value).browser_url;
143
+ return `https://automate.browserstack.com/builds/${this.automateResults.buildHash}/sessions/${this.automateResults.sessionHash}`;
137
144
  });
138
145
  }
146
+ async getTag() {
147
+ var _automateCaps$os_vers, _ref;
148
+ if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
149
+ if (!this.automateResults) throw new Error('Comparison tag details not available');
150
+ const automateCaps = this.automateResults.capabilities;
151
+ const normalizeTags = new NormalizeData();
152
+ let deviceName = this.automateResults.deviceName;
153
+ const osName = normalizeTags.osRollUp(automateCaps.os);
154
+ const osVersion = (_automateCaps$os_vers = automateCaps.os_version) === null || _automateCaps$os_vers === void 0 ? void 0 : _automateCaps$os_vers.split('.')[0];
155
+ const browserName = normalizeTags.browserRollUp(automateCaps.browserName, this.metaData.device());
156
+ const browserVersion = normalizeTags.browserVersionOrDeviceNameRollup(automateCaps.browserVersion, deviceName, this.metaData.device());
157
+ if (!this.metaData.device()) {
158
+ deviceName = `${osName}_${osVersion}_${browserName}_${browserVersion}`;
159
+ }
160
+ let {
161
+ width,
162
+ height
163
+ } = await this.metaData.windowSize();
164
+ const resolution = await this.metaData.screenResolution();
165
+ const orientation = (_ref = this.metaData.orientation() || automateCaps.deviceOrientation) === null || _ref === void 0 ? void 0 : _ref.toLowerCase();
166
+
167
+ // for android window size only constitutes of browser viewport, hence adding nav / status / url bar heights
168
+ [this.header, this.footer] = await this.getHeaderFooter(deviceName, osVersion, browserName);
169
+ height = this.metaData.device() && (osName === null || osName === void 0 ? void 0 : osName.toLowerCase()) === 'android' ? height + this.header + this.footer : height;
170
+ return {
171
+ name: deviceName,
172
+ osName,
173
+ osVersion,
174
+ width,
175
+ height,
176
+ orientation,
177
+ browserName,
178
+ browserVersion,
179
+ resolution
180
+ };
181
+ }
139
182
  }
@@ -56,7 +56,7 @@ export default class GenericProvider {
56
56
  }`;
57
57
  }
58
58
  async createDriver() {
59
- this.driver = new Driver(this.sessionId, this.commandExecutorUrl);
59
+ this.driver = new Driver(this.sessionId, this.commandExecutorUrl, this.capabilities);
60
60
  log.debug(`Passed capabilities -> ${JSON.stringify(this.capabilities)}`);
61
61
  const caps = await this.driver.getCapabilites();
62
62
  log.debug(`Fetched capabilities -> ${JSON.stringify(caps)}`);
@@ -97,21 +97,26 @@ export default class GenericProvider {
97
97
  ignoreRegionXpaths = [],
98
98
  ignoreRegionSelectors = [],
99
99
  ignoreRegionElements = [],
100
- customIgnoreRegions = []
100
+ customIgnoreRegions = [],
101
+ considerRegionXpaths = [],
102
+ considerRegionSelectors = [],
103
+ considerRegionElements = [],
104
+ customConsiderRegions = []
101
105
  }) {
102
106
  let fullscreen = false;
103
107
  this.addDefaultOptions();
104
108
  const percyCSS = (this.defaultPercyCSS() + (this.options.percyCSS || '')).split('\n').join('');
105
- log.debug(`Applying the percyCSS - ${this.options.percyCSS}`);
109
+ log.debug(`[${name}] : Applying the percyCSS - ${this.options.percyCSS}`);
106
110
  await this.addPercyCSS(percyCSS);
107
111
  log.debug('Fetching comparisong tag ...');
108
112
  const tag = await this.getTag();
109
- log.debug(`${name} : Tag ${JSON.stringify(tag)}`);
113
+ log.debug(`[${name}] : Tag ${JSON.stringify(tag)}`);
110
114
  const tiles = await this.getTiles(this.header, this.footer, fullscreen);
111
- log.debug(`${name} : Tiles ${JSON.stringify(tiles)}`);
112
- const ignoreRegions = await this.findIgnoredRegions(ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions);
115
+ log.debug(`[${name}] : Tiles ${JSON.stringify(tiles)}`);
116
+ const ignoreRegions = await this.findRegions(ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions);
117
+ const considerRegions = await this.findRegions(considerRegionXpaths, considerRegionSelectors, considerRegionElements, customConsiderRegions);
113
118
  await this.setDebugUrl();
114
- log.debug(`${name} : Debug url ${this.debugUrl}`);
119
+ log.debug(`[${name}] : Debug url ${this.debugUrl}`);
115
120
  await this.removePercyCSS();
116
121
  return {
117
122
  name,
@@ -119,7 +124,12 @@ export default class GenericProvider {
119
124
  tiles: tiles.tiles,
120
125
  // TODO: Fetch this one for bs automate, check appium sdk
121
126
  externalDebugUrl: this.debugUrl,
122
- ignoredElementsData: ignoreRegions,
127
+ ignoredElementsData: {
128
+ ignoreElementsData: ignoreRegions
129
+ },
130
+ consideredElementsData: {
131
+ considerElementsData: considerRegions
132
+ },
123
133
  environmentInfo: [...this.environmentInfo].join('; '),
124
134
  clientInfo: [...this.clientInfo].join(' '),
125
135
  domInfoSha: tiles.domInfoSha
@@ -176,16 +186,14 @@ export default class GenericProvider {
176
186
  async setDebugUrl() {
177
187
  this.debugUrl = 'https://localhost/v1';
178
188
  }
179
- async findIgnoredRegions(ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions) {
180
- const ignoreElementXpaths = await this.getIgnoreRegionsBy('xpath', ignoreRegionXpaths);
181
- const ignoreElementSelectors = await this.getIgnoreRegionsBy('css selector', ignoreRegionSelectors);
182
- const ignoreElements = await this.getIgnoreRegionsByElement(ignoreRegionElements);
183
- const ignoreElementCustom = await this.getCustomIgnoreRegions(customIgnoreRegions);
184
- return {
185
- ignoreElementsData: [...ignoreElementXpaths, ...ignoreElementSelectors, ...ignoreElements, ...ignoreElementCustom]
186
- };
189
+ async findRegions(xpaths, selectors, elements, customLocations) {
190
+ const xpathRegions = await this.getSeleniumRegionsBy('xpath', xpaths);
191
+ const selectorRegions = await this.getSeleniumRegionsBy('css selector', selectors);
192
+ const elementRegions = await this.getSeleniumRegionsByElement(elements);
193
+ const customRegions = await this.getSeleniumRegionsByLocation(customLocations);
194
+ return [...xpathRegions, ...selectorRegions, ...elementRegions, ...customRegions];
187
195
  }
188
- async ignoreElementObject(selector, elementId) {
196
+ async getRegionObject(selector, elementId) {
189
197
  const scaleFactor = parseInt(await this.metaData.devicePixelRatio());
190
198
  const rect = await this.driver.rect(elementId);
191
199
  const location = {
@@ -208,37 +216,37 @@ export default class GenericProvider {
208
216
  };
209
217
  return jsonObject;
210
218
  }
211
- async getIgnoreRegionsBy(findBy, elements) {
212
- const ignoredElementsArray = [];
219
+ async getSeleniumRegionsBy(findBy, elements) {
220
+ const regionsArray = [];
213
221
  for (const idx in elements) {
214
222
  try {
215
223
  const element = await this.driver.findElement(findBy, elements[idx]);
216
224
  const selector = `${findBy}: ${elements[idx]}`;
217
- const ignoredRegion = await this.ignoreElementObject(selector, element[Object.keys(element)[0]]);
218
- ignoredElementsArray.push(ignoredRegion);
225
+ const region = await this.getRegionObject(selector, element[Object.keys(element)[0]]);
226
+ regionsArray.push(region);
219
227
  } catch (e) {
220
228
  log.warn(`Selenium Element with ${findBy}: ${elements[idx]} not found. Ignoring this ${findBy}.`);
221
229
  log.error(e.toString());
222
230
  }
223
231
  }
224
- return ignoredElementsArray;
232
+ return regionsArray;
225
233
  }
226
- async getIgnoreRegionsByElement(elements) {
227
- const ignoredElementsArray = [];
234
+ async getSeleniumRegionsByElement(elements) {
235
+ const regionsArray = [];
228
236
  for (let index = 0; index < elements.length; index++) {
229
237
  try {
230
238
  const selector = `element: ${index}`;
231
- const ignoredRegion = await this.ignoreElementObject(selector, elements[index]);
232
- ignoredElementsArray.push(ignoredRegion);
239
+ const region = await this.getRegionObject(selector, elements[index]);
240
+ regionsArray.push(region);
233
241
  } catch (e) {
234
242
  log.warn(`Correct Web Element not passed at index ${index}.`);
235
243
  log.debug(e.toString());
236
244
  }
237
245
  }
238
- return ignoredElementsArray;
246
+ return regionsArray;
239
247
  }
240
- async getCustomIgnoreRegions(customLocations) {
241
- const ignoredElementsArray = [];
248
+ async getSeleniumRegionsByLocation(customLocations) {
249
+ const elementsArray = [];
242
250
  const {
243
251
  width,
244
252
  height
@@ -247,8 +255,8 @@ export default class GenericProvider {
247
255
  const customLocation = customLocations[index];
248
256
  const invalid = customLocation.top >= height || customLocation.bottom > height || customLocation.left >= width || customLocation.right > width;
249
257
  if (!invalid) {
250
- const selector = `custom ignore region ${index}`;
251
- const ignoredRegion = {
258
+ const selector = `custom region ${index}`;
259
+ const region = {
252
260
  selector,
253
261
  coOrdinates: {
254
262
  top: customLocation.top,
@@ -257,20 +265,19 @@ export default class GenericProvider {
257
265
  right: customLocation.right
258
266
  }
259
267
  };
260
- ignoredElementsArray.push(ignoredRegion);
268
+ elementsArray.push(region);
261
269
  } else {
262
270
  log.warn(`Values passed in custom ignored region at index: ${index} is not valid`);
263
271
  }
264
272
  }
265
- return ignoredElementsArray;
273
+ return elementsArray;
266
274
  }
267
- async getHeaderFooter() {
275
+ async getHeaderFooter(deviceName, osVersion, browserName) {
268
276
  // passing 0 as key, since across different pages and tests, this config will remain same
269
277
  const devicesConfig = await Cache.withCache(Cache.devicesConfig, 0, async () => {
270
278
  return (await request(DEVICES_CONFIG_URL)).body;
271
279
  });
272
- let deviceKey = `${this.metaData.deviceName()}-${this.metaData.osVersion()}`;
273
- let browserName = this.capabilities.browserName;
280
+ let deviceKey = `${deviceName}-${osVersion}`;
274
281
  return devicesConfig[deviceKey] ? devicesConfig[deviceKey][browserName] ? [devicesConfig[deviceKey][browserName].header, devicesConfig[deviceKey][browserName].footer] : [0, 0] : [0, 0];
275
282
  }
276
283
  }
@@ -10,6 +10,8 @@ export default class Cache {
10
10
  static bstackSessionDetails = 'bstackSessionDetails';
11
11
  static systemBars = 'systemBars';
12
12
  static devicesConfig = 'devicesConfig';
13
+ static dpr = 'dpr';
14
+ static resolution = 'resolution';
13
15
 
14
16
  // maintainance
15
17
  static lastTime = Date.now();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/webdriver-utils",
3
- "version": "1.27.0-beta.0",
3
+ "version": "1.27.0-beta.1",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -29,8 +29,8 @@
29
29
  "test:coverage": "yarn test --coverage"
30
30
  },
31
31
  "dependencies": {
32
- "@percy/config": "1.27.0-beta.0",
33
- "@percy/sdk-utils": "1.27.0-beta.0"
32
+ "@percy/config": "1.27.0-beta.1",
33
+ "@percy/sdk-utils": "1.27.0-beta.1"
34
34
  },
35
- "gitHead": "2bc16314f51dddcc1cda459e7aa4b7b2db85f00a"
35
+ "gitHead": "40cdf9c38613ccaf5e3707cd2cd2d2778ffbd5dd"
36
36
  }
@@ -1,47 +0,0 @@
1
- import validations from './validations.js';
2
- const {
3
- Undefined
4
- } = validations;
5
- export default class TimeIt {
6
- static data = {};
7
- static enabled = process.env.PERCY_METRICS === 'true';
8
- static async run(store, func) {
9
- if (!this.enabled) return await func();
10
- const t1 = Date.now();
11
- try {
12
- return await func();
13
- } finally {
14
- if (Undefined(this.data[store])) this.data[store] = [];
15
- this.data[store].push(Date.now() - t1);
16
- }
17
- }
18
- static min(store) {
19
- return Math.min(...this.data[store]);
20
- }
21
- static max(store) {
22
- return Math.max(...this.data[store]);
23
- }
24
- static avg(store) {
25
- const vals = this.data[store];
26
- return vals.reduce((a, b) => a + b, 0) / vals.length;
27
- }
28
- static summary({
29
- includeVals
30
- } = {}) {
31
- const agg = {};
32
- for (const key of Object.keys(this.data)) {
33
- agg[key] = {
34
- min: this.min(key),
35
- max: this.max(key),
36
- avg: this.avg(key),
37
- count: this.data[key].length
38
- };
39
- if (includeVals) agg[key].vals = this.data[key];
40
- }
41
- return agg;
42
- }
43
- static reset() {
44
- this.data = {};
45
- }
46
- }
47
- ;
@@ -1,7 +0,0 @@
1
- function Undefined(obj) {
2
- return obj === undefined;
3
- }
4
- export { Undefined };
5
-
6
- // export the namespace by default
7
- export * as default from './validations.js';