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

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/index.js CHANGED
@@ -11,7 +11,8 @@ export default class WebdriverUtils {
11
11
  snapshotName,
12
12
  clientInfo,
13
13
  environmentInfo,
14
- options = {}
14
+ options = {},
15
+ buildInfo = {}
15
16
  }) {
16
17
  this.sessionId = sessionId;
17
18
  this.commandExecutorUrl = commandExecutorUrl;
@@ -26,11 +27,14 @@ export default class WebdriverUtils {
26
27
  this.options = camelCasedOptions;
27
28
  this.clientInfo = clientInfo;
28
29
  this.environmentInfo = environmentInfo;
30
+ this.buildInfo = buildInfo;
29
31
  }
30
32
  async automateScreenshot() {
31
- this.log.info('Starting automate screenshot');
32
- const automate = ProviderResolver.resolve(this.sessionId, this.commandExecutorUrl, this.capabilities, this.sessionCapabilites, this.clientInfo, this.environmentInfo, this.options);
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 ...');
33
36
  await automate.createDriver();
37
+ this.log.debug('Created driver ...');
34
38
  return await automate.screenshot(this.snapshotName, this.options);
35
39
  }
36
40
  }
@@ -23,7 +23,7 @@ export default class DesktopMetaData {
23
23
 
24
24
  // combination of browserName + browserVersion + osVersion + osName
25
25
  deviceName() {
26
- return this.browserName() + '_' + this.browserVersion() + '_' + this.osVersion() + '_' + this.osName();
26
+ return this.osName() + '_' + this.osVersion() + '_' + this.browserName() + '_' + this.browserVersion();
27
27
  }
28
28
  orientation() {
29
29
  return 'landscape';
@@ -40,7 +40,7 @@ export default class DesktopMetaData {
40
40
  }
41
41
  async screenResolution() {
42
42
  const data = await this.driver.executeScript({
43
- script: 'return [window.screen.width.toString(), window.screen.height.toString()];',
43
+ script: 'return [(window.screen.width * window.devicePixelRatio).toString(), (window.screen.height * window.devicePixelRatio).toString()];',
44
44
  args: []
45
45
  });
46
46
  const screenInfo = data.value;
@@ -40,6 +40,14 @@ export default class MobileMetaData {
40
40
  height
41
41
  };
42
42
  }
43
+ 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: []
47
+ });
48
+ const screenInfo = data.value;
49
+ return `${screenInfo[0]} x ${screenInfo[1]}`;
50
+ }
43
51
  async devicePixelRatio() {
44
52
  const devicePixelRatio = await this.driver.executeScript({
45
53
  script: 'return window.devicePixelRatio;',
@@ -7,8 +7,8 @@ const {
7
7
  TimeIt
8
8
  } = utils;
9
9
  export default class AutomateProvider extends GenericProvider {
10
- constructor(sessionId, commandExecutorUrl, capabilities, sessionCapabilites, clientInfo, environmentInfo, options) {
11
- super(sessionId, commandExecutorUrl, capabilities, sessionCapabilites, clientInfo, environmentInfo, options);
10
+ constructor(sessionId, commandExecutorUrl, capabilities, sessionCapabilites, clientInfo, environmentInfo, options, buildInfo) {
11
+ super(sessionId, commandExecutorUrl, capabilities, sessionCapabilites, clientInfo, environmentInfo, options, buildInfo);
12
12
  this._markedPercy = false;
13
13
  }
14
14
  static supports(commandExecutorUrl) {
@@ -22,8 +22,11 @@ export default class AutomateProvider extends GenericProvider {
22
22
  }) {
23
23
  let response = null;
24
24
  let error;
25
+ log.info('Preparing to capture screenshots on automate ...');
25
26
  try {
27
+ log.debug('Marking automate session as percy ...');
26
28
  let result = await this.percyScreenshotBegin(name);
29
+ log.debug('Fetching the debug url ...');
27
30
  this.setDebugUrl(result);
28
31
  response = await super.screenshot(name, {
29
32
  ignoreRegionXpaths,
@@ -45,8 +48,8 @@ export default class AutomateProvider extends GenericProvider {
45
48
  try {
46
49
  let result = await this.browserstackExecutor('percyScreenshot', {
47
50
  name,
48
- percyBuildId: process.env.PERCY_BUILD_ID,
49
- percyBuildUrl: process.env.PERCY_BUILD_URL,
51
+ percyBuildId: this.buildInfo.id,
52
+ percyBuildUrl: this.buildInfo.url,
50
53
  state: 'begin'
51
54
  });
52
55
  this._markedPercy = result.success;
@@ -69,16 +72,18 @@ export default class AutomateProvider extends GenericProvider {
69
72
  state: 'end'
70
73
  });
71
74
  } catch (e) {
72
- log.debug(`[${name}] Could not mark Automate session as percy`);
75
+ log.debug(`[${name}] Could not execute percyScreenshot command for Automate`);
76
+ log.error(e);
73
77
  }
74
78
  });
75
79
  }
76
- async getTiles(fullscreen) {
80
+ async getTiles(headerHeight, footerHeight, fullscreen) {
77
81
  if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
82
+ log.info('Starting actual screenshotting phase');
78
83
  const response = await TimeIt.run('percyScreenshot:screenshot', async () => {
79
84
  return await this.browserstackExecutor('percyScreenshot', {
80
85
  state: 'screenshot',
81
- percyBuildId: process.env.PERCY_BUILD_ID,
86
+ percyBuildId: this.buildInfo.id,
82
87
  screenshotType: 'singlepage',
83
88
  scaleFactor: await this.driver.executeScript({
84
89
  script: 'return window.devicePixelRatio;',
@@ -93,12 +98,13 @@ export default class AutomateProvider extends GenericProvider {
93
98
  }
94
99
  const tiles = [];
95
100
  const tileResponse = JSON.parse(responseValue.result);
101
+ log.debug('Tiles captured successfully');
96
102
  for (let tileData of tileResponse.sha) {
97
103
  tiles.push(new Tile({
98
104
  statusBarHeight: 0,
99
105
  navBarHeight: 0,
100
- headerHeight: 0,
101
- footerHeight: 0,
106
+ headerHeight,
107
+ footerHeight,
102
108
  fullscreen,
103
109
  sha: tileData.split('-')[0] // drop build id
104
110
  }));
@@ -2,12 +2,17 @@ import utils from '@percy/sdk-utils';
2
2
  import MetaDataResolver from '../metadata/metaDataResolver.js';
3
3
  import Tile from '../util/tile.js';
4
4
  import Driver from '../driver.js';
5
+ import Cache from '../util/cache.js';
6
+ const {
7
+ request
8
+ } = utils;
9
+ const DEVICES_CONFIG_URL = 'https://storage.googleapis.com/percy-utils/devices.json';
5
10
  const log = utils.logger('webdriver-utils:genericProvider');
6
11
  export default class GenericProvider {
7
12
  clientInfo = new Set();
8
13
  environmentInfo = new Set();
9
14
  options = {};
10
- constructor(sessionId, commandExecutorUrl, capabilities, sessionCapabilites, clientInfo, environmentInfo, options) {
15
+ constructor(sessionId, commandExecutorUrl, capabilities, sessionCapabilites, clientInfo, environmentInfo, options, buildInfo) {
11
16
  this.sessionId = sessionId;
12
17
  this.commandExecutorUrl = commandExecutorUrl;
13
18
  this.capabilities = capabilities;
@@ -15,13 +20,46 @@ export default class GenericProvider {
15
20
  this.addClientInfo(clientInfo);
16
21
  this.addEnvironmentInfo(environmentInfo);
17
22
  this.options = options;
23
+ this.buildInfo = buildInfo;
18
24
  this.driver = null;
19
25
  this.metaData = null;
20
26
  this.debugUrl = null;
27
+ this.header = 0;
28
+ this.footer = 0;
29
+ }
30
+ addDefaultOptions() {
31
+ this.options.freezeAnimation = this.options.freezeAnimation || false;
32
+ }
33
+ defaultPercyCSS() {
34
+ return `*, *::before, *::after {
35
+ -moz-transition: none !important;
36
+ transition: none !important;
37
+ -moz-animation: none !important;
38
+ animation: none !important;
39
+ animation-duration: 0 !important;
40
+ caret-color: transparent !important;
41
+ content-visibility: visible !important;
42
+ }
43
+ html{
44
+ scrollbar-width: auto !important;
45
+ }
46
+ svg {
47
+ shape-rendering: geometricPrecision !important;
48
+ }
49
+ scrollbar, scrollcorner, scrollbar thumb, scrollbar scrollbarbutton {
50
+ pointer-events: none !important;
51
+ -moz-appearance: none !important;
52
+ display: none !important;
53
+ }
54
+ video::-webkit-media-controls {
55
+ display: none !important;
56
+ }`;
21
57
  }
22
58
  async createDriver() {
23
59
  this.driver = new Driver(this.sessionId, this.commandExecutorUrl);
60
+ log.debug(`Passed capabilities -> ${JSON.stringify(this.capabilities)}`);
24
61
  const caps = await this.driver.getCapabilites();
62
+ log.debug(`Fetched capabilities -> ${JSON.stringify(caps)}`);
25
63
  this.metaData = await MetaDataResolver.resolve(this.driver, caps, this.capabilities);
26
64
  }
27
65
  static supports(_commandExecutorUrl) {
@@ -62,16 +100,19 @@ export default class GenericProvider {
62
100
  customIgnoreRegions = []
63
101
  }) {
64
102
  let fullscreen = false;
65
- const percyCSS = this.options.percyCSS || '';
103
+ this.addDefaultOptions();
104
+ const percyCSS = (this.defaultPercyCSS() + (this.options.percyCSS || '')).split('\n').join('');
105
+ log.debug(`Applying the percyCSS - ${this.options.percyCSS}`);
66
106
  await this.addPercyCSS(percyCSS);
107
+ log.debug('Fetching comparisong tag ...');
67
108
  const tag = await this.getTag();
68
- const tiles = await this.getTiles(fullscreen);
69
- const ignoreRegions = await this.findIgnoredRegions(ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions);
70
- await this.setDebugUrl();
71
- await this.removePercyCSS();
72
109
  log.debug(`${name} : Tag ${JSON.stringify(tag)}`);
110
+ const tiles = await this.getTiles(this.header, this.footer, fullscreen);
73
111
  log.debug(`${name} : Tiles ${JSON.stringify(tiles)}`);
112
+ const ignoreRegions = await this.findIgnoredRegions(ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions);
113
+ await this.setDebugUrl();
74
114
  log.debug(`${name} : Debug url ${this.debugUrl}`);
115
+ await this.removePercyCSS();
75
116
  return {
76
117
  name,
77
118
  tag,
@@ -90,31 +131,34 @@ export default class GenericProvider {
90
131
  // execute script and return dom content
91
132
  return 'dummyValue';
92
133
  }
93
- async getTiles(fullscreen) {
134
+ async getTiles(headerHeight, footerHeight, fullscreen) {
94
135
  if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
95
136
  const base64content = await this.driver.takeScreenshot();
137
+ log.debug('Tiles captured successfully');
96
138
  return {
97
139
  tiles: [new Tile({
98
140
  content: base64content,
99
- // TODO: Need to add method to fetch these attr
100
141
  statusBarHeight: 0,
101
142
  navBarHeight: 0,
102
- headerHeight: 0,
103
- footerHeight: 0,
143
+ headerHeight,
144
+ footerHeight,
104
145
  fullscreen
105
146
  })],
106
- // TODO: Add Generic support sha for contextual diff
147
+ // TODO: Add Generic support sha for contextual diff for non-automate
107
148
  domInfoSha: await this.getDomContent()
108
149
  };
109
150
  }
110
151
  async getTag() {
111
152
  if (!this.driver) throw new Error('Driver is null, please initialize driver with createDriver().');
112
- const {
153
+ let {
113
154
  width,
114
155
  height
115
156
  } = await this.metaData.windowSize();
116
157
  const resolution = await this.metaData.screenResolution();
117
158
  const orientation = this.metaData.orientation();
159
+ [this.header, this.footer] = await this.getHeaderFooter();
160
+ // for android window size only constitutes of browser viewport, hence adding nav / status / url bar heights
161
+ height = this.metaData.osName() === 'android' ? height + this.header + this.footer : height;
118
162
  return {
119
163
  name: this.metaData.deviceName(),
120
164
  osName: this.metaData.osName(),
@@ -128,7 +172,7 @@ export default class GenericProvider {
128
172
  };
129
173
  }
130
174
 
131
- // TODO: Add Debugging Url
175
+ // TODO: Add Debugging Url for non-automate
132
176
  async setDebugUrl() {
133
177
  this.debugUrl = 'https://localhost/v1';
134
178
  }
@@ -220,4 +264,13 @@ export default class GenericProvider {
220
264
  }
221
265
  return ignoredElementsArray;
222
266
  }
267
+ async getHeaderFooter() {
268
+ // passing 0 as key, since across different pages and tests, this config will remain same
269
+ const devicesConfig = await Cache.withCache(Cache.devicesConfig, 0, async () => {
270
+ return (await request(DEVICES_CONFIG_URL)).body;
271
+ });
272
+ let deviceKey = `${this.metaData.deviceName()}-${this.metaData.osVersion()}`;
273
+ let browserName = this.capabilities.browserName;
274
+ return devicesConfig[deviceKey] ? devicesConfig[deviceKey][browserName] ? [devicesConfig[deviceKey][browserName].header, devicesConfig[deviceKey][browserName].footer] : [0, 0] : [0, 0];
275
+ }
223
276
  }
@@ -1,9 +1,9 @@
1
1
  import GenericProvider from './genericProvider.js';
2
2
  import AutomateProvider from './automateProvider.js';
3
3
  export default class ProviderResolver {
4
- static resolve(sessionId, commandExecutorUrl, capabilities, sessionCapabilities, clientInfo, environmentInfo, options) {
4
+ static resolve(sessionId, commandExecutorUrl, capabilities, sessionCapabilities, clientInfo, environmentInfo, options, buildInfo) {
5
5
  // We can safely do [0] because GenericProvider is catch all
6
6
  const Klass = [AutomateProvider, GenericProvider].filter(x => x.supports(commandExecutorUrl))[0];
7
- return new Klass(sessionId, commandExecutorUrl, capabilities, sessionCapabilities, clientInfo, environmentInfo, options);
7
+ return new Klass(sessionId, commandExecutorUrl, capabilities, sessionCapabilities, clientInfo, environmentInfo, options, buildInfo);
8
8
  }
9
9
  }
@@ -9,6 +9,7 @@ export default class Cache {
9
9
  static caps = 'caps';
10
10
  static bstackSessionDetails = 'bstackSessionDetails';
11
11
  static systemBars = 'systemBars';
12
+ static devicesConfig = 'devicesConfig';
12
13
 
13
14
  // maintainance
14
15
  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-alpha.0",
3
+ "version": "1.27.0-beta.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,7 +9,7 @@
9
9
  },
10
10
  "publishConfig": {
11
11
  "access": "public",
12
- "tag": "alpha"
12
+ "tag": "beta"
13
13
  },
14
14
  "engines": {
15
15
  "node": ">=14"
@@ -29,8 +29,8 @@
29
29
  "test:coverage": "yarn test --coverage"
30
30
  },
31
31
  "dependencies": {
32
- "@percy/config": "1.27.0-alpha.0",
33
- "@percy/sdk-utils": "1.27.0-alpha.0"
32
+ "@percy/config": "1.27.0-beta.0",
33
+ "@percy/sdk-utils": "1.27.0-beta.0"
34
34
  },
35
- "gitHead": "16f2c87641d844c6af6c3e198f6aff1c08ee0ec1"
35
+ "gitHead": "2bc16314f51dddcc1cda459e7aa4b7b2db85f00a"
36
36
  }