diff --git a/packages/webdriver-utils/src/providers/genericProvider.js b/packages/webdriver-utils/src/providers/genericProvider.js index 2adbad0e8..2b972b7c1 100644 --- a/packages/webdriver-utils/src/providers/genericProvider.js +++ b/packages/webdriver-utils/src/providers/genericProvider.js @@ -205,7 +205,6 @@ export default class GenericProvider { this.pageYShiftFactor = this.currentOperatingSystem === 'iOS' ? this.statusBarHeight : (this.statusBarHeight - scrollFactors.value[1]); this.pageXShiftFactor = this.currentOperatingSystem === 'iOS' ? 0 : (-scrollFactors.value[0]); if (this.currentOperatingSystem === 'iOS') { - console.log(scrollFactors, this.initialScrollFactor); if (scrollFactors.value[0] !== this.initialScrollFactor.value[0] || scrollFactors.value[1] !== this.initialScrollFactor.value[1]) { this.pageXShiftFactor = (-1 * this.removeElementShiftFactor); this.pageYShiftFactor = (-1 * this.removeElementShiftFactor); diff --git a/packages/webdriver-utils/test/driver.test.js b/packages/webdriver-utils/test/driver.test.js index 74dc58b79..3b90da83c 100644 --- a/packages/webdriver-utils/test/driver.test.js +++ b/packages/webdriver-utils/test/driver.test.js @@ -151,6 +151,66 @@ describe('Driver', () => { }); }); + describe('findElementBoundingBox', () => { + let xpathFindElementSpy; + let cssSelectorFindElementSpy; + beforeEach(() => { + xpathFindElementSpy = spyOn(Driver.prototype, 'findElementXpath').and.returnValue(Promise.resolve({ x: 0, y: 10, height: 100, width: 100 })); + cssSelectorFindElementSpy = spyOn(Driver.prototype, 'findElementSelector').and.returnValue(Promise.resolve({ x: 0, y: 10, height: 100, width: 100 })); + }); + describe('when xpath is passed', () => { + it('calls the required function', async () => { + const res = await driver.findElementBoundingBox('xpath', '/xpath1'); + expect(cssSelectorFindElementSpy).toHaveBeenCalledTimes(0); + expect(xpathFindElementSpy).toHaveBeenCalledTimes(1); + expect(xpathFindElementSpy).toHaveBeenCalledWith('/xpath1'); + expect(res).toEqual({ x: 0, y: 10, height: 100, width: 100 }); + }); + }); + + describe('when selector is passed', () => { + it('calls the required function', async () => { + const res = await driver.findElementBoundingBox('css selector', '#id1'); + expect(xpathFindElementSpy).toHaveBeenCalledTimes(0); + expect(cssSelectorFindElementSpy).toHaveBeenCalledTimes(1); + expect(cssSelectorFindElementSpy).toHaveBeenCalledWith('#id1'); + expect(res).toEqual({ x: 0, y: 10, height: 100, width: 100 }); + }); + }); + + describe('when invalid is passed', () => { + it('calls nothing', async () => { + await driver.findElementBoundingBox('abc', '#id1'); + expect(xpathFindElementSpy).toHaveBeenCalledTimes(0); + expect(cssSelectorFindElementSpy).toHaveBeenCalledTimes(0); + }); + }); + }); + + describe('findElementXpath', () => { + let executeScriptSpy; + beforeEach(() => { + executeScriptSpy = spyOn(Driver.prototype, 'executeScript').and.returnValue(Promise.resolve({ value: { x: 0, y: 10, height: 100, width: 100 } })); + }); + it('calls requests', async () => { + const res = await driver.findElementXpath('/xpath1'); + expect(executeScriptSpy).toHaveBeenCalledWith({ script: "return document.evaluate('/xpath1', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.getBoundingClientRect();", args: [] }); + expect(res).toEqual({ x: 0, y: 10, height: 100, width: 100 }); + }); + }); + + describe('findElementSelector', () => { + let executeScriptSpy; + beforeEach(() => { + executeScriptSpy = spyOn(Driver.prototype, 'executeScript').and.returnValue(Promise.resolve({ value: { x: 0, y: 10, height: 100, width: 100 } })); + }); + it('calls requests', async () => { + const res = await driver.findElementSelector('#id1'); + expect(executeScriptSpy).toHaveBeenCalledWith({ script: "return document.querySelector('#id1').getBoundingClientRect();", args: [] }); + expect(res).toEqual({ x: 0, y: 10, height: 100, width: 100 }); + }); + }); + describe('rect', () => { it('calls requests', async () => { const elementId = 'element'; diff --git a/packages/webdriver-utils/test/providers/genericProvider.test.js b/packages/webdriver-utils/test/providers/genericProvider.test.js index f3ff1af60..e2cdb8379 100644 --- a/packages/webdriver-utils/test/providers/genericProvider.test.js +++ b/packages/webdriver-utils/test/providers/genericProvider.test.js @@ -281,15 +281,36 @@ describe('GenericProvider', () => { provider.statusBarHeight = 0; }); - it('should update pageYShiftFactor for iOS when location.y is 0', async () => { - await provider.updatePageShiftFactor({ y: 0 }); - expect(provider.pageYShiftFactor).toBe(-10); + describe('when element is visible in viewport', () => { + beforeEach(() => { + provider.initialScrollFactor = { value: [0, 10] }; + }); + it('should update pageYShiftFactor for iOS when location.y is 0', async () => { + await provider.updatePageShiftFactor({ y: 0 }); + expect(provider.pageYShiftFactor).toBe(-10); + }); + + it('should not update pageYShiftFactor for iOS when location.y is not 0', async () => { + // Location.y is not 0 + await provider.updatePageShiftFactor({ y: 5 }); + expect(provider.pageYShiftFactor).toBe(0); + }); }); - it('should not update pageYShiftFactor for iOS when location.y is not 0', async () => { - // Location.y is not 0 - await provider.updatePageShiftFactor({ y: 5 }); - expect(provider.pageYShiftFactor).toBe(0); + describe('when element is not visible in viewport and iOS scrolls automatically', () => { + beforeEach(() => { + provider.initialScrollFactor = { value: [0, 30] }; + }); + it('should update pageYShiftFactor to negative value even if location.y is 0', async () => { + await provider.updatePageShiftFactor({ y: 0 }); + expect(provider.pageYShiftFactor).toBe(-50000); + }); + + it('should update pageYShiftFactor to negative value even if location.y is not 0', async () => { + // Location.y is not 0 + await provider.updatePageShiftFactor({ y: 5 }); + expect(provider.pageYShiftFactor).toBe(-50000); + }); }); }); @@ -384,34 +405,88 @@ describe('GenericProvider', () => { }); }); + describe('getRegionObjectFromBoundingBox', () => { + let provider; + let mockLocation = { x: 10, y: 20, width: 100, height: 200 }; + beforeEach(async () => { + // mock metadata + provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); + await provider.createDriver(); + spyOn(DesktopMetaData.prototype, 'devicePixelRatio') + .and.returnValue(1); + provider.statusBarHeight = 0; + }); + + describe('When not an iOS', () => { + it('should return a JSON object with the correct selector and coordinates', async () => { + // Call function with mock data + const selector = 'mock-selector'; + const result = await provider.getRegionObjectFromBoundingBox(selector, mockLocation); + + // 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 + }); + }); + }); + + describe('When iOS', () => { + beforeEach(() => { + provider.currentOperatingSystem = 'iOS'; + provider.statusBarHeight = 132; + }); + it('should return a JSON object with the correct selector and coordinates with added statusBarHeight', async () => { + await provider.createDriver(); + + // Call function with mock data + const selector = 'mock-selector'; + const result = await provider.getRegionObjectFromBoundingBox(selector, mockLocation); + + // Assert expected result + expect(result.selector).toEqual(selector); + expect(result.coOrdinates).toEqual({ + top: mockLocation.y + provider.statusBarHeight, + bottom: mockLocation.y + mockLocation.height + provider.statusBarHeight, + left: mockLocation.x, + right: mockLocation.x + mockLocation.width + }); + }); + }); + }); + describe('getSeleniumRegionsByXpaths', () => { let getRegionObjectSpy; let provider; + let xpathResponse = { top: 0, bottom: 500, right: 0, left: 300 }; beforeEach(async () => { provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); await provider.createDriver(); - getRegionObjectSpy = spyOn(GenericProvider.prototype, 'getRegionObject').and.returnValue({}); + getRegionObjectSpy = spyOn(GenericProvider.prototype, 'getRegionObjectFromBoundingBox').and.returnValue(xpathResponse); }); it('should add regions for each xpath', async () => { - spyOn(Driver.prototype, 'findElement').and.returnValue(Promise.resolve({ ELEMENT: 'mock_id' })); + spyOn(Driver.prototype, 'findElementBoundingBox').and.returnValue(Promise.resolve({ x: 0, y: 100, height: 500, width: 300 })); const xpaths = ['/xpath/1', '/xpath/2', '/xpath/3']; const elementsArray = await provider.getSeleniumRegionsBy('xpath', xpaths); - expect(provider.driver.findElement).toHaveBeenCalledTimes(3); + expect(provider.driver.findElementBoundingBox).toHaveBeenCalledTimes(3); expect(getRegionObjectSpy).toHaveBeenCalledTimes(3); - expect(elementsArray).toEqual([{}, {}, {}]); + expect(elementsArray).toEqual([xpathResponse, xpathResponse, xpathResponse]); }); it('should ignore xpath when element is not found', async () => { - spyOn(Driver.prototype, 'findElement').and.rejectWith(new Error('Element not found')); + spyOn(Driver.prototype, 'findElementBoundingBox').and.rejectWith(new Error('Element not found')); const xpaths = ['/xpath/1', '/xpath/2', '/xpath/3']; const elementsArray = await provider.getSeleniumRegionsBy('xpath', xpaths); - expect(provider.driver.findElement).toHaveBeenCalledTimes(3); + expect(provider.driver.findElementBoundingBox).toHaveBeenCalledTimes(3); expect(getRegionObjectSpy).not.toHaveBeenCalled(); expect(elementsArray).toEqual([]); }); @@ -424,48 +499,118 @@ describe('GenericProvider', () => { beforeEach(async () => { provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); await provider.createDriver(); - getRegionObjectSpy = spyOn(GenericProvider.prototype, 'getRegionObject').and.returnValue({}); + getRegionObjectSpy = spyOn(GenericProvider.prototype, 'getRegionObjectFromBoundingBox').and.returnValue({}); }); it('should add regions for each id', async () => { - spyOn(Driver.prototype, 'findElement').and.returnValue(Promise.resolve({ ELEMENT: 'mock_id' })); + spyOn(Driver.prototype, 'findElementBoundingBox').and.returnValue(Promise.resolve({ value: { x: 0, y: 100, height: 500, width: 300 } })); const ids = ['#id1', '#id2', '#id3']; const elementsArray = await provider.getSeleniumRegionsBy('css selector', ids); - expect(provider.driver.findElement).toHaveBeenCalledTimes(3); + expect(provider.driver.findElementBoundingBox).toHaveBeenCalledTimes(3); expect(getRegionObjectSpy).toHaveBeenCalledTimes(3); expect(elementsArray).toEqual([{}, {}, {}]); }); it('should ignore id when element is not found', async () => { - spyOn(Driver.prototype, 'findElement').and.rejectWith(new Error('Element not found')); + spyOn(Driver.prototype, 'findElementBoundingBox').and.rejectWith(new Error('Element not found')); const ids = ['#id1', '#id2', '#id3']; const elementsArray = await provider.getSeleniumRegionsBy('css selector', ids); - expect(provider.driver.findElement).toHaveBeenCalledTimes(3); + expect(provider.driver.findElementBoundingBox).toHaveBeenCalledTimes(3); expect(getRegionObjectSpy).not.toHaveBeenCalled(); expect(elementsArray).toEqual([]); }); }); + describe('getInitialPosition', () => { + let provider; + beforeEach(async () => { + provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); + await provider.createDriver(); + }); + describe('when not IOS', () => { + it('should not get the initial scroll position', async () => { + await provider.getInitialPosition(); + expect(provider.initialScrollFactor).toEqual({ value: [0, 0] }); + }); + }); + + describe('when IOS', () => { + let executeScriptSpy; + beforeEach(() => { + provider.currentOperatingSystem = 'iOS'; + executeScriptSpy = spyOn(Driver.prototype, 'executeScript'); + }); + + afterEach(() => { + provider.currentOperatingSystem = null; + }); + it('should get the initial scroll position', async () => { + spyOn(Driver.prototype, 'executeScript').and.returnValue({ value: [0, 200] }); + await provider.getInitialPosition(); + expect(executeScriptSpy).toHaveBeenCalledWith({ script: 'return [parseInt(window.scrollX * window.devicePixelRatio), parseInt(window.scrollY * window.devicePixelRatio)];', args: [] }); + expect(provider.initialScrollFactor).toEqual({ value: [0, 200] }); + }); + }); + }); + + describe('scrollToInitialPosition', () => { + let provider; + beforeEach(async () => { + provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); + await provider.createDriver(); + }); + describe('when not IOS', () => { + it('should not scroll to position', async () => { + await provider.scrollToInitialPosition(0, 50); + expect(spyOn(Driver.prototype, 'executeScript')).toHaveBeenCalledTimes(0); + }); + }); + + describe('when IOS', () => { + let executeScriptSpy; + beforeEach(() => { + provider.currentOperatingSystem = 'iOS'; + provider.initialScrollFactor = { value: [0, 50] }; + executeScriptSpy = spyOn(Driver.prototype, 'executeScript'); + }); + + afterEach(() => { + provider.currentOperatingSystem = null; + }); + it('should scroll to position', async () => { + await provider.scrollToInitialPosition(0, 50); + expect(executeScriptSpy).toHaveBeenCalledTimes(1); + expect(executeScriptSpy).toHaveBeenCalledWith({ script: 'window.scrollTo(0, 50)', args: [] }); + }); + }); + }); + describe('getSeleniumRegionsByElement', () => { let getRegionObjectSpy; + let getInitialPositionSpy; + let scrollToInitialPositionSpy; let provider; beforeEach(async () => { provider = new GenericProvider('123', 'http:executorUrl', { platform: 'win' }, {}); await provider.createDriver(); getRegionObjectSpy = spyOn(GenericProvider.prototype, 'getRegionObject').and.returnValue({}); + getInitialPositionSpy = spyOn(GenericProvider.prototype, 'getInitialPosition'); + scrollToInitialPositionSpy = spyOn(GenericProvider.prototype, 'scrollToInitialPosition'); }); it('should add regions for each element', async () => { const elements = ['mockElement_1', 'mockElement_2', 'mockElement_3']; const elementsArray = await provider.getSeleniumRegionsByElement(elements); - + expect(getInitialPositionSpy).toHaveBeenCalledTimes(1); expect(getRegionObjectSpy).toHaveBeenCalledTimes(3); + expect(scrollToInitialPositionSpy).toHaveBeenCalledTimes(1); + expect(scrollToInitialPositionSpy).toHaveBeenCalledWith(provider.initialScrollFactor.value[0], provider.initialScrollFactor.value[1]); expect(elementsArray).toEqual([{}, {}, {}]); });