From fc03d0cbab90134f1c6d58342afc51b30ac815f9 Mon Sep 17 00:00:00 2001 From: amit3200 Date: Sun, 22 Oct 2023 21:08:19 +0530 Subject: [PATCH] Fixing FindRegion + Refactor --- packages/webdriver-utils/src/driver.js | 26 +++---- packages/webdriver-utils/src/index.js | 1 + .../src/metadata/desktopMetaData.js | 2 +- .../src/providers/automateProvider.js | 4 - .../src/providers/genericProvider.js | 16 ++-- packages/webdriver-utils/test/driver.test.js | 14 ++++ .../test/metadata/desktopMetaData.test.js | 2 +- .../test/providers/automateProvider.test.js | 6 -- .../test/providers/genericProvider.test.js | 75 ++++++++++++++----- 9 files changed, 96 insertions(+), 50 deletions(-) diff --git a/packages/webdriver-utils/src/driver.js b/packages/webdriver-utils/src/driver.js index 950720173..6d4d68bbd 100644 --- a/packages/webdriver-utils/src/driver.js +++ b/packages/webdriver-utils/src/driver.js @@ -10,6 +10,16 @@ export default class Driver { this.passedCapabilities = passedCapabilities; } + requestPostOptions(command) { + return { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=utf-8' + }, + body: JSON.stringify(command) + }; + } + async getCapabilites() { return await Cache.withCache(Cache.caps, this.sessionId, async () => { try { @@ -43,13 +53,7 @@ export default class Driver { if (!command.script.includes('browserstack_executor')) { command.script = `/* percy_automate_script */ \n ${command.script}`; } - const options = { - method: 'POST', - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify(command) - }; + const options = this.requestPostOptions(command); const baseUrl = `${this.executorUrl}/session/${this.sessionId}/execute/sync`; const response = JSON.parse((await request(baseUrl, options)).body); return response; @@ -68,13 +72,7 @@ export default class Driver { } async findElement(using, value) { - const options = { - method: 'POST', - headers: { - 'Content-Type': 'application/json;charset=utf-8' - }, - body: JSON.stringify({ using, value }) - }; + const options = this.requestPostOptions({ using, value }); const baseUrl = `${this.executorUrl}/session/${this.sessionId}/element`; const response = JSON.parse((await request(baseUrl, options)).body); return response.value; diff --git a/packages/webdriver-utils/src/index.js b/packages/webdriver-utils/src/index.js index 7f20b2415..e977cf4ac 100644 --- a/packages/webdriver-utils/src/index.js +++ b/packages/webdriver-utils/src/index.js @@ -24,6 +24,7 @@ export default class WebdriverUtils { const comparisonData = await automate.screenshot(snapshotName, options); comparisonData.metadata.cliScreenshotStartTime = startTime; comparisonData.metadata.cliScreenshotEndTime = Date.now(); + log.debug(`[${snapshotName}] : Comparison Data: ${JSON.stringify(comparisonData)}`); return comparisonData; } catch (e) { log.error(`[${snapshotName}] : Error - ${e.message}`); diff --git a/packages/webdriver-utils/src/metadata/desktopMetaData.js b/packages/webdriver-utils/src/metadata/desktopMetaData.js index ef0fd1653..67df56131 100644 --- a/packages/webdriver-utils/src/metadata/desktopMetaData.js +++ b/packages/webdriver-utils/src/metadata/desktopMetaData.js @@ -49,7 +49,7 @@ export default class DesktopMetaData { 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 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]}`; }); diff --git a/packages/webdriver-utils/src/providers/automateProvider.js b/packages/webdriver-utils/src/providers/automateProvider.js index 98f6de64d..5c3b40fd3 100644 --- a/packages/webdriver-utils/src/providers/automateProvider.js +++ b/packages/webdriver-utils/src/providers/automateProvider.js @@ -187,10 +187,6 @@ export default class AutomateProvider extends GenericProvider { 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, diff --git a/packages/webdriver-utils/src/providers/genericProvider.js b/packages/webdriver-utils/src/providers/genericProvider.js index bacf51903..f79748740 100644 --- a/packages/webdriver-utils/src/providers/genericProvider.js +++ b/packages/webdriver-utils/src/providers/genericProvider.js @@ -36,6 +36,8 @@ export default class GenericProvider { this.debugUrl = null; this.header = 0; this.footer = 0; + this.pageYShiftFactor = 0; + this.pageXShiftFactor = 0; } addDefaultOptions() { @@ -89,6 +91,10 @@ export default class GenericProvider { const tiles = await this.getTiles(this.header, this.footer, fullscreen); log.debug(`[${name}] : Tiles ${JSON.stringify(tiles)}`); + const scrollFactors = await this.driver.executeScript({ script: 'return [parseInt(window.scrollX * window.devicePixelRatio), parseInt(window.scrollY * window.devicePixelRatio)];', args: [] }); + this.pageYShiftFactor = tag.osName === 'iOS' ? tiles.tiles[0].statusBarHeight : (tiles.tiles[0].statusBarHeight - scrollFactors.value[1]); + this.pageXShiftFactor = tag.osName === 'iOS' ? 0 : (-scrollFactors.value[0]); + const ignoreRegions = await this.findRegions( ignoreRegionXpaths, ignoreRegionSelectors, ignoreRegionElements, customIgnoreRegions ); @@ -192,15 +198,15 @@ export default class GenericProvider { } async getRegionObject(selector, elementId) { - const scaleFactor = parseInt(await this.metaData.devicePixelRatio()); + const scaleFactor = await this.metaData.devicePixelRatio(); const rect = await this.driver.rect(elementId); const location = { x: rect.x, y: rect.y }; const size = { height: rect.height, width: rect.width }; const coOrdinates = { - top: Math.floor(location.y * scaleFactor), - bottom: Math.ceil((location.y + size.height) * scaleFactor), - left: Math.floor(location.x * scaleFactor), - right: Math.ceil((location.x + size.width) * scaleFactor) + top: Math.floor(location.y * scaleFactor) + this.pageYShiftFactor, + bottom: Math.ceil((location.y + size.height) * scaleFactor) + this.pageYShiftFactor, + left: Math.floor(location.x * scaleFactor) + this.pageXShiftFactor, + right: Math.ceil((location.x + size.width) * scaleFactor) + this.pageXShiftFactor }; const jsonObject = { diff --git a/packages/webdriver-utils/test/driver.test.js b/packages/webdriver-utils/test/driver.test.js index 6f513e2b6..fa8310c3c 100644 --- a/packages/webdriver-utils/test/driver.test.js +++ b/packages/webdriver-utils/test/driver.test.js @@ -161,4 +161,18 @@ describe('Driver', () => { expect(res).toEqual('mockVal'); }); }); + + describe('requestPostOptions', () => { + const command = { simple: 'test' }; + const expectedResponse = { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=utf-8' + }, + body: JSON.stringify(command) + }; + it('returns post options', () => { + expect(driver.requestPostOptions(command)).toEqual(expectedResponse); + }); + }); }); diff --git a/packages/webdriver-utils/test/metadata/desktopMetaData.test.js b/packages/webdriver-utils/test/metadata/desktopMetaData.test.js index 9bec6dc39..4088405c4 100644 --- a/packages/webdriver-utils/test/metadata/desktopMetaData.test.js +++ b/packages/webdriver-utils/test/metadata/desktopMetaData.test.js @@ -108,7 +108,7 @@ describe('DesktopMetaData', () => { screenInfo = await desktopMetaData.screenResolution(); expect(screenInfo).toEqual('1980 x 1080'); expect(executeScriptSpy) - .toHaveBeenCalledWith({ script: 'return [(window.screen.width * window.devicePixelRatio).toString(), (window.screen.height * window.devicePixelRatio).toString()];', args: [] }); + .toHaveBeenCalledWith({ script: 'return [parseInt(window.screen.width * window.devicePixelRatio).toString(), parseInt(window.screen.height * window.devicePixelRatio).toString()];', args: [] }); }); }); diff --git a/packages/webdriver-utils/test/providers/automateProvider.test.js b/packages/webdriver-utils/test/providers/automateProvider.test.js index 5dc205913..6f0d0fca7 100644 --- a/packages/webdriver-utils/test/providers/automateProvider.test.js +++ b/packages/webdriver-utils/test/providers/automateProvider.test.js @@ -201,7 +201,6 @@ 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') @@ -244,7 +243,6 @@ describe('AutomateProvider', () => { let windowSizeSpy; let orientationSpy; let resolutionSpy; - let getHeaderFooterSpy; let percyBuildInfo = { id: '123', url: 'https://percy.io/abc/123' @@ -256,7 +254,6 @@ describe('AutomateProvider', () => { percyScreenshotBeginSpy = spyOn(AutomateProvider.prototype, 'percyScreenshotBegin').and.returnValue({ value: '{"buildHash":"12e3","sessionHash":"abc1d","capabilities":{"browserName":"chrome","browserVersion":"113.0","os":"win11","os_version":"11","deviceOrientation":false,"resolution":["1920","1080"]},"success":true,"deviceName":"x.x.x.x"}' }); spyOn(Driver.prototype, 'getCapabilites'); - getHeaderFooterSpy = spyOn(GenericProvider.prototype, 'getHeaderFooter').and.returnValue(Promise.resolve([0, 0])); windowSizeSpy = spyOn(DesktopMetaData.prototype, 'windowSize') .and.returnValue(Promise.resolve({ width: 1000, height: 1000 })); resolutionSpy = spyOn(DesktopMetaData.prototype, 'screenResolution') @@ -275,7 +272,6 @@ describe('AutomateProvider', () => { expect(windowSizeSpy).toHaveBeenCalledTimes(1); expect(resolutionSpy).toHaveBeenCalledTimes(1); expect(orientationSpy).toHaveBeenCalledTimes(1); - expect(getHeaderFooterSpy).toHaveBeenCalledTimes(1); expect(res).toEqual({ name: 'Windows_11_chrome_113', osName: 'Windows', @@ -296,7 +292,6 @@ describe('AutomateProvider', () => { percyScreenshotBeginSpy = spyOn(AutomateProvider.prototype, 'percyScreenshotBegin').and.returnValue({ value: '{"buildHash":"12e3","sessionHash":"abc1d","capabilities":{"browserName":"chrome_android","browserVersion":"chrome_android","os":"android","os_version":"11","deviceOrientation":"portrait","resolution":["1920","1080"]},"success":true,"deviceName":"Samsung Galaxy S21"}' }); spyOn(Driver.prototype, 'getCapabilites'); - getHeaderFooterSpy = spyOn(GenericProvider.prototype, 'getHeaderFooter').and.returnValue(Promise.resolve([0, 0])); windowSizeSpy = spyOn(MobileMetaData.prototype, 'windowSize') .and.returnValue(Promise.resolve({ width: 1000, height: 1000 })); resolutionSpy = spyOn(MobileMetaData.prototype, 'screenResolution') @@ -315,7 +310,6 @@ describe('AutomateProvider', () => { expect(windowSizeSpy).toHaveBeenCalledTimes(1); expect(resolutionSpy).toHaveBeenCalledTimes(1); expect(orientationSpy).toHaveBeenCalledTimes(1); - expect(getHeaderFooterSpy).toHaveBeenCalledTimes(1); expect(res).toEqual({ name: 'Samsung Galaxy S21', osName: 'Android', diff --git a/packages/webdriver-utils/test/providers/genericProvider.test.js b/packages/webdriver-utils/test/providers/genericProvider.test.js index ccecda797..598e25630 100644 --- a/packages/webdriver-utils/test/providers/genericProvider.test.js +++ b/packages/webdriver-utils/test/providers/genericProvider.test.js @@ -140,10 +140,13 @@ describe('GenericProvider', () => { let getTilesSpy; beforeEach(() => { + const scrollFactors = { value: [0, 10] }; getTagSpy = spyOn(GenericProvider.prototype, 'getTag').and.returnValue(Promise.resolve('mock-tag')); getTilesSpy = spyOn(GenericProvider.prototype, 'getTiles').and.returnValue(Promise.resolve({ tiles: 'mock-tile', domInfoSha: 'mock-dom-sha' })); spyOn(DesktopMetaData.prototype, 'windowSize') .and.returnValue(Promise.resolve({ width: 1920, height: 1080 })); + spyOn(Driver.prototype, 'executeScript') + .and.returnValue(scrollFactors); }); it('calls correct funcs', async () => { @@ -184,28 +187,62 @@ describe('GenericProvider', () => { describe('getRegionObject', () => { let provider; let mockLocation = { x: 10, y: 20, width: 100, height: 200 }; - beforeEach(() => { - // mock metadata - provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); - spyOn(DesktopMetaData.prototype, 'devicePixelRatio') - .and.returnValue(1); - spyOn(Driver.prototype, 'rect').and.returnValue(Promise.resolve(mockLocation)); + + describe('When on Tile 0', () => { + beforeEach(() => { + // mock metadata + provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); + spyOn(DesktopMetaData.prototype, 'devicePixelRatio') + .and.returnValue(1); + spyOn(Driver.prototype, 'rect').and.returnValue(Promise.resolve(mockLocation)); + }); + + it('should return a JSON object with the correct selector and coordinates for tile 0', async () => { + await provider.createDriver(); + + // Call function with mock data + const selector = 'mock-selector'; + const result = await provider.getRegionObject(selector, 'mockElementId'); + + // Assert expected result + expect(result.selector).toEqual(selector); + expect(result.coOrdinates).toEqual({ + top: mockLocation.y, + bottom: mockLocation.y + mockLocation.height, + left: mockLocation.x, + right: mockLocation.x + mockLocation.width + }); + }); }); - it('should return a JSON object with the correct selector and coordinates', async () => { - await provider.createDriver(); + describe('When on Tile 1', () => { + beforeEach(() => { + // mock metadata + provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); + spyOn(DesktopMetaData.prototype, 'devicePixelRatio') + .and.returnValue(1); + spyOn(Driver.prototype, 'rect').and.returnValue(Promise.resolve(mockLocation)); + provider.pageYShiftFactor = -10; + }); - // Call function with mock data - const selector = 'mock-selector'; - const result = await provider.getRegionObject(selector, 'mockElementId'); - - // Assert expected result - expect(result.selector).toEqual(selector); - expect(result.coOrdinates).toEqual({ - top: mockLocation.y, - bottom: mockLocation.y + mockLocation.height, - left: mockLocation.x, - right: mockLocation.x + mockLocation.width + afterEach(() => { + provider.pageYShiftFactor = 0; + }); + it('should return a JSON object with the correct selector and coordinates', async () => { + await provider.createDriver(); + + // Call function with mock data + const selector = 'mock-selector'; + const result = await provider.getRegionObject(selector, 'mockElementId'); + + // Assert expected result + expect(result.selector).toEqual(selector); + expect(result.coOrdinates).toEqual({ + top: mockLocation.y + provider.pageYShiftFactor, + bottom: mockLocation.y + mockLocation.height + provider.pageYShiftFactor, + left: mockLocation.x, + right: mockLocation.x + mockLocation.width + }); }); }); });