├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── example └── EyesWebExample.js ├── index.js ├── package-lock.json ├── package.json ├── protractor.conf.js ├── src ├── CssTranslatePositionProvider.js ├── ElementFinderWrappers.js ├── ElementPositionProvider.js ├── Eyes.js ├── EyesRegionProvider.js ├── EyesRemoteWebElement.js ├── EyesSeleniumUtils.js ├── EyesTargetLocator.js ├── EyesWebDriver.js ├── Frame.js ├── FrameChain.js ├── ScrollPositionProvider.js ├── Target.js └── capture │ ├── EyesWebDriverScreenshot.js │ ├── FirefoxScreenshotImageProvider.js │ ├── ImageProviderFactory.js │ ├── SafariScreenshotImageProvider.js │ └── TakesScreenshotImageProvider.js ├── test ├── Eyes.spec.js ├── IOSTest.js ├── TestUtils.js ├── appium │ ├── android-native-sause-test.js │ ├── ios-native-sause-test.js │ └── ios-simple-sause-test.js ├── protractor │ ├── check-interface-test.js │ └── simple-protractor-test.js └── selenium │ ├── check-ignore-region-test.js │ ├── check-interface-test.js │ ├── hide-scrollbars-selenium-test.js │ ├── scaling-methods-test.js │ └── simple-selenium-test.js └── typings ├── eyes.selenium-tests.ts ├── index.d.ts ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # JetBrains 6 | .idea 7 | 8 | # VS Code 9 | .vscode 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | RunResults 16 | 17 | # Coverage directory used by tools like istanbul, JSCover, etc. 18 | lib-cov 19 | coverage 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | compiled/ 27 | .sass-cache 28 | 29 | # Dependency directory 30 | # Deployed apps should consider commenting this line out: 31 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 32 | node_modules 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | dist: trusty 3 | sudo: required 4 | addons: 5 | apt: 6 | sources: 7 | - google-chrome 8 | packages: 9 | - google-chrome-stable 10 | # latest version of Firefox already installed by default 11 | node_js: 12 | - 'node' 13 | - '8' 14 | - '6' 15 | env: 16 | matrix: 17 | - SELENIUM_VERSION=2 PROTRACTOR_VERSION=4 18 | - SELENIUM_VERSION=3 PROTRACTOR_VERSION=5 19 | matrix: 20 | exclude: 21 | - node_js: '8' 22 | env: SELENIUM_VERSION=2 PROTRACTOR_VERSION=4 23 | - node_js: 'node' 24 | env: SELENIUM_VERSION=2 PROTRACTOR_VERSION=4 25 | before_install: 26 | - rm package-lock.json 27 | install: 28 | - npm install selenium-webdriver@^$SELENIUM_VERSION protractor@^$PROTRACTOR_VERSION 29 | - npm install 30 | - npm install webdriver-manager 31 | - webdriver-manager update 32 | before_script: 33 | - export DISPLAY=:99.0 34 | - sh -e /etc/init.d/xvfb start 35 | - webdriver-manager start & 36 | - sleep 10 # give webdriver some time to start 37 | script: 38 | - node index.js 39 | - npm run test-selenium 40 | - npm run test-protractor 41 | after_script: 42 | - sh -e /etc/init.d/xvfb stop 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | - - 2 | SDK LICENSE AGREEMENT 3 | IMPORTANT - PLEASE READ CAREFULLY THE TERMS OF THIS LICENSE AGREEMENT (“AGREEMENT”). BY INSTALLING, ACCESSING AND/OR USING THE SOFTWARE (AS DEFINED BELOW), YOU EXPRESSLY ACKNOWLEDGE AND AGREE THAT YOU, OR THE COMPANY YOU REPRESENT, (“YOU” OR “LICENSEE”) ARE ENTERING INTO A LEGAL AGREEMENT WITH APPLITOOLS LTD. AND ITS AFFILIATES (“APPLITOOLS”), AND HAVE UNDERSTOOD AND AGREE TO COMPLY WITH, AND BE LEGALLY BOUND BY, THE TERMS AND CONDITIONS OF THIS AGREEMENT. YOU HEREBY WAIVE ANY RIGHTS OR REQUIREMENTS UNDER ANY LAWS OR REGULATIONS IN ANY JURISDICTION WHICH REQUIRE AN ORIGINAL (NON-ELECTRONIC) SIGNATURE OR DELIVERY OR RETENTION OF NON-ELECTRONIC RECORDS, TO THE EXTENT PERMITTED UNDER APPLICABLE LAW. 4 | THE SOFTWARE MAY BE USED SOLELY FOR YOUR PERSONAL, NON-COMMERCIAL PURPOSES. FOR COMMERCIAL PURPOSES PLEASE CONTACT THE REGIONAL APPLITOOLS BUSINESS REPRESENTATIVE. 5 | 1. Definitions. For purposes of this Agreement, the following capitalized terms shall have the following meaning: 6 | 1. “Documentation” means the user’s guides and technical manuals delivered by Applitools to Licensee. 7 | 2. “Feedback” means suggestions, comments or feedback (whether orally or in writing) with respect to the Software. 8 | 3. “Intellectual Property Rights” means all intangible legal rights, titles and interests evidenced by or embodied in all: (i) inventions (regardless of patentability and whether or not reduced to practice), improvements thereto, patents, patent applications, patent disclosures, together with all reissuances, continuations, continuations in part, revisions, extensions and reexaminations thereof; (ii) trademarks, service marks, trade dress, logos, trade names, corporate names, together with translations, adaptations, derivations and combinations thereof, including goodwill associated therewith, and applications, registrations, and renewals in connection therewith; (iii) any work of authorship, regardless of copyrightable, copyrightable works, copyrights (including moral rights), and applications, registrations and renewals in connection therewith; (iv) mask works and applications, registrations and renewals in connection therewith; (v) trade secrets and Confidential Information; and (vi) other proprietary rights and any other similar rights, in each case on a worldwide basis, and copies and tangible embodiments thereof, in whatever form or medium. 9 | 4. “License” means the right to use the Software pursuant to Section ‎2.1 to this Agreement. 10 | 5. “Software” means Applitools’ software development kit software in object or source code version, Documentation and any updates and upgrade thereto (to the extent delivered). 11 | 1. License 12 | 1. Grant of License. Subject to the terms and conditions of this Agreement, Applitools grants You, during the Term, a personal, non-exclusive, non-sublicensable, non-transferable, revocable license to: (i) use the Software solely for Your own personal (non-commercial) use and (ii) use and display Applitools’ Marks solely for the purpose of publicizing or advertising that You are using the Software. 13 | 2. Documentation. Applitools may make available Documentation to Licensee for Licensee to use solely in connection with Licensee’s use of the Software during the term of this Agreement. Licensee may print or copy the Documentation as needed for its own purposes provided that all copyright notices are included therein. The Documentation shall be considered the Confidential Information of Applitools. 14 | 1. Reservation of Rights; Use Restrictions. Other than the rights explicitly granted in this Agreement, Licensee shall have no other rights, express or implied, in the Software. Without limiting the generality of the foregoing, Licensee agrees and undertakes not to: (i) allow any third party to use the Software in any manner, including but not limited to, sell, lease, sublicense or distribute the Software, or any part thereof; (ii) modify, revise, or alter the Software or reverse engineer, decompile, disassemble or otherwise reduce to human-perceivable form the Software’s source code; (iii) copy or allow copies of the Software to be made; (iv) remove, alter or obscure any proprietary notice or identification, including copyright, trademark, patent or other notices, contained in or displayed on or via the Software; (v) use the Software to violate any applicable laws, rules or regulations, or for any unlawful, harmful, irresponsible, or inappropriate purpose, or in any manner that breaches this Agreement, and/or (vi) represent that it possesses any proprietary interest in the Software. 15 | 1. Third Party Software. Licensee acknowledges and agrees that any third party software (“Third Party Software”) that provided with the Software is provided under the terms of the license attached/linked thereto or, if no such license is attached, such Third Party Software is provided for free and on “AS IS” basis. Applitools is not liable for any losses or damages which may occur resulting from the use of any Third Party Software. Applitools does not possess any proprietary interest in such Third Party Software. 16 | 1. Open Source Licenses. The Software includes certain open source code software and materials (as shall be listed in the documentation of the Software) (“Open Source Software”) that are subject to their respective open source licenses (“Open Source Licenses”). Such Open Source Licenses contain a list of conditions with respect to warranty, copyright policy and other provisions. By executing this Agreement, Licensee undertakes to strictly comply with the terms and condition of the Open Source Licenses, as may be amended from time to time. In order to comply with the Open Source Licenses, Licensee shall read the respective licenses or notices, such list of Open Source Licenses may be amended from time to time by Applitools, at its sole discretion. In the event of any inconsistencies or conflicting provisions between the provisions of the Open Source Licenses and the provisions of this Agreement, the provisions of the Open Source Licenses shall prevail. Without derogating from the generality of the foregoing, it is clarified that any Open Source Software is provided on an “AS IS” basis, without indemnity or warranty of any kind, whether express or implied. For clarity, the representations and warranties set forth in Section ‎4 hereunder shall not apply to any Open Source Software. 17 | 1. Title & Ownership. APPLITOOLS DOES NOT SELL OR TRANSFER TITLE IN THE SOFTWARE, OR ANY PART THEREOF, TO LICENSEE. The Documentation, Software (excluding any Open Source Software and Third Party Software therein which are owned by their respective licensors) and/or any copies thereof, including without limitation any derivative works made (regardless of whether such derivative works were made and/or developed pursuant to the request and/or specifications of Licensee, and irrespective of any support and/or assistance Applitools may, will or had received from Licensee, or any third party on its behalf, with respect thereto), as well as any updates or upgrades thereto, if provided to Applitools pursuant to this Agreement, shall remain Applitools’ sole and exclusive property. All Intellectual Property Rights evidenced by or embodied in and/or attached/connected/related to the Software, or part thereof, are and shall be owned solely and exclusively by Applitools. Nothing in this Agreement shall constitute a waiver of Applitools’ Intellectual Property Rights under any law, or be in any way construed or interpreted as such. It is further agreed that to the extent Licensee provides Applitools with Feedback, Licensee acknowledges that any and all rights, including Intellectual Property Rights in such Feedback shall belong exclusively to Applitools and Licensee hereby irrevocably and unconditionally transfers and assigns to Applitools all intellectual property rights in such Feedback and waives any and all moral rights that Licensee may have in respect thereto. It is further understood that use of Feedback, if any, may be made by Applitools at its sole discretion, and that Applitools in no way shall be obliged to make use of any kind of the Feedback or part thereof. 18 | 1. Warranty. Applitools warrants that to its knowledge it has the right to grant the license under this Agreement. Applitools’ sole liability for any breach of this warranty or any other warranty under this Agreement shall be, at Applitools’ sole discretion: (i) to replace or repair the Software or the applicable portion thereof; or (ii) to terminate this Agreement. 19 | 2. Warranty Exclusions. The warranties set forth in Section ‎4 are contingent upon Licensee’s proper use of the Software, and shall not apply to damage caused by abuse, misuse, alteration, neglect or unauthorized repair or installation, or by the use or attempted use of Software other than that supplied and supported by Applitools. Applitools will use reasonable commercial efforts to repair or replace the Software or the applicable portion thereof, pursuant to the foregoing warranty within thirty (30) days of being so notified. 20 | 1. Warranty Disclaimers. AS BETWEEN LICENSEE AND APPLITOOLS, EXCEPT AS SET IN SECTION ‎4, THE SOFTWARE IS PROVIDED ON AN “AS IS” AND “AS AVAILABLE” BASIS WITHOUT WARRANTIES OF ANY KIND INCLUDING, WITHOUT LIMITATION, REPRESENTATIONS, WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR INTENDED OR PARTICULAR PURPOSE, TITLE, NON-INFRINGEMENT, THAT THE SOFTWARE WILL MEET LICENSEE’S REQUIREMENTS OR EXPECTATIONS OR WILL ACHIEVE ANY SPECIFIC RESULTS AND THOSE ARISING BY STATUTE OR FROM A COURSE OF DEALING OR USAGE OF TRADE. Applicable law may not allow the exclusion of certain warranties, so to that extent such exclusions may not apply. 21 | 1. High Risk Activities. You acknowledge that the Software is not fault tolerant and is not designed, manufactured, or intended for use or resale as on-line control equipment in hazardous or high risk environments and activities requiring fail-safe performance (such as in the operation of nuclear facilities, aircraft navigation or communication systems, air traffic control, direct life support machines and/or devices, or weapons systems) in which the failure of the Software could lead directly to death, personal injury, or severe physical or environmental damage, and You agree not to use or allow the use of the Software or any portion thereof for, or in connection with, any such environment or activity. 22 | 1. Indemnify. You agree that Applitools shall have no liability whatsoever for any use made of the Software by You or any third party. You hereby agree to defend, indemnify and hold harmless Applitools and its affiliates and their respective officers, directors, agents and employees from any and all claims, damages, liabilities, costs, and expenses (including attorney’s fees) arising from claims related to Your use of the Software as well as from Your failure to comply with this Agreement. 23 | 1. Limitation of Liability. UNDER NO CIRCUMSTANCES SHALL APPLITOOLS AND/OR ITS AFFILIATES BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL, PUNITIVE OR CONSEQUENTIAL DAMAGES, OR FOR ANY LOSS OF DATA, REVENUE, BUSINESS OR REPUTATION, THAT ARISES UNDER OR IN CONNECTION WITH THIS AGREEMENT, OR THAT RESULTS FROM THE USE OF, OR THE INABILITY TO USE, THE SOFTWARE. APPLITOOLS’S TOTAL AGGREGATE LIABILITY FOR ANY AND ALL DIRECT DAMAGES AND LOSSES THAT ARISE UNDER OR IN CONNECTION WITH THIS AGREEMENT SHALL NOT IN ANY CIRCUMSTANCE EXCEED THE AMOUNT OF 100.00 (ONE HUNDRED) UNITED STATES DOLLARS. THE FOREGOING LIMITATIONS AND EXCLUSIONS IN THIS SECTION ‎7 SHALL APPLY: (I) EVEN IF APPLITOOLS HAS BEEN ADVISED OF THE POSSIBILITY OF ANY DAMAGES OR LOSSES; (II) EVEN IF ANY REMEDY SET FORTH HEREIN FAILS OF ITS ESSENTIAL PURPOSE; AND (III) REGARDLESS OF THE BASIS OR THEORY OF LIABILITY. 24 | 1. Export Laws. Licensee agrees to comply fully with all U.S., EU, Israeli, and all applicable export laws and regulations to ensure that neither the Software nor any technical data related thereto are exported or re-exported directly or indirectly in violation of, or used for any purposes prohibited by, such laws and regulations. For clarity, and without derogating from Section ‎11 below, in case of any change of any applicable law, policy or regulation, which might affect Applitools’ business, Applitools will have the right to terminate this Agreement and the license granted hereunder and the Licensee shall have no claims regarding such termination. 25 | 1. Term and Termination. This Agreement shall continue until terminated as set forth in this section (the “Term”). You may terminate this Agreement at any time by removing the Software from Your system and destroying all copies of the Software and Documentation relating to the Software. Unauthorized copying of the Software or otherwise failing to comply with this Agreement will result in automatic immediate termination of this Agreement and will make available to Applitools legal remedies. Applitools reserves the right to terminate this Agreement and the License at any time and without notice. Upon termination of this Agreement, the License will terminate and You: (i) will cease any and all rights to use the Software, and (ii) will remove the Software from all hard drives, networks and other storage media and destroy all copies of the Software in your possession or under your control. The provisions of Sections ‎2.3, ‎2.4, ‎2.5, ‎6, ‎7, ‎8, ‎9, 10, ‎11 and ‎12 shall survive the termination, expiration or other ending of this Agreement. 26 | 1. Miscellaneous. This Agreement represents the complete agreement concerning the Software between You and Applitools and supersedes all prior agreements and representations between You and Applitools. If any provision of this Agreement is held to be unenforceable for any reason, such provision shall be reformed only to the extent necessary to make it enforceable. Any waiver of any provision of this Agreement will be effective only if in writing and signed by Applitools. This Agreement is personal to You and may not be assigned or transferred for any reason whatsoever without the consent of Applitools and any action or conduct in violation of the foregoing shall be void and without effect. Applitools expressly reserves the right to assign this Agreement and to delegate any of its obligations hereunder. This Agreement are governed by and construed under the laws of the State of Israel, excluding its conflicts of law rules. You expressly agree that the exclusive jurisdiction for any claim or action arising out of or relating to this Agreement shall be the courts located in Tel Aviv, Israel, and You further agree and submit to the exercise of personal jurisdiction of such courts for the purpose of litigating any such claim or action. In any action or proceeding to enforce rights under this Agreement, the prevailing party shall be entitled to recover costs and attorneys’ fees. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eyes.selenium 2 | 3 | This repository is deprecated. It has moved to [eyes-sdk-javascript](https://github.com/applitools/eyes.sdk.javascript1/tree/master/packages/eyes-selenium-3) 4 | -------------------------------------------------------------------------------- /example/EyesWebExample.js: -------------------------------------------------------------------------------- 1 | require('chromedriver'); 2 | 3 | var webdriver = require('selenium-webdriver'); 4 | var Capabilities = webdriver.Capabilities; 5 | var Builder = webdriver.Builder; 6 | var By = webdriver.By; 7 | 8 | var SeleniumSDK = require('../index'); // should be replaced to 'eyes.selenium' 9 | var Eyes = SeleniumSDK.Eyes; 10 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 11 | 12 | // Open a Chrome browser. 13 | var driver = new Builder() 14 | .withCapabilities(Capabilities.chrome()) 15 | .build(); 16 | 17 | // Initialize the eyes SDK and set your private API key. 18 | var eyes = new Eyes(); 19 | // eyes.setApiKey('YOUR_API_KEY'); // Set APPLITOOLS_API_KEY env variable or uncomment and update this line 20 | eyes.setLogHandler(new ConsoleLogHandler(false)); 21 | 22 | try { 23 | // Start the test and set the browser's viewport size to 800x600. 24 | eyes.open(driver, 'Hello World!', 'My first Javascript test!', {width: 800, height: 600}); 25 | 26 | // Navigate the browser to the "hello world!" web-site. 27 | driver.get('https://applitools.com/helloworld'); 28 | 29 | // Visual checkpoint #1. 30 | eyes.checkWindow('Main Page'); 31 | 32 | // Click the "Click me!" button. 33 | driver.findElement(By.css('button')).click(); 34 | 35 | // Visual checkpoint #2. 36 | eyes.checkWindow('Click!'); 37 | 38 | // End the test. 39 | eyes.close(); 40 | } finally { 41 | // Close the browser. 42 | driver.quit(); 43 | 44 | // If the test was aborted before eyes.close was called ends the test as aborted. 45 | eyes.abortIfNotClosed(); 46 | } 47 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | exports.EyesWebDriverScreenshot = require('./src/capture/EyesWebDriverScreenshot').EyesWebDriverScreenshot; 4 | exports.FirefoxScreenshotImageProvider = require('./src/capture/FirefoxScreenshotImageProvider').FirefoxScreenshotImageProvider; 5 | exports.ImageProviderFactory = require('./src/capture/ImageProviderFactory').ImageProviderFactory; 6 | exports.SafariScreenshotImageProvider = require('./src/capture/SafariScreenshotImageProvider').SafariScreenshotImageProvider; 7 | exports.TakesScreenshotImageProvider = require('./src/capture/TakesScreenshotImageProvider').TakesScreenshotImageProvider; 8 | exports.CssTranslatePositionProvider = require('./src/CssTranslatePositionProvider').CssTranslatePositionProvider; 9 | exports.ElementPositionProvider = require('./src/ElementPositionProvider').ElementPositionProvider; 10 | exports.Eyes = require('./src/Eyes').Eyes; 11 | exports.StitchMode = exports.Eyes.StitchMode; 12 | exports.EyesRegionProvider = require('./src/EyesRegionProvider').EyesRegionProvider; 13 | exports.EyesRemoteWebElement = require('./src/EyesRemoteWebElement').EyesRemoteWebElement; 14 | exports.EyesSeleniumUtils = require('./src/EyesSeleniumUtils').EyesSeleniumUtils; 15 | exports.EyesTargetLocator = require('./src/EyesTargetLocator').EyesTargetLocator; 16 | exports.EyesWebDriver = require('./src/EyesWebDriver').EyesWebDriver; 17 | exports.Frame = require('./src/Frame').Frame; 18 | exports.FrameChain = require('./src/FrameChain').FrameChain; 19 | exports.ScrollPositionProvider = require('./src/ScrollPositionProvider').ScrollPositionProvider; 20 | exports.Target = require('./src/Target').Target; 21 | 22 | var EyesSDK = require('eyes.sdk'); 23 | exports.ConsoleLogHandler = EyesSDK.ConsoleLogHandler; 24 | exports.ContextBasedScaleProvider = EyesSDK.ContextBasedScaleProvider; 25 | exports.ContextBasedScaleProviderFactory = EyesSDK.ContextBasedScaleProviderFactory; 26 | exports.CoordinatesType = EyesSDK.CoordinatesType; 27 | exports.CutProvider = EyesSDK.CutProvider; 28 | // exports.EyesBase = EyesSDK.EyesBase; 29 | exports.FailureReport = EyesSDK.EyesBase.FailureReport; 30 | exports.TestResultsStatus = EyesSDK.EyesBase.TestResultsStatus; 31 | exports.EyesScreenshot = EyesSDK.EyesScreenshot; 32 | exports.FileLogHandler = EyesSDK.FileLogHandler; 33 | exports.FixedCutProvider = EyesSDK.FixedCutProvider; 34 | exports.FixedScaleProvider = EyesSDK.FixedScaleProvider; 35 | exports.FixedScaleProviderFactory = EyesSDK.FixedScaleProviderFactory; 36 | exports.ImageProvider = EyesSDK.ImageProvider; 37 | exports.Logger = EyesSDK.Logger; 38 | exports.LogHandler = EyesSDK.LogHandler; 39 | exports.MatchSettings = EyesSDK.MatchSettings; 40 | exports.MatchLevel = EyesSDK.MatchSettings.MatchLevel; 41 | exports.ImageMatchSettings = EyesSDK.MatchSettings.ImageMatchSettings; 42 | exports.ExactMatchSettings = EyesSDK.MatchSettings.ExactMatchSettings; 43 | exports.MutableImage = EyesSDK.MutableImage; 44 | exports.NullCutProvider = EyesSDK.NullCutProvider; 45 | exports.NullLogHandler = EyesSDK.NullLogHandler; 46 | exports.NullScaleProvider = EyesSDK.NullScaleProvider; 47 | exports.PositionProvider = EyesSDK.PositionProvider; 48 | exports.RegionProvider = EyesSDK.RegionProvider; 49 | exports.RemoteSessionEventHandler = EyesSDK.RemoteSessionEventHandler; 50 | exports.ScaleProvider = EyesSDK.ScaleProvider; 51 | exports.ScaleProviderFactory = EyesSDK.ScaleProviderFactory; 52 | exports.ScaleProviderIdentityFactory = EyesSDK.ScaleProviderIdentityFactory; 53 | exports.ServerConnector = EyesSDK.ServerConnector; 54 | exports.SessionEventHandler = EyesSDK.SessionEventHandler; 55 | exports.TestResultsFormatter = EyesSDK.TestResultsFormatter; 56 | exports.Triggers = EyesSDK.Triggers; 57 | 58 | var EyesUtils = require('eyes.utils'); 59 | exports.ArgumentGuard = EyesUtils.ArgumentGuard; 60 | exports.GeneralUtils = EyesUtils.GeneralUtils; 61 | exports.GeometryUtils = EyesUtils.GeometryUtils; 62 | exports.ImageDeltaCompressor = EyesUtils.ImageDeltaCompressor; 63 | exports.ImageUtils = EyesUtils.ImageUtils; 64 | exports.PromiseFactory = EyesUtils.PromiseFactory; 65 | exports.PropertyHandler = EyesUtils.PropertyHandler; 66 | exports.SimplePropertyHandler = EyesUtils.SimplePropertyHandler; 67 | exports.ReadOnlyPropertyHandler = EyesUtils.ReadOnlyPropertyHandler; 68 | exports.StreamUtils = EyesUtils.StreamUtils; 69 | exports.UserAgent = EyesUtils.UserAgent; 70 | exports.OSNames = EyesUtils.OSNames; 71 | exports.BrowserNames = EyesUtils.BrowserNames; 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eyes.selenium", 3 | "version": "0.0.84", 4 | "description": "Applitools Eyes SDK For Selenium JavaScript WebDriver", 5 | "keywords": [ 6 | "eyes.selenium", 7 | "applitools", 8 | "eyes", 9 | "test automation", 10 | "visual regression", 11 | "automation", 12 | "selenium", 13 | "tests", 14 | "testing", 15 | "webdriver", 16 | "webdriverjs", 17 | "protractor" 18 | ], 19 | "homepage": "https://applitools.com", 20 | "author": { 21 | "name": "Applitools Team", 22 | "email": "team@applitools.com", 23 | "url": "https://applitools.com" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git://github.com/applitools/Eyes.Selenium.JavaScript.git" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/applitools/Eyes.Selenium.JavaScript/issues" 31 | }, 32 | "directories": { 33 | "example": "./example", 34 | "lib": "./src", 35 | "test": "./test" 36 | }, 37 | "files": [ 38 | "README.md", 39 | "LICENSE", 40 | "index.js", 41 | "src/", 42 | "test/", 43 | "typings/" 44 | ], 45 | "main": "./index.js", 46 | "types": "./typings/index.d.ts", 47 | "dependencies": { 48 | "@types/node": "*", 49 | "eyes.sdk": "^0.0.74", 50 | "eyes.utils": "^0.0.30" 51 | }, 52 | "devDependencies": { 53 | "chromedriver": "^2.38.3", 54 | "mocha": "^5.2.0", 55 | "protractor": "^4.0.0 || ^5.4.0", 56 | "selenium-webdriver": "^2.53.0 || ^3.6.0" 57 | }, 58 | "scripts": { 59 | "test": "npm run test-selenium", 60 | "test-appium": "mocha \"./test/appium/**/*.js\"", 61 | "test-selenium": "mocha \"./test/selenium/**/*.js\"", 62 | "test-protractor": "protractor protractor.conf.js" 63 | }, 64 | "license": "SEE LICENSE IN LICENSE", 65 | "engines": { 66 | "node": ">= 6.9.0" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | exports.config = { 5 | seleniumAddress: 'http://localhost:4444/wd/hub', 6 | specs: [ 7 | './test/protractor/**/*.js' 8 | ], 9 | capabilities: { 10 | browserName: 'chrome' 11 | }, 12 | restartBrowserBetweenTests: true, 13 | framework: 'jasmine2', 14 | jasmineNodeOpts: { 15 | showColors: true, 16 | defaultTimeoutInterval: 300000 17 | }, 18 | onPrepare: function() { 19 | // we need this to get appName and testName and pass them to eyes.open in beforeEach 20 | jasmine.getEnv().addReporter({ 21 | specStarted: function(result) { 22 | global.testName = result.description; 23 | global.appName = result.fullName.replace(" " + testName, ""); 24 | } 25 | }); 26 | }, 27 | }; -------------------------------------------------------------------------------- /src/CssTranslatePositionProvider.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | var EyesSDK = require('eyes.sdk'), 5 | EyesUtils = require('eyes.utils'), 6 | EyesSeleniumUtils = require('./EyesSeleniumUtils').EyesSeleniumUtils; 7 | var PositionProvider = EyesSDK.PositionProvider, 8 | ArgumentGuard = EyesUtils.ArgumentGuard; 9 | 10 | /** 11 | * @constructor 12 | * @param {Logger} logger A Logger instance. 13 | * @param {EyesWebDriver} executor 14 | * @param {PromiseFactory} promiseFactory 15 | * @augments PositionProvider 16 | */ 17 | function CssTranslatePositionProvider(logger, executor, promiseFactory) { 18 | ArgumentGuard.notNull(logger, "logger"); 19 | ArgumentGuard.notNull(executor, "executor"); 20 | 21 | this._logger = logger; 22 | this._driver = executor; 23 | this._promiseFactory = promiseFactory; 24 | this._lastSetPosition = null; 25 | } 26 | 27 | CssTranslatePositionProvider.prototype = new PositionProvider(); 28 | CssTranslatePositionProvider.prototype.constructor = CssTranslatePositionProvider; 29 | 30 | /** 31 | * @return {Promise<{x: number, y: number}>} The scroll position of the current frame. 32 | */ 33 | CssTranslatePositionProvider.prototype.getCurrentPosition = function () { 34 | var that = this; 35 | return that._promiseFactory.makePromise(function (resolve) { 36 | that._logger.verbose("getCurrentPosition()"); 37 | that._logger.verbose("position to return: ", that._lastSetPosition); 38 | resolve(that._lastSetPosition); 39 | }); 40 | }; 41 | 42 | /** 43 | * Go to the specified location. 44 | * @param {{x: number, y: number}} location The position to scroll to. 45 | * @return {Promise} 46 | */ 47 | CssTranslatePositionProvider.prototype.setPosition = function (location) { 48 | var that = this; 49 | that._logger.verbose("Setting position to:", location); 50 | return EyesSeleniumUtils.translateTo(this._driver, location, this._promiseFactory).then(function () { 51 | that._logger.verbose("Done!"); 52 | that._lastSetPosition = location; 53 | }); 54 | }; 55 | 56 | /** 57 | * @return {Promise<{width: number, height: number}>} The entire size of the container which the position is relative to. 58 | */ 59 | CssTranslatePositionProvider.prototype.getEntireSize = function () { 60 | var that = this; 61 | return EyesSeleniumUtils.getEntirePageSize(this._driver, this._promiseFactory).then(function (result) { 62 | that._logger.verbose("Entire size: ", result); 63 | return result; 64 | }); 65 | }; 66 | 67 | /** 68 | * @return {Promise>} 69 | */ 70 | CssTranslatePositionProvider.prototype.getState = function () { 71 | var that = this; 72 | return EyesSeleniumUtils.getCurrentTransform(this._driver, this._promiseFactory).then(function (transforms) { 73 | that._logger.verbose("Current transform", transforms); 74 | return transforms; 75 | }); 76 | }; 77 | 78 | /** 79 | * @param {object.} state The initial state of position 80 | * @return {Promise} 81 | */ 82 | CssTranslatePositionProvider.prototype.restoreState = function (state) { 83 | var that = this; 84 | return EyesSeleniumUtils.setTransforms(this._driver, state, this._promiseFactory).then(function () { 85 | that._logger.verbose("Transform (position) restored."); 86 | }); 87 | }; 88 | 89 | exports.CssTranslatePositionProvider = CssTranslatePositionProvider; 90 | }()); 91 | -------------------------------------------------------------------------------- /src/ElementFinderWrappers.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var EyesUtils = require('eyes.utils'), 5 | EyesRemoteWebElement = require('./EyesRemoteWebElement').EyesRemoteWebElement; 6 | var GeneralUtils = EyesUtils.GeneralUtils; 7 | 8 | // functions in ElementFinder that return a new ElementFinder and therefore we must wrap and return our own 9 | var ELEMENT_FINDER_TO_ELEMENT_FINDER_FUNCTIONS = ['element', '$', 'evaluate', 'allowAnimations']; 10 | // functions in ElementFinder that return a new ElementArrayFinder and therefore we must wrap and return our own 11 | var ELEMENT_FINDER_TO_ELEMENT_ARRAY_FINDER_FUNCTIONS = ['all', '$$']; 12 | // function in ElementArrayFinder that return a new ElementFinder and therefore we must wrap and return our own 13 | var ELEMENT_ARRAY_FINDER_TO_ELEMENT_FINDER_FUNCTIONS = ['get', 'first', 'last']; 14 | 15 | /** 16 | * Wraps Protractor's ElementFinder to make sure we return our own Web Element. 17 | * 18 | * @param {ElementFinder} finder 19 | * @param {EyesWebDriver} eyesDriver 20 | * @param {Logger} logger 21 | * @mixin ElementFinder 22 | * @constructor 23 | **/ 24 | function ElementFinderWrapper(finder, eyesDriver, logger) { 25 | GeneralUtils.mixin(this, finder); 26 | 27 | this._logger = logger; 28 | this._eyesDriver = eyesDriver; 29 | this._finder = finder; 30 | 31 | var that = this; 32 | ELEMENT_FINDER_TO_ELEMENT_FINDER_FUNCTIONS.forEach(function (fnName) { 33 | that[fnName] = function () { 34 | return new ElementFinderWrapper(that._finder[fnName].apply(that._finder, arguments), that._eyesDriver, that._logger); 35 | }; 36 | }); 37 | 38 | ELEMENT_FINDER_TO_ELEMENT_ARRAY_FINDER_FUNCTIONS.forEach(function (fnName) { 39 | that[fnName] = function () { 40 | return new ElementArrayFinderWrapper(that._finder[fnName].apply(that._finder, arguments), that._eyesDriver, that._logger); 41 | }; 42 | }); 43 | } 44 | 45 | /** 46 | * Wrap the getWebElement function 47 | * 48 | * @return {EyesRemoteWebElement} 49 | */ 50 | ElementFinderWrapper.prototype.getWebElement = function () { 51 | this._logger.verbose("ElementFinderWrapper:getWebElement - called"); 52 | return new EyesRemoteWebElement(this._finder.getWebElement.apply(this._finder), this._eyesDriver, this._logger); 53 | }; 54 | 55 | /** 56 | * Schedules a command to click on this element. 57 | * @return {!Promise} A promise that will be resolved when the click command has completed. 58 | */ 59 | ElementFinderWrapper.prototype.click = function () { 60 | this._logger.verbose("ElementFinderWrapper:click - called"); 61 | var element = this.getWebElement(); 62 | return element.click.apply(element); 63 | }; 64 | 65 | /** 66 | * Schedules a command to type a sequence on the DOM element represented by this instance. 67 | * @param {...(number|string|!IThenable<(number|string)>)} var_args The sequence of keys to type. Number keys may 68 | * be referenced numerically or by string (1 or '1'). All arguments will be joined into a single sequence. 69 | * @return {!Promise} A promise that will be resolved when all keys have been typed. 70 | */ 71 | ElementFinderWrapper.prototype.sendKeys = function (var_args) { 72 | this._logger.verbose("ElementFinderWrapper:sendKeys - called"); 73 | var element = this.getWebElement(); 74 | return element.sendKeys.apply(element, arguments); 75 | }; 76 | 77 | /** 78 | * Wrapper for ElementArrayFinder object from Protractor 79 | * 80 | * @param {ElementArrayFinder} arrayFinder 81 | * @param {EyesWebDriver} eyesDriver 82 | * @param {Logger} logger 83 | * @mixin ElementArrayFinder 84 | * @constructor 85 | **/ 86 | function ElementArrayFinderWrapper(arrayFinder, eyesDriver, logger) { 87 | GeneralUtils.mixin(this, arrayFinder); 88 | 89 | this._logger = logger; 90 | this._eyesDriver = eyesDriver; 91 | this._arrayFinder = arrayFinder; 92 | 93 | var that = this; 94 | // Wrap the functions that return objects that require pre-wrapping 95 | ELEMENT_ARRAY_FINDER_TO_ELEMENT_FINDER_FUNCTIONS.forEach(function (fnName) { 96 | that[fnName] = function () { 97 | return new ElementFinderWrapper(that._arrayFinder[fnName].apply(that._arrayFinder, arguments), that._eyesDriver, that._logger); 98 | }; 99 | }); 100 | 101 | // Patch this internal function. 102 | var originalFn = that._arrayFinder.asElementFinders_; 103 | that._arrayFinder.asElementFinders_ = function () { 104 | return originalFn.apply(that._arrayFinder).then(function (arr) { 105 | var list = []; 106 | arr.forEach(function (finder) { 107 | list.push(new ElementFinderWrapper(finder, that._eyesDriver, that._logger)); 108 | }); 109 | return list; 110 | }); 111 | } 112 | } 113 | 114 | /** 115 | * Wrap the getWebElements function 116 | * 117 | * @return {Promise} 118 | */ 119 | ElementArrayFinderWrapper.prototype.getWebElements = function () { 120 | var that = this; 121 | that._logger.verbose("ElementArrayFinderWrapper:getWebElements - called"); 122 | return that._arrayFinder.getWebElements.apply(that._arrayFinder).then(function (elements) { 123 | var res = []; 124 | elements.forEach(function (el) { 125 | res.push(new EyesRemoteWebElement(el, that._eyesDriver, that._logger)); 126 | }); 127 | return res; 128 | }); 129 | }; 130 | 131 | exports.ElementFinderWrapper = ElementFinderWrapper; 132 | exports.ElementArrayFinderWrapper = ElementArrayFinderWrapper; 133 | }()); 134 | -------------------------------------------------------------------------------- /src/ElementPositionProvider.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | var EyesSDK = require('eyes.sdk'), 5 | EyesUtils = require('eyes.utils'); 6 | var PositionProvider = EyesSDK.PositionProvider, 7 | ArgumentGuard = EyesUtils.ArgumentGuard; 8 | 9 | /** 10 | * @constructor 11 | * @param {Logger} logger A Logger instance. 12 | * @param {EyesWebDriver} eyesDriver 13 | * @param {EyesRemoteWebElement} element 14 | * @param {PromiseFactory} promiseFactory 15 | * @augments PositionProvider 16 | */ 17 | function ElementPositionProvider(logger, eyesDriver, element, promiseFactory) { 18 | ArgumentGuard.notNull(logger, "logger"); 19 | ArgumentGuard.notNull(eyesDriver, "executor"); 20 | ArgumentGuard.notNull(element, "element"); 21 | 22 | this._logger = logger; 23 | this._eyesDriver = eyesDriver; 24 | this._promiseFactory = promiseFactory; 25 | this._element = element; 26 | } 27 | 28 | ElementPositionProvider.prototype = new PositionProvider(); 29 | ElementPositionProvider.prototype.constructor = ElementPositionProvider; 30 | 31 | /** 32 | * @return {Promise<{x: number, y: number}>} The scroll position of the current frame. 33 | */ 34 | ElementPositionProvider.prototype.getCurrentPosition = function () { 35 | var that = this, elScrollLeft; 36 | that._logger.verbose("getCurrentPosition()"); 37 | 38 | return that._element.getScrollLeft().then(function (value) { 39 | elScrollLeft = value; 40 | return that._element.getScrollTop(); 41 | }).then(function (value) { 42 | var location = { x: elScrollLeft, y: value }; 43 | that._logger.verbose("Current position: ", location); 44 | return location; 45 | }); 46 | }; 47 | 48 | /** 49 | * Go to the specified location. 50 | * @param {{x: number, y: number}} location The position to scroll to. 51 | * @return {Promise} 52 | */ 53 | ElementPositionProvider.prototype.setPosition = function (location) { 54 | var that = this; 55 | that._logger.verbose("Scrolling to:", location); 56 | return that._element.scrollTo(location).then(function () { 57 | that._logger.verbose("Done scrolling!"); 58 | }); 59 | }; 60 | 61 | /** 62 | * @return {Promise<{width: number, height: number}>} The entire size of the container which the position is relative to. 63 | */ 64 | ElementPositionProvider.prototype.getEntireSize = function () { 65 | var that = this, elScrollWidth, elScrollHeight; 66 | that._logger.verbose("getEntireSize()"); 67 | return that._element.getScrollWidth().then(function (value) { 68 | elScrollWidth = value; 69 | return that._element.getScrollHeight(); 70 | }).then(function (value) { 71 | elScrollHeight = value; 72 | 73 | that._logger.verbose("Entire size: ", elScrollWidth, ",", elScrollHeight); 74 | 75 | return { 76 | width: elScrollWidth, 77 | height: elScrollHeight 78 | }; 79 | }); 80 | }; 81 | 82 | /** 83 | * @return {Promise<{x: number, y: number}>} 84 | */ 85 | ElementPositionProvider.prototype.getState = function () { 86 | return this.getCurrentPosition(); 87 | }; 88 | 89 | /** 90 | * @param {{x: number, y: number}} state The initial state of position 91 | * @return {Promise} 92 | */ 93 | ElementPositionProvider.prototype.restoreState = function (state) { 94 | var that = this; 95 | return this.setPosition(state).then(function () { 96 | that._logger.verbose("Position restored."); 97 | }); 98 | }; 99 | 100 | exports.ElementPositionProvider = ElementPositionProvider; 101 | }()); 102 | -------------------------------------------------------------------------------- /src/EyesRegionProvider.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var EyesUtils = require('eyes.utils'), 5 | EyesSDK = require('eyes.sdk'), 6 | EyesWebDriverScreenshot = require('./capture/EyesWebDriverScreenshot').EyesWebDriverScreenshot; 7 | var RegionProvider = EyesSDK.RegionProvider, 8 | GeometryUtils = EyesUtils.GeometryUtils; 9 | 10 | /** 11 | * @param {Logger} logger 12 | * @param driver 13 | * @param {{left: number, top: number, width: number, height: number}} region 14 | * @param {CoordinatesType} coordinatesType 15 | * @augments RegionProvider 16 | * @constructor 17 | */ 18 | function EyesRegionProvider(logger, driver, region, coordinatesType) { 19 | this._logger = logger; 20 | this._driver = driver; 21 | this._region = region || GeometryUtils.createRegion(0, 0, 0, 0); 22 | this._coordinatesType = coordinatesType || null; 23 | } 24 | 25 | EyesRegionProvider.prototype = new RegionProvider(); 26 | EyesRegionProvider.prototype.constructor = EyesRegionProvider; 27 | 28 | /** 29 | * @return {{left: number, top: number, width: number, height: number}} A region with "as is" viewport coordinates. 30 | */ 31 | EyesRegionProvider.prototype.getRegion = function () { 32 | return this._region; 33 | }; 34 | 35 | /** 36 | * @param {MutableImage} image 37 | * @param {CoordinatesType} toCoordinatesType 38 | * @param {PromiseFactory} promiseFactory 39 | * @return {Promise<{left: number, top: number, width: number, height: number}>} A region in selected viewport coordinates. 40 | */ 41 | EyesRegionProvider.prototype.getRegionInLocation = function (image, toCoordinatesType, promiseFactory) { 42 | var that = this; 43 | return promiseFactory.makePromise(function (resolve) { 44 | if (that._coordinatesType == toCoordinatesType) { 45 | resolve(that._region); 46 | return; 47 | } 48 | 49 | var ewds = new EyesWebDriverScreenshot(that._logger, that._driver, image, promiseFactory); 50 | return ewds.buildScreenshot().then(function () { 51 | var newRegion = ewds.convertRegionLocation(that._region, that._coordinatesType, toCoordinatesType); 52 | resolve(newRegion); 53 | }); 54 | }); 55 | }; 56 | 57 | /** 58 | * @return {CoordinatesType} The type of coordinates on which the region is based. 59 | */ 60 | EyesRegionProvider.prototype.getCoordinatesType = function () { 61 | return this._coordinatesType; 62 | }; 63 | 64 | exports.EyesRegionProvider = EyesRegionProvider; 65 | }()); 66 | -------------------------------------------------------------------------------- /src/EyesRemoteWebElement.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var EyesSDK = require('eyes.sdk'), 5 | EyesUtils = require('eyes.utils'); 6 | var MouseAction = EyesSDK.Triggers.MouseAction, 7 | GeneralUtils = EyesUtils.GeneralUtils, 8 | GeometryUtils = EyesUtils.GeometryUtils; 9 | 10 | var JS_GET_SCROLL_LEFT = "return arguments[0].scrollLeft;"; 11 | 12 | var JS_GET_SCROLL_TOP = "return arguments[0].scrollTop;"; 13 | 14 | var JS_GET_SCROLL_WIDTH = "return arguments[0].scrollWidth;"; 15 | 16 | var JS_GET_SCROLL_HEIGHT = "return arguments[0].scrollHeight;"; 17 | 18 | var JS_GET_OVERFLOW = "return arguments[0].style.overflow;"; 19 | 20 | var JS_GET_LOCATION = "var rect = arguments[0].getBoundingClientRect(); return [rect.left, rect.top]"; 21 | 22 | /** 23 | * @param {string} styleProp 24 | * @return {string} 25 | */ 26 | var JS_GET_COMPUTED_STYLE_FORMATTED_STR = function (styleProp) { 27 | return "var elem = arguments[0], styleProp = '"+ styleProp +"'; " + 28 | "if (window.getComputedStyle) { " + 29 | " return window.getComputedStyle(elem, null).getPropertyValue(styleProp);" + 30 | "} else if (elem.currentStyle) { " + 31 | " return elem.currentStyle[styleProp];" + 32 | "} else { " + 33 | " return null;" + 34 | "}"; 35 | }; 36 | 37 | /** 38 | * @param {number} scrollLeft 39 | * @param {number} scrollTop 40 | * @return {string} 41 | */ 42 | var JS_SCROLL_TO_FORMATTED_STR = function (scrollLeft, scrollTop) { 43 | return "arguments[0].scrollLeft = " + scrollLeft + "; " + 44 | "arguments[0].scrollTop = " + scrollTop + ";"; 45 | }; 46 | 47 | /** 48 | * @param {string} overflow 49 | * @return {string} 50 | */ 51 | var JS_SET_OVERFLOW_FORMATTED_STR = function (overflow) { 52 | return "arguments[0].style.overflow = '" + overflow + "'"; 53 | }; 54 | 55 | /** 56 | * Wraps a Remote Web Element. 57 | * 58 | * @constructor 59 | * @param {WebElement} webElement 60 | * @param {EyesWebDriver} eyesDriver 61 | * @param {Logger} logger 62 | * @mixin WebElement 63 | **/ 64 | function EyesRemoteWebElement(webElement, eyesDriver, logger) { 65 | this._element = webElement; 66 | this._logger = logger; 67 | this._eyesDriver = eyesDriver; 68 | 69 | GeneralUtils.mixin(this, webElement); 70 | } 71 | 72 | function _getRectangle(location, size) { 73 | size = size || {height: 0, width: 0}; 74 | location = location || {x: 0, y: 0}; 75 | 76 | var left = location.x, 77 | top = location.y, 78 | width = size.width, 79 | height = size.height; 80 | 81 | if (left < 0) { 82 | width = Math.max(0, width + left); 83 | left = 0; 84 | } 85 | 86 | if (top < 0) { 87 | height = Math.max(0, height + top); 88 | top = 0; 89 | } 90 | 91 | return { 92 | top: top, 93 | left: left, 94 | width: width, 95 | height: height 96 | }; 97 | } 98 | 99 | function _getBounds (element) { 100 | return element.getLocation().then(function (location) { 101 | return element.getSize().then(function (size) { 102 | return _getRectangle(location, size); 103 | }, function () { 104 | return _getRectangle(location); 105 | }); 106 | }, function () { 107 | return _getRectangle(); 108 | }); 109 | } 110 | 111 | EyesRemoteWebElement.registerSendKeys = function (element, eyesDriver, logger, args) { 112 | var text = args.join(''); 113 | logger.verbose("registerSendKeys: text is", text); 114 | return _getBounds(element).then(function (rect) { 115 | eyesDriver.getEyes().addKeyboardTrigger(rect, text); 116 | }); 117 | }; 118 | 119 | // noinspection JSUnusedGlobalSymbols 120 | /** 121 | * Schedules a command to type a sequence on the DOM element represented by this instance. 122 | * @param {...(number|string|!IThenable<(number|string)>)} var_args The sequence of keys to type. Number keys may 123 | * be referenced numerically or by string (1 or '1'). All arguments will be joined into a single sequence. 124 | * @return {!Promise} A promise that will be resolved when all keys have been typed. 125 | */ 126 | EyesRemoteWebElement.prototype.sendKeys = function (var_args) { 127 | var that = this, args = Array.prototype.slice.call(arguments, 0); 128 | return EyesRemoteWebElement.registerSendKeys(that._element, that._eyesDriver, that._logger, args).then(function () { 129 | var element = that.getRemoteWebElement(); 130 | return element.sendKeys.apply(element, args); 131 | }); 132 | }; 133 | 134 | EyesRemoteWebElement.registerClick = function (element, eyesDriver, logger) { 135 | logger.verbose("apply click on element"); 136 | return _getBounds(element).then(function (rect) { 137 | var offset = {x: rect.width / 2, y: rect.height / 2}; 138 | eyesDriver.getEyes().addMouseTrigger(MouseAction.Click, rect, offset); 139 | }); 140 | }; 141 | 142 | // noinspection JSUnusedGlobalSymbols 143 | /** 144 | * Schedules a command to click on this element. 145 | * @return {!Promise} A promise that will be resolved when the click command has completed. 146 | */ 147 | EyesRemoteWebElement.prototype.click = function () { 148 | var that = this; 149 | that._logger.verbose("click on element"); 150 | return EyesRemoteWebElement.registerClick(that._element, that._eyesDriver, that._logger).then(function () { 151 | var element = that.getRemoteWebElement(); 152 | return element.click.apply(element); 153 | }); 154 | }; 155 | 156 | EyesRemoteWebElement.prototype.findElement = function (locator) { 157 | return new EyesRemoteWebElement(this.getRemoteWebElement().findElement(locator), this._eyesDriver, this._logger); 158 | }; 159 | 160 | EyesRemoteWebElement.prototype.findElements = function (locator) { 161 | var that = this; 162 | return this.getRemoteWebElement().findElements(locator).then(function (elements) { 163 | return elements.map(function (element) { 164 | return new EyesRemoteWebElement(element, that._eyesDriver, that._logger); 165 | }); 166 | }); 167 | }; 168 | 169 | /** 170 | * Returns the computed value of the style property for the current element. 171 | * @param {string} propStyle The style property which value we would like to extract. 172 | * @return {promise.Promise} The value of the style property of the element, or {@code null}. 173 | */ 174 | EyesRemoteWebElement.prototype.getComputedStyle = function (propStyle) { 175 | return this._eyesDriver.executeScript(JS_GET_COMPUTED_STYLE_FORMATTED_STR(propStyle), this.getRemoteWebElement()); 176 | }; 177 | 178 | /** 179 | * @return {promise.Promise} The integer value of a computed style. 180 | */ 181 | EyesRemoteWebElement.prototype.getComputedStyleInteger = function (propStyle) { 182 | return this.getComputedStyle(propStyle).then(function (value) { 183 | return Math.round(parseFloat(value.trim().replace("px", ""))); 184 | }); 185 | }; 186 | 187 | /** 188 | * @return {promise.Promise} The value of the scrollLeft property of the element. 189 | */ 190 | EyesRemoteWebElement.prototype.getScrollLeft = function () { 191 | return this._eyesDriver.executeScript(JS_GET_SCROLL_LEFT, this.getRemoteWebElement()).then(function (value) { 192 | return parseInt(value, 10); 193 | }); 194 | }; 195 | 196 | /** 197 | * @return {promise.Promise} The value of the scrollTop property of the element. 198 | */ 199 | EyesRemoteWebElement.prototype.getScrollTop = function () { 200 | return this._eyesDriver.executeScript(JS_GET_SCROLL_TOP, this.getRemoteWebElement()).then(function (value) { 201 | return parseInt(value, 10); 202 | }); 203 | }; 204 | 205 | /** 206 | * @return {promise.Promise} The value of the scrollWidth property of the element. 207 | */ 208 | EyesRemoteWebElement.prototype.getScrollWidth = function () { 209 | return this._eyesDriver.executeScript(JS_GET_SCROLL_WIDTH, this.getRemoteWebElement()).then(function (value) { 210 | return parseInt(value, 10); 211 | }); 212 | }; 213 | 214 | /** 215 | * @return {promise.Promise} The value of the scrollHeight property of the element. 216 | */ 217 | EyesRemoteWebElement.prototype.getScrollHeight = function () { 218 | return this._eyesDriver.executeScript(JS_GET_SCROLL_HEIGHT, this.getRemoteWebElement()).then(function (value) { 219 | return parseInt(value, 10); 220 | }); 221 | }; 222 | 223 | /** 224 | * @return {promise.Promise} The width of the left border. 225 | */ 226 | EyesRemoteWebElement.prototype.getBorderLeftWidth = function () { 227 | return this.getComputedStyleInteger("border-left-width"); 228 | }; 229 | 230 | /** 231 | * @return {promise.Promise} The width of the right border. 232 | */ 233 | EyesRemoteWebElement.prototype.getBorderRightWidth = function () { 234 | return this.getComputedStyleInteger("border-right-width"); 235 | }; 236 | 237 | /** 238 | * @return {promise.Promise} The width of the top border. 239 | */ 240 | EyesRemoteWebElement.prototype.getBorderTopWidth = function () { 241 | return this.getComputedStyleInteger("border-top-width"); 242 | }; 243 | 244 | /** 245 | * @return {promise.Promise} The width of the bottom border. 246 | */ 247 | EyesRemoteWebElement.prototype.getBorderBottomWidth = function () { 248 | return this.getComputedStyleInteger("border-bottom-width"); 249 | }; 250 | 251 | /** 252 | * @return {!promise.Thenable<{width: number, height: number}>} element's size 253 | */ 254 | EyesRemoteWebElement.prototype.getSize = function () { 255 | return this.getRemoteWebElement().getSize().then(function (value) { 256 | return GeometryUtils.createSize(value.width, value.height); 257 | }); 258 | }; 259 | 260 | /** 261 | * @return {!promise.Thenable<{x: number, y: number}>} element's location 262 | */ 263 | EyesRemoteWebElement.prototype.getLocation = function () { 264 | // The workaround is similar to Java one, 265 | // https://github.com/applitools/eyes.sdk.java3/blob/master/eyes.selenium.java/src/main/java/com/applitools/eyes/selenium/EyesRemoteWebElement.java#L453 266 | // but we can't get raw data (including decimal values) from remote Selenium webdriver 267 | // and therefore we should use our own client-side script for retrieving exact values and rounding up them 268 | 269 | // this.getRemoteWebElement().getLocation() 270 | return this._eyesDriver.executeScript(JS_GET_LOCATION, this.getRemoteWebElement()).then(function (value) { 271 | var x = Math.ceil(value[0]) || 0; 272 | var y = Math.ceil(value[1]) || 0; 273 | return GeometryUtils.createLocation(x, y); 274 | }); 275 | }; 276 | 277 | /** 278 | * Scrolls to the specified location inside the element. 279 | * @param {{x: number, y: number}} location The location to scroll to. 280 | * @return {promise.Promise} 281 | */ 282 | EyesRemoteWebElement.prototype.scrollTo = function (location) { 283 | return this._eyesDriver.executeScript(JS_SCROLL_TO_FORMATTED_STR(location.x, location.y), this.getRemoteWebElement()); 284 | }; 285 | 286 | /** 287 | * @return {promise.Promise} The overflow of the element. 288 | */ 289 | EyesRemoteWebElement.prototype.getOverflow = function () { 290 | return this._eyesDriver.executeScript(JS_GET_OVERFLOW, this.getRemoteWebElement()); 291 | }; 292 | 293 | /** 294 | * @param {string} overflow The overflow to set 295 | * @return {promise.Promise} The overflow of the element. 296 | */ 297 | EyesRemoteWebElement.prototype.setOverflow = function (overflow) { 298 | return this._eyesDriver.executeScript(JS_SET_OVERFLOW_FORMATTED_STR(overflow), this.getRemoteWebElement()); 299 | }; 300 | 301 | /** 302 | * @return {WebElement} The original element object 303 | */ 304 | EyesRemoteWebElement.prototype.getRemoteWebElement = function () { 305 | if (this._element.getRemoteWebElement) { 306 | this._logger.log('EyesRemoteWebElement.getRemoteWebElement - remote element contains another remote'); 307 | return this._element.getRemoteWebElement(); 308 | } 309 | 310 | return this._element; 311 | }; 312 | 313 | exports.EyesRemoteWebElement = EyesRemoteWebElement; 314 | }()); 315 | -------------------------------------------------------------------------------- /src/EyesTargetLocator.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var EyesUtils = require('eyes.utils'), 5 | FrameChain = require('./FrameChain').FrameChain, 6 | ScrollPositionProvider = require('./ScrollPositionProvider').ScrollPositionProvider, 7 | EyesRemoteWebElement = require('./EyesRemoteWebElement').EyesRemoteWebElement; 8 | var ArgumentGuard = EyesUtils.ArgumentGuard, 9 | GeneralUtils = EyesUtils.GeneralUtils; 10 | 11 | /** 12 | * @enum {number} 13 | * @readonly 14 | */ 15 | EyesTargetLocator.TargetType = { 16 | FRAME: 1, 17 | PARENT_FRAME: 2, 18 | DEFAULT_CONTENT: 3 19 | }; 20 | 21 | //noinspection JSUnusedLocalSymbols 22 | /** 23 | * A wrapper for an action to be performed before the actual switch is made. 24 | */ 25 | function OnWillSwitch() { 26 | /** 27 | * Will be called before switching into a frame. 28 | * @param {TargetType} targetType The type of frame we're about to switch into. 29 | * @param {WebElement} targetFrame The element about to be switched to, 30 | * if available. Otherwise, null. 31 | */ 32 | this.prototype.willSwitchToFrame = function (targetType, targetFrame) { 33 | }; 34 | 35 | /** 36 | * Will be called before switching into a window. 37 | * @param {string} nameOrHandle The name/handle of the window to be switched to. 38 | */ 39 | this.prototype.willSwitchToWindow = function (nameOrHandle) { 40 | }; 41 | } 42 | 43 | /** 44 | * Initialized a new EyesTargetLocator object. 45 | * @constructor 46 | * @param {Logger} logger A Logger instance. 47 | * @param {EyesWebDriver} driver The WebDriver from which the targetLocator was received. 48 | * @param {TargetLocator} targetLocator The actual TargetLocator object. 49 | * @param {OnWillSwitch} onWillSwitch A delegate to be called whenever a relevant switch is about to be performed. 50 | * @param {PromiseFactory} promiseFactory 51 | * @mixin TargetLocator 52 | */ 53 | function EyesTargetLocator(logger, driver, targetLocator, onWillSwitch, promiseFactory) { 54 | ArgumentGuard.notNull(logger, "logger"); 55 | ArgumentGuard.notNull(driver, "driver"); 56 | ArgumentGuard.notNull(targetLocator, "targetLocator"); 57 | ArgumentGuard.notNull(onWillSwitch, "onWillSwitch"); 58 | 59 | this._logger = logger; 60 | this._driver = driver; 61 | this._targetLocator = targetLocator; 62 | this._onWillSwitch = onWillSwitch; 63 | this._promiseFactory = promiseFactory; 64 | this._scrollPosition = new ScrollPositionProvider(this._logger, this._driver, this._promiseFactory); 65 | 66 | GeneralUtils.mixin(this, targetLocator); 67 | } 68 | 69 | /** 70 | * @param {int|string|EyesRemoteWebElement} obj 71 | * @return {Promise} 72 | */ 73 | EyesTargetLocator.prototype.frame = function (obj) { 74 | var that = this, frames; 75 | if (typeof obj === 'string' || obj instanceof String) { 76 | this._logger.verbose("EyesTargetLocator.frame('", obj, "')"); 77 | // Finding the target element so we can report it. 78 | // We use find elements(plural) to avoid exception when the element 79 | // is not found. 80 | this._logger.verbose("Getting frames by name..."); 81 | return this._driver.findElementsByName(obj).then(function(elements) { 82 | if (elements.length === 0) { 83 | that._logger.verbose("No frames Found! Trying by id..."); 84 | // If there are no frames by that name, we'll try the id 85 | return that._driver.findElementsById(obj); 86 | } 87 | 88 | return elements; 89 | }).then(function(elements) { 90 | if (elements.length === 0) { 91 | // No such frame, bummer 92 | throw new Error("No frame with name or id '" + obj + "' exists!"); 93 | } 94 | 95 | return elements; 96 | }).then(function (frames) { 97 | if (frames.length === 0) { 98 | that._logger.verbose("No frames Found! Trying by id..."); 99 | // If there are no frames by that name, we'll try the id 100 | frames = that._driver.findElementsById(obj); 101 | if (frames.length === 0) { 102 | // No such frame, bummer 103 | throw new Error("No frame with name or id '" + obj + "' exists!"); 104 | } 105 | } 106 | that._logger.verbose("Done! Making preparations.."); 107 | return that._onWillSwitch.willSwitchToFrame(EyesTargetLocator.TargetType.FRAME, frames[0]); 108 | }).then(function () { 109 | that._logger.verbose("Done! Switching to frame..."); 110 | return that._targetLocator.frame(obj) 111 | }).then(function () { 112 | that._logger.verbose("Done!"); 113 | }); 114 | } else if (obj instanceof EyesRemoteWebElement) { 115 | that._logger.verbose("EyesTargetLocator.frame(element)"); 116 | that._logger.verbose("Making preparations.."); 117 | return that._onWillSwitch.willSwitchToFrame(EyesTargetLocator.TargetType.FRAME, obj).then(function () { 118 | that._logger.verbose("Done! Switching to frame..."); 119 | return that._targetLocator.frame(obj.getRemoteWebElement()) 120 | }).then(function () { 121 | that._logger.verbose("Done!"); 122 | }); 123 | } else { 124 | that._logger.verbose("EyesTargetLocator.frame(", typeof obj, ")"); 125 | // Finding the target element so and reporting it using onWillSwitch. 126 | that._logger.verbose("Getting frames list..."); 127 | return that._driver.findElementsByCssSelector("frame, iframe").then(function (elements) { 128 | if (obj > elements.length) { 129 | throw new Error("Frame index [" + obj + "] is invalid!"); 130 | } 131 | 132 | return elements; 133 | }).then(function (frames) { 134 | that._logger.verbose("Done! getting the specific frame..."); 135 | var targetFrame = frames[obj]; 136 | that._logger.verbose("Done! Making preparations..."); 137 | return that._onWillSwitch.willSwitchToFrame(EyesTargetLocator.TargetType.FRAME, targetFrame); 138 | 139 | }).then(function () { 140 | that._logger.verbose("Done! Switching to frame..."); 141 | return that._targetLocator.frame(obj) 142 | }).then(function () { 143 | that._logger.verbose("Done!"); 144 | }); 145 | } 146 | }; 147 | 148 | /** 149 | * @return {Promise} 150 | */ 151 | 152 | EyesTargetLocator.prototype.parentFrame = function () { 153 | var that = this; 154 | this._logger.verbose("EyesTargetLocator.parentFrame()"); 155 | return this._driver.getPromiseFactory().makePromise(function (resolve) { 156 | if (that._driver.getFrameChain().size() !== 0) { 157 | that._logger.verbose("Making preparations.."); 158 | return that._onWillSwitch.willSwitchToFrame(EyesTargetLocator.TargetType.PARENT_FRAME, null).then(function () { 159 | return that._targetLocator.defaultContent(); 160 | }).then(function () { 161 | that._logger.verbose("Done! Switching to parent frame.."); 162 | return that.frames(that._driver.getFrameChain()); 163 | }).then(function () { 164 | that._logger.verbose("Done!"); 165 | resolve(); 166 | }); 167 | } 168 | 169 | resolve(); 170 | }); 171 | }; 172 | 173 | /** 174 | * Switches into every frame in the frame chain. This is used as way to 175 | * switch into nested frames (while considering scroll) in a single call. 176 | * @param {FrameChain|string[]} obj The path to the frame to switch to. 177 | * Or the path to the frame to check. This is a list of frame names/IDs 178 | * (where each frame is nested in the previous frame). 179 | * @return {Promise} The WebDriver with the switched context. 180 | */ 181 | EyesTargetLocator.prototype.frames = function (obj) { 182 | var that = this; 183 | return this._driver.getPromiseFactory().makePromise(function (resolve) { 184 | if (obj instanceof FrameChain) { 185 | that._logger.verbose("EyesTargetLocator.frames(frameChain)"); 186 | if (obj.size() > 0) { 187 | return _framesSetPosition(that, obj, obj.size() - 1, that._driver.getPromiseFactory()).then(function () { 188 | that._logger.verbose("Done switching into nested frames!"); 189 | resolve(); 190 | }); 191 | } 192 | 193 | resolve(); 194 | } else if (Array.isArray(obj)) { 195 | if (obj.length > 0) { 196 | return _framesSetPositionFromArray(that, obj, obj.length - 1, that._driver.getPromiseFactory()).then(function () { 197 | that._logger.verbose("Done switching into nested frames!"); 198 | resolve(); 199 | }); 200 | } 201 | 202 | resolve(); 203 | } 204 | }); 205 | }; 206 | 207 | function _framesSetPosition(targetLocator, obj, retries, promiseFactory) { 208 | return promiseFactory.makePromise(function (resolve) { 209 | 210 | // TODO: check order, if it make sense 211 | var frame = obj.getFrames()[retries]; 212 | targetLocator._scrollPosition.setPosition(frame.getParentScrollPosition()).then(function () { 213 | targetLocator._logger.verbose("Done! Switching to frame..."); 214 | return targetLocator._driver.switchTo().frame(frame.getReference()); 215 | }).then(function () { 216 | targetLocator._logger.verbose("Done!"); 217 | 218 | if (retries === 0) { 219 | resolve(); 220 | return; 221 | } 222 | 223 | targetLocator.controlFlow().then(function () { 224 | _framesSetPosition(targetLocator, obj, retries - 1, promiseFactory).then(function () { 225 | resolve(); 226 | }); 227 | }); 228 | }); 229 | }); 230 | } 231 | 232 | function _framesSetPositionFromArray(targetLocator, obj, retries, promiseFactory) { 233 | return promiseFactory.makePromise(function (resolve) { 234 | 235 | targetLocator._logger.verbose("Switching to frame..."); 236 | targetLocator._driver.switchTo().frame(obj[retries]).then(function () { 237 | targetLocator._logger.verbose("Done!"); 238 | 239 | if (retries === 0) { 240 | resolve(); 241 | return; 242 | } 243 | 244 | targetLocator.controlFlow().then(function () { 245 | _framesSetPositionFromArray(targetLocator, obj, retries - 1, promiseFactory).then(function () { 246 | resolve(); 247 | }); 248 | }); 249 | }); 250 | }); 251 | } 252 | 253 | /** 254 | * @param {string} nameOrHandle 255 | * @return {Promise} 256 | */ 257 | EyesTargetLocator.prototype.window = function (nameOrHandle) { 258 | var that = this; 259 | this._logger.verbose("EyesTargetLocator.window()"); 260 | return this._driver.getPromiseFactory().makePromise(function (resolve) { 261 | that._logger.verbose("Making preparations.."); 262 | that._onWillSwitch.willSwitchToWindow(nameOrHandle).then(function () { 263 | that._logger.verbose("Done! Switching to window.."); 264 | return that._targetLocator.window(nameOrHandle); 265 | }).then(function () { 266 | that._logger.verbose("Done!"); 267 | resolve(); 268 | }); 269 | }); 270 | }; 271 | 272 | /** 273 | * @return {Promise} 274 | */ 275 | EyesTargetLocator.prototype.defaultContent = function () { 276 | var that = this; 277 | return this._driver.getPromiseFactory().makePromise(function (resolve) { 278 | that._logger.verbose("EyesTargetLocator.defaultContent()"); 279 | if (that._driver.getFrameChain().size() !== 0) { 280 | that._logger.verbose("Making preparations.."); 281 | that._onWillSwitch.willSwitchToFrame(EyesTargetLocator.TargetType.DEFAULT_CONTENT, null).then(function () { 282 | that._logger.verbose("Done! Switching to default content.."); 283 | return that._targetLocator.defaultContent(); 284 | }).then(function () { 285 | that._logger.verbose("Done!"); 286 | resolve(); 287 | }); 288 | } 289 | 290 | resolve(); 291 | }); 292 | }; 293 | 294 | /** 295 | * @return {Promise} 296 | */ 297 | EyesTargetLocator.prototype.activeElement = function () { 298 | var that = this; 299 | this._logger.verbose("EyesTargetLocator.activeElement()"); 300 | return this._driver.getPromiseFactory().makePromise(function (resolve) { 301 | that._logger.verbose("Switching to element.."); 302 | that._targetLocator.activeElement().then(function (element) { 303 | var result = new EyesRemoteWebElement(element, that._driver, that._logger); 304 | that._logger.verbose("Done!"); 305 | resolve(result); 306 | }) 307 | }); 308 | }; 309 | 310 | /** 311 | * @return {Promise} 312 | */ 313 | EyesTargetLocator.prototype.alert = function () { 314 | var that = this; 315 | this._logger.verbose("EyesTargetLocator.alert()"); 316 | return this._driver.getPromiseFactory().makePromise(function (resolve) { 317 | that._logger.verbose("Switching to alert.."); 318 | that._targetLocator.alert().then(function (alert) { 319 | that._logger.verbose("Done!"); 320 | resolve(alert); 321 | }); 322 | }); 323 | }; 324 | 325 | exports.EyesTargetLocator = EyesTargetLocator; 326 | }()); 327 | -------------------------------------------------------------------------------- /src/EyesWebDriver.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var webdriver = require('selenium-webdriver'), 5 | GeneralUtils = require('eyes.utils').GeneralUtils, 6 | Frame = require('./Frame').Frame, 7 | FrameChain = require('./FrameChain').FrameChain, 8 | EyesSeleniumUtils = require('./EyesSeleniumUtils').EyesSeleniumUtils, 9 | EyesRemoteWebElement = require('./EyesRemoteWebElement').EyesRemoteWebElement, 10 | ScrollPositionProvider = require('./ScrollPositionProvider').ScrollPositionProvider, 11 | EyesTargetLocator = require('./EyesTargetLocator').EyesTargetLocator; 12 | 13 | /** 14 | * Wraps a Remote Web Driver. 15 | * 16 | * @constructor 17 | * @param {WebDriver} driver 18 | * @param {Eyes} eyes An instance of Eyes 19 | * @param {Logger} logger 20 | * @mixin WebDriver 21 | **/ 22 | function EyesWebDriver(driver, eyes, logger) { 23 | this._logger = logger; 24 | this._eyes = eyes; 25 | this._driver = undefined; 26 | this.setRemoteWebDriver(driver); 27 | 28 | /** @deprecated */ 29 | this._promiseFactory = this._eyes._promiseFactory; 30 | /** @type {webdriver.By|ProtractorBy} */ 31 | this._byFunctions = eyes._isProtractorLoaded ? global.by : webdriver.By; 32 | 33 | this._frameChain = new FrameChain(this._logger, null); 34 | 35 | this._defaultContentViewportSize = null; 36 | } 37 | 38 | //noinspection JSUnusedGlobalSymbols 39 | /** 40 | * @return {Eyes} 41 | */ 42 | EyesWebDriver.prototype.getEyes = function () { 43 | return this._eyes; 44 | }; 45 | 46 | //noinspection JSUnusedGlobalSymbols 47 | /** 48 | * @return {PromiseFactory} 49 | */ 50 | EyesWebDriver.prototype.getPromiseFactory = function () { 51 | return this._eyes._promiseFactory; 52 | }; 53 | 54 | //noinspection JSUnusedGlobalSymbols 55 | /** 56 | * @return {WebDriver} 57 | */ 58 | EyesWebDriver.prototype.getRemoteWebDriver = function () { 59 | return this._driver.driver || this._driver; 60 | }; 61 | 62 | //noinspection JSUnusedGlobalSymbols 63 | EyesWebDriver.prototype.setRemoteWebDriver = function (driver) { 64 | this._driver = driver; 65 | 66 | GeneralUtils.mixin(this, driver); 67 | 68 | // TODO: is there a way to override bindings? 69 | delete this.then; 70 | delete this.catch; 71 | delete this.cancel; 72 | }; 73 | 74 | //noinspection JSUnusedGlobalSymbols 75 | EyesWebDriver.prototype.getUserAgent = function () { 76 | return this._driver.executeScript('return navigator.userAgent'); 77 | }; 78 | 79 | //noinspection JSCheckFunctionSignatures 80 | /** 81 | * @param {webdriver.By|ProtractorBy} locator 82 | * @return {EyesRemoteWebElement} 83 | */ 84 | EyesWebDriver.prototype.findElement = function (locator) { 85 | return new EyesRemoteWebElement(this._driver.findElement(locator), this, this._logger); 86 | }; 87 | 88 | //noinspection JSCheckFunctionSignatures 89 | /** 90 | * @param {webdriver.By|ProtractorBy} locator 91 | * @return {Promise} 92 | */ 93 | EyesWebDriver.prototype.findElements = function (locator) { 94 | var that = this; 95 | return this._driver.findElements(locator).then(function (elements) { 96 | return elements.map(function (element) { 97 | return new EyesRemoteWebElement(element, that, that._logger); 98 | }); 99 | }); 100 | }; 101 | 102 | //noinspection JSUnusedGlobalSymbols 103 | /** 104 | * @param {string} cssSelector 105 | * @return {EyesRemoteWebElement} 106 | */ 107 | EyesWebDriver.prototype.findElementByCssSelector = function (cssSelector) { 108 | return this.findElement(this._byFunctions.css(cssSelector)); 109 | }; 110 | 111 | //noinspection JSUnusedGlobalSymbols 112 | /** 113 | * @param {string} cssSelector 114 | * @return {Promise} 115 | */ 116 | EyesWebDriver.prototype.findElementsByCssSelector = function (cssSelector) { 117 | return this.findElements(this._byFunctions.css(cssSelector)); 118 | }; 119 | 120 | //noinspection JSUnusedGlobalSymbols 121 | /** 122 | * @param {string} name 123 | * @return {EyesRemoteWebElement} 124 | */ 125 | EyesWebDriver.prototype.findElementById = function (name) { 126 | return this.findElement(this._byFunctions.id(name)); 127 | }; 128 | 129 | //noinspection JSUnusedGlobalSymbols 130 | /** 131 | * @param {string} name 132 | * @return {Promise} 133 | */ 134 | EyesWebDriver.prototype.findElementsById = function (name) { 135 | return this.findElements(this._byFunctions.id(name)); 136 | }; 137 | 138 | //noinspection JSUnusedGlobalSymbols 139 | /** 140 | * @param {string} name 141 | * @return {EyesRemoteWebElement} 142 | */ 143 | EyesWebDriver.prototype.findElementByName = function (name) { 144 | return this.findElement(this._byFunctions.name(name)); 145 | }; 146 | 147 | //noinspection JSUnusedGlobalSymbols 148 | /** 149 | * @param {string} name 150 | * @return {Promise} 151 | */ 152 | EyesWebDriver.prototype.findElementsByName = function (name) { 153 | return this.findElements(this._byFunctions.name(name)); 154 | }; 155 | 156 | // EyesWebDriver.prototype.init = function () { 157 | // return new Promise(function (resolve) { 158 | // this._driver.getCapabilities().then(function (capabilities) { 159 | // if (!capabilities.has(webdriver.Capability.TAKES_SCREENSHOT)) { 160 | // this._screenshotTaker = new ScreenshotTaker(); 161 | // } 162 | // resolve(); 163 | // }.bind(this)); 164 | // }.bind(this)); 165 | // }; 166 | 167 | /** 168 | * @return {EyesTargetLocator} 169 | */ 170 | EyesWebDriver.prototype.switchTo = function () { 171 | var that = this; 172 | this._logger.verbose("switchTo()"); 173 | 174 | var OnWillSwitch = function () { 175 | }; 176 | 177 | /** 178 | * @param {EyesTargetLocator.TargetType} targetType 179 | * @param {EyesRemoteWebElement|WebElement} targetFrame 180 | * @return {Promise} 181 | */ 182 | OnWillSwitch.willSwitchToFrame = function (targetType, targetFrame) { 183 | that._logger.verbose("willSwitchToFrame()"); 184 | switch (targetType) { 185 | case EyesTargetLocator.TargetType.DEFAULT_CONTENT: 186 | that._logger.verbose("Default content."); 187 | that._frameChain.clear(); 188 | return that.getPromiseFactory().resolve(); 189 | case EyesTargetLocator.TargetType.PARENT_FRAME: 190 | that._logger.verbose("Parent frame."); 191 | that._frameChain.pop(); 192 | return that.getPromiseFactory().resolve(); 193 | default: // Switching into a frame 194 | that._logger.verbose("Frame"); 195 | 196 | var frameId, pl, sp, size; 197 | return targetFrame.getId() 198 | .then(function (_id) { 199 | frameId = _id; 200 | return targetFrame.getLocation(); 201 | }) 202 | .then(function (_location) { 203 | pl = _location; 204 | return targetFrame.getSize(); 205 | }) 206 | .then(function (_size) { 207 | size = _size; 208 | return new ScrollPositionProvider(that._logger, that._driver, that.getPromiseFactory()).getCurrentPosition(); 209 | }) 210 | .then(function (_scrollPosition) { 211 | sp = _scrollPosition; 212 | 213 | // Get the frame's content location. 214 | return EyesSeleniumUtils.getLocationWithBordersAddition(that._logger, targetFrame, pl, that.getPromiseFactory()); 215 | }).then(function (contentLocation) { 216 | that._frameChain.push(new Frame(that._logger, targetFrame, frameId, contentLocation, size, sp)); 217 | that._logger.verbose("Done!"); 218 | }); 219 | } 220 | }; 221 | 222 | //noinspection JSUnusedLocalSymbols 223 | OnWillSwitch.willSwitchToWindow = function (nameOrHandle) { 224 | that._logger.verbose("willSwitchToWindow()"); 225 | that._frameChain.clear(); 226 | that._logger.verbose("Done!"); 227 | return that.getPromiseFactory().resolve(); 228 | }; 229 | 230 | return new EyesTargetLocator(this._logger, this, this._driver.switchTo(), OnWillSwitch, this.getPromiseFactory()); 231 | }; 232 | 233 | /** 234 | * @param {boolean} forceQuery If true, we will perform the query even if we have a cached viewport size. 235 | * @return {Promise<{width: number, height: number}>} The viewport size of the default content (outer most frame). 236 | */ 237 | EyesWebDriver.prototype.getDefaultContentViewportSize = function (forceQuery) { 238 | var that = this; 239 | return this.getPromiseFactory().makePromise(function (resolve) { 240 | that._logger.verbose("getDefaultContentViewportSize()"); 241 | 242 | if (that._defaultContentViewportSize !== null && !forceQuery) { 243 | that._logger.verbose("Using cached viewport size: ", that._defaultContentViewportSize); 244 | resolve(that._defaultContentViewportSize); 245 | return; 246 | } 247 | 248 | var currentFrames = that.getFrameChain(); 249 | var promise = that.getPromiseFactory().resolve(); 250 | 251 | // Optimization 252 | if (currentFrames.size() > 0) { 253 | promise.then(function () { 254 | return that.switchTo().defaultContent(); 255 | }); 256 | } 257 | 258 | promise.then(function () { 259 | that._logger.verbose("Extracting viewport size..."); 260 | return EyesSeleniumUtils.getViewportSizeOrDisplaySize(that._logger, that._driver, that.getPromiseFactory()); 261 | }).then(function (viewportSize) { 262 | that._defaultContentViewportSize = viewportSize; 263 | that._logger.verbose("Done! Viewport size: ", that._defaultContentViewportSize); 264 | }); 265 | 266 | if (currentFrames.size() > 0) { 267 | promise.then(function () { 268 | return that.switchTo().frames(currentFrames); 269 | }); 270 | } 271 | 272 | promise.then(function () { 273 | resolve(that._defaultContentViewportSize); 274 | }); 275 | }); 276 | }; 277 | 278 | /** 279 | * 280 | * @return {FrameChain} A copy of the current frame chain. 281 | */ 282 | EyesWebDriver.prototype.getFrameChain = function () { 283 | return new FrameChain(this._logger, this._frameChain); 284 | }; 285 | 286 | exports.EyesWebDriver = EyesWebDriver; 287 | }()); 288 | -------------------------------------------------------------------------------- /src/Frame.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | var ArgumentGuard = require('eyes.utils').ArgumentGuard; 5 | 6 | /** 7 | * @constructor 8 | * @param {Logger} logger A Logger instance. 9 | * @param {WebElement} reference The web element for the frame, used as a reference to switch into the frame. 10 | * @param {string} frameId The id of the frame. Can be used later for comparing two frames. 11 | * @param {{x: number, y: number}} location The location of the frame within the current frame. 12 | * @param {{width: number, height: number}} size The frame element size (i.e., the size of the frame on the screen, not the internal document size). 13 | * @param {{x: number, y: number}} parentScrollPosition The scroll position the frame's parent was in when the frame was switched to. 14 | */ 15 | function Frame(logger, reference, frameId, location, size, parentScrollPosition) { 16 | ArgumentGuard.notNull(logger, "logger"); 17 | ArgumentGuard.notNull(reference, "reference"); 18 | ArgumentGuard.notNull(frameId, "frameId"); 19 | ArgumentGuard.notNull(location, "location"); 20 | ArgumentGuard.notNull(size, "size"); 21 | ArgumentGuard.notNull(parentScrollPosition, "parentScrollPosition"); 22 | 23 | logger.verbose("Frame(logger, reference, " + frameId + ", ", location, ", ", size, ", ", parentScrollPosition, ")"); 24 | 25 | this._logger = logger; 26 | this._reference = reference; 27 | this._id = frameId; 28 | this._parentScrollPosition = parentScrollPosition; 29 | this._size = size; 30 | this._location = location; 31 | } 32 | 33 | /** 34 | * @return {WebElement} 35 | */ 36 | Frame.prototype.getReference = function () { 37 | return this._reference; 38 | }; 39 | 40 | /** 41 | * @return {string} 42 | */ 43 | Frame.prototype.getId = function () { 44 | return this._id; 45 | }; 46 | 47 | /** 48 | * @return {{x: number, y: number}} 49 | */ 50 | Frame.prototype.getLocation = function () { 51 | return this._location; 52 | }; 53 | 54 | /** 55 | * @return {{width: number, height: number}} 56 | */ 57 | Frame.prototype.getSize = function () { 58 | return this._size; 59 | }; 60 | 61 | /** 62 | * @return {{x: number, y: number}} 63 | */ 64 | Frame.prototype.getParentScrollPosition = function () { 65 | return this._parentScrollPosition; 66 | }; 67 | 68 | exports.Frame = Frame; 69 | }()); 70 | -------------------------------------------------------------------------------- /src/FrameChain.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var EyesUtils = require('eyes.utils'), 5 | Frame = require('./Frame').Frame; 6 | var ArgumentGuard = EyesUtils.ArgumentGuard, 7 | GeometryUtils = EyesUtils.GeometryUtils; 8 | 9 | /** 10 | * Creates a new frame chain. 11 | * @constructor 12 | * @param {Logger} logger A Logger instance. 13 | * @param {FrameChain} other A frame chain from which the current frame chain will be created. 14 | */ 15 | function FrameChain(logger, other) { 16 | ArgumentGuard.notNull(logger, "logger"); 17 | 18 | this._logger = logger; 19 | 20 | if (other && other instanceof FrameChain) { 21 | this._logger.verbose("Frame chain copy constructor (size " + other.size() + ")"); 22 | this._frames = []; 23 | for (var i = 0, l = other.size(); i < l; i++) { 24 | this._frames.push(new Frame(logger, 25 | other.getFrames()[i].getReference(), 26 | other.getFrames()[i].getId(), 27 | other.getFrames()[i].getLocation(), 28 | other.getFrames()[i].getSize(), 29 | other.getFrames()[i].getParentScrollPosition()) 30 | ); 31 | } 32 | this._logger.verbose("Done!"); 33 | } else { 34 | this._frames = []; 35 | } 36 | } 37 | 38 | /** 39 | * Compares two frame chains. 40 | * @param {FrameChain} c1 Frame chain to be compared against c2. 41 | * @param {FrameChain} c2 Frame chain to be compared against c1. 42 | * @return {boolean} True if both frame chains represent the same frame, false otherwise. 43 | */ 44 | FrameChain.isSameFrameChain = function (c1, c2) { 45 | var lc1 = c1.size(); 46 | var lc2 = c2.size(); 47 | 48 | // different chains size means different frames 49 | if (lc1 !== lc2) { 50 | return false; 51 | } 52 | 53 | for (var i = 0; i < lc1; ++i) { 54 | if (c1.getFrames()[i].getId() !== c1.getFrames()[i].getId()) { 55 | return false; 56 | } 57 | } 58 | 59 | return true; 60 | }; 61 | 62 | /** 63 | * @return {Frame[]} frames stored in chain 64 | */ 65 | FrameChain.prototype.getFrames = function () { 66 | return this._frames; 67 | }; 68 | 69 | /** 70 | * @param {number} index Index of needed frame 71 | * @return {Frame} frame by index in array 72 | */ 73 | FrameChain.prototype.getFrame = function (index) { 74 | if (this._frames.length > index) { 75 | return this._frames[index]; 76 | } 77 | 78 | throw new Error("No frames for given index"); 79 | }; 80 | 81 | /** 82 | * 83 | * @return {number} The number of frames in the chain. 84 | */ 85 | FrameChain.prototype.size = function () { 86 | return this._frames.length; 87 | }; 88 | 89 | /** 90 | * Removes all current frames in the frame chain. 91 | */ 92 | FrameChain.prototype.clear = function () { 93 | return this._frames = []; 94 | }; 95 | 96 | /** 97 | * Removes the last inserted frame element. Practically means we switched 98 | * back to the parent of the current frame 99 | */ 100 | FrameChain.prototype.pop = function () { 101 | return this._frames.pop(); 102 | }; 103 | 104 | /** 105 | * Appends a frame to the frame chain. 106 | * @param {Frame} frame The frame to be added. 107 | */ 108 | FrameChain.prototype.push = function (frame) { 109 | return this._frames.push(frame); 110 | }; 111 | 112 | /** 113 | * @return {{x: number, y: number}} The location of the current frame in the page. 114 | */ 115 | FrameChain.prototype.getCurrentFrameOffset = function () { 116 | var result = {x: 0, y: 0}; 117 | 118 | for (var i = 0, l = this._frames.length; i < l; i++) { 119 | result = GeometryUtils.locationOffset(result, this._frames[i].getLocation()); 120 | } 121 | 122 | return result; 123 | }; 124 | 125 | /** 126 | * @return {{x: number, y: number}} The outermost frame's location, or NoFramesException. 127 | */ 128 | FrameChain.prototype.getDefaultContentScrollPosition = function () { 129 | if (this._frames.length === 0) { 130 | throw new Error("No frames in frame chain"); 131 | } 132 | return this._frames[0].getParentScrollPosition(); 133 | }; 134 | 135 | /** 136 | * @return {{width: number, height: number}} The size of the current frame. 137 | */ 138 | FrameChain.prototype.getCurrentFrameSize = function () { 139 | this._logger.verbose("getCurrentFrameSize()"); 140 | var result = this._frames[this._frames.length - 1].getSize(); 141 | this._logger.verbose("Done!"); 142 | return result; 143 | }; 144 | 145 | exports.FrameChain = FrameChain; 146 | }()); 147 | -------------------------------------------------------------------------------- /src/ScrollPositionProvider.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 'use strict'; 3 | 4 | var ArgumentGuard = require('eyes.utils').ArgumentGuard; 5 | var PositionProvider = require('eyes.sdk').PositionProvider; 6 | var EyesSeleniumUtils = require('./EyesSeleniumUtils').EyesSeleniumUtils; 7 | 8 | /** 9 | * @constructor 10 | * @param {Logger} logger A Logger instance. 11 | * @param {EyesWebDriver} executor 12 | * @param {PromiseFactory} promiseFactory 13 | * @augments PositionProvider 14 | */ 15 | function ScrollPositionProvider(logger, executor, promiseFactory) { 16 | ArgumentGuard.notNull(logger, "logger"); 17 | ArgumentGuard.notNull(executor, "executor"); 18 | 19 | this._logger = logger; 20 | this._driver = executor; 21 | this._promiseFactory = promiseFactory; 22 | } 23 | 24 | ScrollPositionProvider.prototype = new PositionProvider(); 25 | ScrollPositionProvider.prototype.constructor = ScrollPositionProvider; 26 | 27 | /** 28 | * @return {Promise<{x: number, y: number}>} The scroll position of the current frame. 29 | */ 30 | ScrollPositionProvider.prototype.getCurrentPosition = function () { 31 | var that = this; 32 | that._logger.verbose("getCurrentScrollPosition()"); 33 | return EyesSeleniumUtils.getCurrentScrollPosition(this._driver, this._promiseFactory).then(function (result) { 34 | that._logger.verbose("Current position: ", result); 35 | return result; 36 | }); 37 | }; 38 | 39 | /** 40 | * Go to the specified location. 41 | * @param {{x: number, y: number}} location The position to scroll to. 42 | * @return {Promise} 43 | */ 44 | ScrollPositionProvider.prototype.setPosition = function (location) { 45 | var that = this; 46 | that._logger.verbose("Scrolling to:", location); 47 | return EyesSeleniumUtils.scrollTo(this._driver, location, this._promiseFactory).then(function () { 48 | that._logger.verbose("Done scrolling!"); 49 | }); 50 | }; 51 | 52 | /** 53 | * @return {Promise<{width: number, height: number}>} The entire size of the container which the position is relative to. 54 | */ 55 | ScrollPositionProvider.prototype.getEntireSize = function () { 56 | var that = this; 57 | return EyesSeleniumUtils.getEntirePageSize(this._driver, this._promiseFactory).then(function (result) { 58 | that._logger.verbose("Entire size: ", result); 59 | return result; 60 | }); 61 | }; 62 | 63 | /** 64 | * @return {Promise<{x: number, y: number}>} 65 | */ 66 | ScrollPositionProvider.prototype.getState = function () { 67 | return this.getCurrentPosition(); 68 | }; 69 | 70 | /** 71 | * @param {{x: number, y: number}} state The initial state of position 72 | * @return {Promise} 73 | */ 74 | ScrollPositionProvider.prototype.restoreState = function (state) { 75 | var that = this; 76 | return this.setPosition(state).then(function () { 77 | that._logger.verbose("Position restored."); 78 | }); 79 | }; 80 | 81 | exports.ScrollPositionProvider = ScrollPositionProvider; 82 | }()); 83 | -------------------------------------------------------------------------------- /src/Target.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var GeometryUtils = require('eyes.utils').GeometryUtils; 5 | 6 | /** 7 | * @typedef {{left: number, top: number, width: number, height: number}} Region 8 | * @typedef {{left: number, top: number, width: number, height: number, 9 | * maxLeftOffset: number, maxRightOffset: number, maxUpOffset: number, maxDownOffset: number}} FloatingRegion 10 | * @typedef {{element: webdriver.WebElement|EyesRemoteWebElement|webdriver.By, 11 | * maxLeftOffset: number, maxRightOffset: number, maxUpOffset: number, maxDownOffset: number}} FloatingElement 12 | */ 13 | 14 | /** 15 | * @constructor 16 | **/ 17 | function Target(region, frame) { 18 | this._region = region; 19 | this._frame = frame; 20 | 21 | this._timeout = null; 22 | this._stitchContent = false; 23 | this._ignoreMismatch = false; 24 | this._matchLevel = null; 25 | this._ignoreCaret = null; 26 | this._ignoreRegions = []; 27 | this._floatingRegions = []; 28 | 29 | this._ignoreObjects = []; 30 | this._floatingObjects = []; 31 | } 32 | 33 | //noinspection JSUnusedGlobalSymbols 34 | /** 35 | * @param {number} ms Milliseconds to wait 36 | * @return {Target} 37 | */ 38 | Target.prototype.timeout = function (ms) { 39 | this._timeout = ms; 40 | return this; 41 | }; 42 | 43 | //noinspection JSUnusedGlobalSymbols 44 | /** 45 | * @param {boolean} [stitchContent=true] 46 | * @return {Target} 47 | */ 48 | Target.prototype.fully = function (stitchContent) { 49 | if (stitchContent !== false) { 50 | stitchContent = true; 51 | } 52 | 53 | this._stitchContent = stitchContent; 54 | return this; 55 | }; 56 | 57 | //noinspection JSUnusedGlobalSymbols 58 | /** 59 | * @param {boolean} [ignoreMismatch=true] 60 | * @return {Target} 61 | */ 62 | Target.prototype.ignoreMismatch = function (ignoreMismatch) { 63 | if (ignoreMismatch !== false) { 64 | ignoreMismatch = true; 65 | } 66 | 67 | this._ignoreMismatch = ignoreMismatch; 68 | return this; 69 | }; 70 | 71 | //noinspection JSUnusedGlobalSymbols 72 | /** 73 | * @param {MatchLevel} matchLevel 74 | * @return {Target} 75 | */ 76 | Target.prototype.matchLevel = function (matchLevel) { 77 | this._matchLevel = matchLevel; 78 | return this; 79 | }; 80 | 81 | //noinspection JSUnusedGlobalSymbols 82 | /** 83 | * @param {boolean} [ignoreCaret=true] 84 | * @return {Target} 85 | */ 86 | Target.prototype.ignoreCaret = function (ignoreCaret) { 87 | if (ignoreCaret !== false) { 88 | ignoreCaret = true; 89 | } 90 | 91 | this._ignoreCaret = ignoreCaret; 92 | return this; 93 | }; 94 | 95 | //noinspection JSUnusedGlobalSymbols 96 | /** 97 | * @param {...(Region|webdriver.WebElement|EyesRemoteWebElement|webdriver.By| 98 | * {element: (webdriver.WebElement|EyesRemoteWebElement|webdriver.By)})} ignoreRegion 99 | * @return {Target} 100 | */ 101 | Target.prototype.ignore = function (ignoreRegion) { 102 | for (var i = 0, l = arguments.length; i < l; i++) { 103 | if (!arguments[i]) { 104 | throw new Error("Ignore region can't be null or empty."); 105 | } 106 | 107 | if (GeometryUtils.isRegion(arguments[i])) { 108 | this._ignoreRegions.push(arguments[i]); 109 | } else if (arguments[i].constructor.name === "Object" && "element" in arguments[i]) { 110 | this._ignoreObjects.push(arguments[i]); 111 | } else { 112 | this._ignoreObjects.push({element: arguments[i]}); 113 | } 114 | } 115 | return this; 116 | }; 117 | 118 | //noinspection JSUnusedGlobalSymbols 119 | /** 120 | * @param {...(FloatingRegion|FloatingElement)} floatingRegion 121 | * @return {Target} 122 | */ 123 | Target.prototype.floating = function (floatingRegion) { 124 | for (var i = 0, l = arguments.length; i < l; i++) { 125 | if (!arguments[i]) { 126 | throw new Error("Floating region can't be null or empty."); 127 | } 128 | 129 | if (GeometryUtils.isRegion(arguments[i]) && 130 | "maxLeftOffset" in arguments[i] && "maxRightOffset" in arguments[i] && "maxUpOffset" in arguments[i] && "maxDownOffset" in arguments[i]) { 131 | this._floatingRegions.push(arguments[i]); 132 | } else { 133 | this._floatingObjects.push(arguments[i]); 134 | } 135 | } 136 | return this; 137 | }; 138 | 139 | /** 140 | * @return {Region|webdriver.WebElement|EyesRemoteWebElement|webdriver.By|null} 141 | */ 142 | Target.prototype.getRegion = function () { 143 | return this._region; 144 | }; 145 | 146 | /** 147 | * @return {boolean} 148 | */ 149 | Target.prototype.isUsingRegion = function () { 150 | return !!this._region; 151 | }; 152 | 153 | /** 154 | * @return {webdriver.WebElement|EyesRemoteWebElement|string|null} 155 | */ 156 | Target.prototype.getFrame = function () { 157 | return this._frame; 158 | }; 159 | 160 | /** 161 | * @return {boolean} 162 | */ 163 | Target.prototype.isUsingFrame = function () { 164 | return !!this._frame; 165 | }; 166 | 167 | /** 168 | * @return {int|null} 169 | */ 170 | Target.prototype.getTimeout = function () { 171 | return this._timeout; 172 | }; 173 | 174 | /** 175 | * @return {boolean} 176 | */ 177 | Target.prototype.getStitchContent = function () { 178 | return this._stitchContent; 179 | }; 180 | 181 | /** 182 | * @return {boolean} 183 | */ 184 | Target.prototype.getIgnoreMismatch = function () { 185 | return this._ignoreMismatch; 186 | }; 187 | 188 | /** 189 | * @return {boolean} 190 | */ 191 | Target.prototype.getMatchLevel = function () { 192 | return this._matchLevel; 193 | }; 194 | 195 | /** 196 | * @return {boolean|null} 197 | */ 198 | Target.prototype.getIgnoreCaret = function () { 199 | return this._ignoreCaret; 200 | }; 201 | 202 | /** 203 | * @return {Region[]} 204 | */ 205 | Target.prototype.getIgnoreRegions = function () { 206 | return this._ignoreRegions; 207 | }; 208 | 209 | /** 210 | * @return {{element: (webdriver.WebElement|EyesRemoteWebElement|webdriver.By)}[]} 211 | */ 212 | Target.prototype.getIgnoreObjects = function () { 213 | return this._ignoreObjects; 214 | }; 215 | 216 | /** 217 | * @return {FloatingRegion[]} 218 | */ 219 | Target.prototype.getFloatingRegions = function () { 220 | return this._floatingRegions; 221 | }; 222 | 223 | /** 224 | * @return {FloatingElement[]} 225 | */ 226 | Target.prototype.getFloatingObjects = function () { 227 | return this._floatingObjects; 228 | }; 229 | 230 | /** 231 | * Validate current window 232 | * 233 | * @return {Target} 234 | * @constructor 235 | */ 236 | Target.window = function () { 237 | return new Target(); 238 | }; 239 | 240 | /** 241 | * Validate region (in current window or frame) using region's rect, element or element's locator 242 | * 243 | * @param {Region|webdriver.WebElement|EyesRemoteWebElement|webdriver.By} region The region to validate. 244 | * @param {webdriver.WebElement|EyesRemoteWebElement|string} [frame] The element which is the frame to switch to. 245 | * @return {Target} 246 | * @constructor 247 | */ 248 | Target.region = function (region, frame) { 249 | return new Target(region, frame); 250 | }; 251 | 252 | /** 253 | * Validate frame 254 | * 255 | * @param {EyesRemoteWebElement|webdriver.WebElement|string} frame The element which is the frame to switch to. 256 | * @return {Target} 257 | * @constructor 258 | */ 259 | Target.frame = function (frame) { 260 | return new Target(null, frame); 261 | }; 262 | 263 | exports.Target = Target; 264 | }()); 265 | -------------------------------------------------------------------------------- /src/capture/EyesWebDriverScreenshot.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | var EyesSDK = require('eyes.sdk'), 5 | EyesUtils = require('eyes.utils'), 6 | ScrollPositionProvider = require('../ScrollPositionProvider').ScrollPositionProvider, 7 | FrameChain = require('../FrameChain').FrameChain; 8 | var EyesScreenshot = EyesSDK.EyesScreenshot, 9 | CoordinatesType = EyesSDK.CoordinatesType, 10 | ArgumentGuard = EyesUtils.ArgumentGuard, 11 | GeneralUtils = EyesUtils.GeneralUtils, 12 | GeometryUtils = EyesUtils.GeometryUtils; 13 | 14 | /** 15 | * @readonly 16 | * @enum {number} 17 | */ 18 | var ScreenshotType = { 19 | VIEWPORT: 1, 20 | ENTIRE_FRAME: 2 21 | }; 22 | 23 | /** 24 | * 25 | * @param {Logger} logger 26 | * @param {FrameChain} frameChain 27 | * @param {ScreenshotType} screenshotType 28 | * @return {{x: number, y: number}} 29 | */ 30 | var calcFrameLocationInScreenshot = function (logger, frameChain, screenshotType) { 31 | logger.verbose("Getting first frame.."); 32 | var firstFrame = frameChain.getFrame(0); 33 | logger.verbose("Done!"); 34 | var locationInScreenshot = GeometryUtils.createLocationFromLocation(firstFrame.getLocation()); 35 | 36 | // We only consider scroll of the default content if this is a viewport screenshot. 37 | if (screenshotType == ScreenshotType.VIEWPORT) { 38 | var defaultContentScroll = firstFrame.getParentScrollPosition(); 39 | locationInScreenshot = GeometryUtils.locationOffset(locationInScreenshot, defaultContentScroll); 40 | } 41 | 42 | logger.verbose("Iterating over frames.."); 43 | var frame; 44 | for (var i = 1, l = frameChain.size(); i < l; ++i) { 45 | logger.verbose("Getting next frame..."); 46 | frame = frameChain.getFrames()[i]; 47 | logger.verbose("Done!"); 48 | 49 | var frameLocation = frame.getLocation(); 50 | 51 | // For inner frames we must consider the scroll 52 | var frameParentScrollPosition = frame.getParentScrollPosition(); 53 | 54 | // Offsetting the location in the screenshot 55 | locationInScreenshot = GeometryUtils.locationOffset(locationInScreenshot, { 56 | x: frameLocation.x - frameParentScrollPosition.x, 57 | y: frameLocation.y - frameParentScrollPosition.y 58 | }); 59 | } 60 | 61 | logger.verbose("Done!"); 62 | return locationInScreenshot; 63 | }; 64 | 65 | /** 66 | * @param {Logger} logger A Logger instance. 67 | * @param {EyesWebDriver} driver The web driver used to get the screenshot. 68 | * @param {MutableImage} image The actual screenshot image. 69 | * @param {PromiseFactory} promiseFactory 70 | * @augments EyesScreenshot 71 | * @constructor 72 | */ 73 | function EyesWebDriverScreenshot(logger, driver, image, promiseFactory) { 74 | EyesScreenshot.call(this, image); 75 | 76 | ArgumentGuard.notNull(logger, "logger"); 77 | ArgumentGuard.notNull(driver, "driver"); 78 | 79 | this._logger = logger; 80 | this._driver = driver; 81 | this._image = image; 82 | this._promiseFactory = promiseFactory; 83 | this._frameChain = driver.getFrameChain(); 84 | 85 | EyesScreenshot.call(this._image); 86 | } 87 | 88 | EyesWebDriverScreenshot.prototype = new EyesScreenshot(); 89 | EyesWebDriverScreenshot.prototype.constructor = EyesWebDriverScreenshot; 90 | 91 | /** 92 | * @param {ScreenshotType} [screenshotType] The screenshot's type (e.g., viewport/full page). 93 | * @param {{x: number, y: number}} [frameLocationInScreenshot] The current frame's location in the screenshot. 94 | * @param {{width: number, height: number}} [frameSize] The full internal size of the frame. 95 | * @return {Promise} 96 | */ 97 | EyesWebDriverScreenshot.prototype.buildScreenshot = function (screenshotType, frameLocationInScreenshot, frameSize) { 98 | var that = this, viewportSize, imageSize; 99 | var positionProvider = new ScrollPositionProvider(this._logger, this._driver, this._promiseFactory); 100 | 101 | return this._driver.getDefaultContentViewportSize(false).then(function (vs) { 102 | viewportSize = vs; 103 | return that._image.getSize(); 104 | }).then(function (is) { 105 | imageSize = is; 106 | return positionProvider.getEntireSize(); 107 | }).then(function (ppEs) { 108 | // If we're inside a frame, then the frame size is given by the frame 109 | // chain. Otherwise, it's the size of the entire page. 110 | if (!frameSize) { 111 | if (that._frameChain.size() !== 0) { 112 | frameSize = that._frameChain.getCurrentFrameSize(); 113 | } else { 114 | // get entire page size might throw an exception for applications 115 | // which don't support Javascript (e.g., Appium). In that case 116 | // we'll use the viewport size as the frame's size. 117 | if (ppEs) { 118 | frameSize = ppEs; 119 | } else { 120 | frameSize = viewportSize; 121 | } 122 | } 123 | } 124 | 125 | return positionProvider.getCurrentPosition(); 126 | }).then(function (ppCp) { 127 | // Getting the scroll position. For native Appium apps we can't get the scroll position, so we use (0,0) 128 | if (ppCp) { 129 | that._currentFrameScrollPosition = ppCp; 130 | } else { 131 | that._currentFrameScrollPosition = GeometryUtils.createLocation(0, 0); 132 | } 133 | 134 | if (screenshotType == null) { 135 | if (imageSize.width <= viewportSize.width && imageSize.height <= viewportSize.height) { 136 | screenshotType = ScreenshotType.VIEWPORT; 137 | } else { 138 | screenshotType = ScreenshotType.ENTIRE_FRAME; 139 | } 140 | } 141 | that._screenshotType = screenshotType; 142 | 143 | // This is used for frame related calculations. 144 | if (frameLocationInScreenshot == null) { 145 | if (that._frameChain.size() > 0) { 146 | frameLocationInScreenshot = calcFrameLocationInScreenshot(that._logger, that._frameChain, that._screenshotType); 147 | } else { 148 | frameLocationInScreenshot = GeometryUtils.createLocation(0, 0); 149 | } 150 | } 151 | that._frameLocationInScreenshot = frameLocationInScreenshot; 152 | 153 | that._logger.verbose("Calculating frame window.."); 154 | that._frameWindow = GeometryUtils.createRegionFromLocationAndSize(frameLocationInScreenshot, frameSize); 155 | 156 | if (GeometryUtils.isRegionsIntersected(that._frameWindow, GeometryUtils.createRegion(0, 0, imageSize.width, imageSize.height))) { 157 | that._frameWindow = GeometryUtils.intersect(that._frameWindow, GeometryUtils.createRegion(0, 0, imageSize.width, imageSize.height)); 158 | } 159 | 160 | if (that._frameWindow.width <= 0 || that._frameWindow.height <= 0) { 161 | throw new Error("Got empty frame window for screenshot!"); 162 | } 163 | 164 | that._logger.verbose("EyesWebDriverScreenshot - Done!"); 165 | }); 166 | }; 167 | 168 | /** 169 | * @return {{left: number, top: number, width: number, height: number}} The region of the frame which is available in the screenshot, 170 | * in screenshot coordinates. 171 | */ 172 | EyesWebDriverScreenshot.prototype.getFrameWindow = function () { 173 | return this._frameWindow; 174 | }; 175 | 176 | /** 177 | * @return {FrameChain} A copy of the frame chain which was available when the 178 | * screenshot was created. 179 | */ 180 | EyesWebDriverScreenshot.prototype.getFrameChain = function () { 181 | return new FrameChain(this._logger, this._frameWindow); 182 | }; 183 | 184 | //noinspection JSUnusedGlobalSymbols 185 | /** 186 | * Returns a part of the screenshot based on the given region. 187 | * 188 | * @param {{left: number, top: number, width: number, height: number}} region The region for which we should get the sub screenshot. 189 | * @param {CoordinatesType} coordinatesType How should the region be calculated on the screenshot image. 190 | * @param {boolean} throwIfClipped Throw an EyesException if the region is not fully contained in the screenshot. 191 | * @return {Promise} A screenshot instance containing the given region. 192 | */ 193 | EyesWebDriverScreenshot.prototype.getSubScreenshot = function (region, coordinatesType, throwIfClipped) { 194 | var that = this; 195 | this._logger.verbose("getSubScreenshot(", region, ", ", coordinatesType, ", ", throwIfClipped, ")"); 196 | 197 | ArgumentGuard.notNull(region, "region"); 198 | ArgumentGuard.notNull(coordinatesType, "coordinatesType"); 199 | 200 | // We calculate intersection based on as-is coordinates. 201 | var asIsSubScreenshotRegion = this.getIntersectedRegion(region, coordinatesType, CoordinatesType.SCREENSHOT_AS_IS); 202 | 203 | var sizeFromRegion = GeometryUtils.createSizeFromRegion(region); 204 | var sizeFromSubRegion = GeometryUtils.createSizeFromRegion(asIsSubScreenshotRegion); 205 | if (GeometryUtils.isRegionEmpty(asIsSubScreenshotRegion) || (throwIfClipped && 206 | !(sizeFromRegion.height == sizeFromSubRegion.height && sizeFromRegion.width == sizeFromSubRegion.width))) { 207 | throw new Error("Region ", region, ", (", coordinatesType, ") is out of screenshot bounds ", this._frameWindow); 208 | } 209 | 210 | return this._image.cropImage(asIsSubScreenshotRegion).then(function (subScreenshotImage) { 211 | // The frame location in the sub screenshot is the negative of the 212 | // context-as-is location of the region. 213 | var contextAsIsRegionLocation = that.convertLocationFromLocation(GeometryUtils.createLocationFromRegion(asIsSubScreenshotRegion), CoordinatesType.SCREENSHOT_AS_IS, CoordinatesType.CONTEXT_AS_IS); 214 | 215 | var frameLocationInSubScreenshot = GeometryUtils.createLocation(-contextAsIsRegionLocation.x, -contextAsIsRegionLocation.y); 216 | 217 | var result = new EyesWebDriverScreenshot(that._logger, that._driver, subScreenshotImage, that._promiseFactory); 218 | return result.buildScreenshot(that._screenshotType, frameLocationInSubScreenshot, null).then(function () { 219 | that._logger.verbose("Done!"); 220 | return result; 221 | }); 222 | }); 223 | }; 224 | 225 | //noinspection JSUnusedGlobalSymbols 226 | /** 227 | * Converts a location's coordinates with the {@code from} coordinates type 228 | * to the {@code to} coordinates type. 229 | * 230 | * @param {{x: number, y: number}} location The location which coordinates needs to be converted. 231 | * @param {CoordinatesType} from The current coordinates type for {@code location}. 232 | * @param {CoordinatesType} to The target coordinates type for {@code location}. 233 | * @return {{x: number, y: number}} A new location which is the transformation of {@code location} to the {@code to} coordinates type. 234 | */ 235 | EyesWebDriverScreenshot.prototype.convertLocationFromLocation = function (location, from, to) { 236 | ArgumentGuard.notNull(location, "location"); 237 | ArgumentGuard.notNull(from, "from"); 238 | ArgumentGuard.notNull(to, "to"); 239 | 240 | var result = {x: location.x, y: location.y}; 241 | 242 | if (from == to) { 243 | return result; 244 | } 245 | 246 | // If we're not inside a frame, and the screenshot is the entire 247 | // page, then the context as-is/relative are the same (notice 248 | // screenshot as-is might be different, e.g., 249 | // if it is actually a sub-screenshot of a region). 250 | if (this._frameChain.size() == 0 && this._screenshotType == ScreenshotType.ENTIRE_FRAME) { 251 | if ((from == CoordinatesType.CONTEXT_RELATIVE 252 | || from == CoordinatesType.CONTEXT_AS_IS) 253 | && to == CoordinatesType.SCREENSHOT_AS_IS) { 254 | 255 | // If this is not a sub-screenshot, this will have no effect. 256 | result = GeometryUtils.locationOffset(result, this._frameLocationInScreenshot); 257 | 258 | } else if (from == CoordinatesType.SCREENSHOT_AS_IS && 259 | (to == CoordinatesType.CONTEXT_RELATIVE || to == CoordinatesType.CONTEXT_AS_IS)) { 260 | 261 | result = GeometryUtils.locationOffset(result, { 262 | x: -this._frameLocationInScreenshot.x, 263 | y: -this._frameLocationInScreenshot.y 264 | }); 265 | } 266 | return result; 267 | } 268 | 269 | switch (from) { 270 | case CoordinatesType.CONTEXT_AS_IS: 271 | switch (to) { 272 | case CoordinatesType.CONTEXT_RELATIVE: 273 | result = GeometryUtils.locationOffset(result, this._currentFrameScrollPosition); 274 | break; 275 | 276 | case CoordinatesType.SCREENSHOT_AS_IS: 277 | result = GeometryUtils.locationOffset(result, this._frameLocationInScreenshot); 278 | break; 279 | 280 | default: 281 | throw new Error("Cannot convert from '" + from + "' to '" + to + "'"); 282 | } 283 | break; 284 | 285 | case CoordinatesType.CONTEXT_RELATIVE: 286 | switch (to) { 287 | case CoordinatesType.SCREENSHOT_AS_IS: 288 | // First, convert context-relative to context-as-is. 289 | result = GeometryUtils.locationOffset(result, {x: -this._currentFrameScrollPosition.x, y: -this._currentFrameScrollPosition.y}); 290 | // Now convert context-as-is to screenshot-as-is. 291 | result = GeometryUtils.locationOffset(result, this._frameLocationInScreenshot); 292 | break; 293 | 294 | case CoordinatesType.CONTEXT_AS_IS: 295 | result = GeometryUtils.locationOffset(result, {x: -this._currentFrameScrollPosition.x, y: -this._currentFrameScrollPosition.y}); 296 | break; 297 | 298 | default: 299 | throw new Error("Cannot convert from '" + from + "' to '" + to + "'"); 300 | } 301 | break; 302 | 303 | case CoordinatesType.SCREENSHOT_AS_IS: 304 | switch (to) { 305 | case CoordinatesType.CONTEXT_RELATIVE: 306 | // First convert to context-as-is. 307 | result = GeometryUtils.locationOffset(result, { 308 | x: -this._frameLocationInScreenshot.x, 309 | y: -this._frameLocationInScreenshot.y 310 | }); 311 | // Now convert to context-relative. 312 | result = GeometryUtils.locationOffset(result, {x: -this._currentFrameScrollPosition.x, y: -this._currentFrameScrollPosition.y}); 313 | break; 314 | 315 | case CoordinatesType.CONTEXT_AS_IS: 316 | result = GeometryUtils.locationOffset(result, { 317 | x: -this._frameLocationInScreenshot.x, 318 | y: -this._frameLocationInScreenshot.y 319 | }); 320 | break; 321 | 322 | default: 323 | throw new Error("Cannot convert from '" + from + "' to '" + to + "'"); 324 | } 325 | break; 326 | 327 | default: 328 | throw new Error("Cannot convert from '" + from + "' to '" + to + "'"); 329 | } 330 | return result; 331 | }; 332 | 333 | //noinspection JSUnusedGlobalSymbols 334 | /** 335 | * @param {{x: number, y: number}} location 336 | * @param {CoordinatesType} coordinatesType 337 | * @return {{x: number, y: number}} 338 | */ 339 | EyesWebDriverScreenshot.prototype.getLocationInScreenshot = function (location, coordinatesType) { 340 | this._location = this.convertLocationFromLocation(location, coordinatesType, CoordinatesType.SCREENSHOT_AS_IS); 341 | 342 | // Making sure it's within the screenshot bounds 343 | if (!GeometryUtils.isRegionContainsLocation(this._frameWindow, location)) { 344 | throw new Error("Location " + location + " ('" + coordinatesType + "') is not visible in screenshot!"); 345 | } 346 | return this._location; 347 | }; 348 | 349 | //noinspection JSUnusedGlobalSymbols 350 | /** 351 | * 352 | * @param {{left: number, top: number, width: number, height: number}} region 353 | * @param {CoordinatesType} originalCoordinatesType 354 | * @param {CoordinatesType} resultCoordinatesType 355 | * @return {{left: number, top: number, width: number, height: number}} 356 | */ 357 | EyesWebDriverScreenshot.prototype.getIntersectedRegion = function (region, originalCoordinatesType, resultCoordinatesType) { 358 | if (GeometryUtils.isRegionEmpty(region)) { 359 | return GeneralUtils.clone(region); 360 | } 361 | 362 | var intersectedRegion = this.convertRegionLocation(region, originalCoordinatesType, CoordinatesType.SCREENSHOT_AS_IS); 363 | 364 | switch (originalCoordinatesType) { 365 | // If the request was context based, we intersect with the frame 366 | // window. 367 | case CoordinatesType.CONTEXT_AS_IS: 368 | case CoordinatesType.CONTEXT_RELATIVE: 369 | GeometryUtils.intersect(intersectedRegion, this._frameWindow); 370 | break; 371 | 372 | // If the request is screenshot based, we intersect with the image 373 | case CoordinatesType.SCREENSHOT_AS_IS: 374 | GeometryUtils.intersect(intersectedRegion, GeometryUtils.createRegion(0, 0, this._image.width, this._image.height)); 375 | break; 376 | 377 | default: 378 | throw new Error("Unknown coordinates type: '" + originalCoordinatesType + "'"); 379 | } 380 | 381 | // If the intersection is empty we don't want to convert the 382 | // coordinates. 383 | if (GeometryUtils.isRegionEmpty(intersectedRegion)) { 384 | return intersectedRegion; 385 | } 386 | 387 | // Converting the result to the required coordinates type. 388 | intersectedRegion = this.convertRegionLocation(intersectedRegion, CoordinatesType.SCREENSHOT_AS_IS, resultCoordinatesType); 389 | 390 | return intersectedRegion; 391 | }; 392 | 393 | //noinspection JSUnusedGlobalSymbols 394 | /** 395 | * Gets the elements region in the screenshot. 396 | * 397 | * @param {WebElement} element The element which region we want to intersect. 398 | * @return {Promise<{left: number, top: number, width: number, height: number}>} The intersected region, in {@code SCREENSHOT_AS_IS} coordinates 399 | * type. 400 | */ 401 | EyesWebDriverScreenshot.prototype.getIntersectedRegionFromElement = function (element) { 402 | ArgumentGuard.notNull(element, "element"); 403 | 404 | var pl, ds; 405 | return element.getLocation().then(function (location) { 406 | pl = location; 407 | return element.getSize(); 408 | }).then(function (size) { 409 | ds = size; 410 | 411 | // Since the element coordinates are in context relative 412 | var region = this.getIntersectedRegion(GeometryUtils.createRegionFromLocationAndSize(pl, ds), CoordinatesType.CONTEXT_RELATIVE, CoordinatesType.CONTEXT_RELATIVE); 413 | 414 | if (!GeometryUtils.isRegionEmpty(region)) { 415 | region = this.convertRegionLocation(region, CoordinatesType.CONTEXT_RELATIVE, CoordinatesType.SCREENSHOT_AS_IS); 416 | } 417 | 418 | return region; 419 | }); 420 | }; 421 | 422 | exports.EyesWebDriverScreenshot = EyesWebDriverScreenshot; 423 | }()); 424 | -------------------------------------------------------------------------------- /src/capture/FirefoxScreenshotImageProvider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { ImageProvider, MutableImage } = require('eyes.sdk'); 4 | 5 | const { EyesWebDriverScreenshot } = require('./EyesWebDriverScreenshot'); 6 | 7 | /** 8 | * This class is needed because in certain versions of firefox, a frame screenshot only brings the frame viewport. 9 | * To solve this issue, we create an image with the full size of the browser viewport and place the frame image on it 10 | * in the appropriate place. 11 | */ 12 | class FirefoxScreenshotImageProvider extends ImageProvider { 13 | /** 14 | * @param {Eyes} eyes 15 | * @param {Logger} logger 16 | * @param {EyesWebDriver} tsInstance 17 | */ 18 | constructor(eyes, logger, tsInstance) { 19 | super(); 20 | 21 | this._eyes = eyes; 22 | this._logger = logger; 23 | this._tsInstance = tsInstance; 24 | } 25 | 26 | /** 27 | * @override 28 | * @return {Promise} 29 | */ 30 | getImage() { 31 | const that = this; 32 | this._logger.verbose('Getting screenshot as base64...'); 33 | return this._tsInstance.takeScreenshot().then(screenshot64 => { 34 | that._logger.verbose('Done getting base64! Creating BufferedImage...'); 35 | const image = MutableImage.fromBase64(screenshot64, that._eyes.getPromiseFactory()); 36 | 37 | return that._eyes._promiseFactory.resolve() 38 | .then(() => { 39 | const frameChain = that._tsInstance.getFrameChain(); 40 | if (frameChain.size() > 0) { 41 | // Frame frame = frameChain.peek(); 42 | // Region region = eyes.getRegionToCheck(); 43 | const screenshot = new EyesWebDriverScreenshot( 44 | that._logger, 45 | that._tsInstance, 46 | image, 47 | that._eyes.getPromiseFactory() 48 | ); 49 | 50 | return screenshot.buildScreenshot() 51 | .then(() => that._eyes.getViewportSize()) 52 | .then(viewportSize => { 53 | const loc = screenshot.getFrameWindow().getLocation(); 54 | that._logger.verbose(`frame.getLocation(): ${loc}`); 55 | 56 | const scaleRatio = that._eyes.getDevicePixelRatio(); 57 | return image.cropImage({ 58 | left: loc.x * scaleRatio, 59 | top: loc.y * scaleRatio, 60 | width: viewportSize.width * scaleRatio, 61 | height: viewportSize.height * scaleRatio 62 | }); 63 | }); 64 | } 65 | 66 | return image; 67 | }); 68 | }); 69 | } 70 | } 71 | 72 | exports.FirefoxScreenshotImageProvider = FirefoxScreenshotImageProvider; 73 | -------------------------------------------------------------------------------- /src/capture/ImageProviderFactory.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { BrowserNames } = require('eyes.utils'); 4 | 5 | const { TakesScreenshotImageProvider } = require('./TakesScreenshotImageProvider'); 6 | const { FirefoxScreenshotImageProvider } = require('./FirefoxScreenshotImageProvider'); 7 | const { SafariScreenshotImageProvider } = require('./SafariScreenshotImageProvider'); 8 | 9 | class ImageProviderFactory { 10 | /** 11 | * @param {UserAgent} userAgent 12 | * @param {Eyes} eyes 13 | * @param {Logger} logger 14 | * @param {EyesWebDriver} driver 15 | * @return {ImageProvider} 16 | */ 17 | static getImageProvider(userAgent, eyes, logger, driver) { 18 | if (userAgent) { 19 | if (userAgent.getBrowser() === BrowserNames.Firefox) { 20 | try { 21 | if (parseInt(userAgent.getBrowserMajorVersion(), 10) >= 48) { 22 | return new FirefoxScreenshotImageProvider(eyes, logger, driver); 23 | } 24 | } catch (ignored) { 25 | return new TakesScreenshotImageProvider(logger, driver); 26 | } 27 | } else if (userAgent.getBrowser() === BrowserNames.Safari) { 28 | return new SafariScreenshotImageProvider(eyes, logger, driver, userAgent); 29 | } 30 | } 31 | return new TakesScreenshotImageProvider(logger, driver); 32 | } 33 | } 34 | 35 | exports.ImageProviderFactory = ImageProviderFactory; 36 | -------------------------------------------------------------------------------- /src/capture/SafariScreenshotImageProvider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { OSNames } = require('eyes.utils'); 4 | const { ImageProvider, MutableImage } = require('eyes.sdk'); 5 | 6 | const { ScrollPositionProvider } = require('../ScrollPositionProvider'); 7 | 8 | class SafariScreenshotImageProvider extends ImageProvider { 9 | /** 10 | * @param {Eyes} eyes 11 | * @param {Logger} logger A Logger instance. 12 | * @param {EyesWebDriver} tsInstance 13 | * @param {UserAgent} userAgent 14 | */ 15 | constructor(eyes, logger, tsInstance, userAgent) { 16 | super(); 17 | 18 | this._eyes = eyes; 19 | this._logger = logger; 20 | this._tsInstance = tsInstance; 21 | this._userAgent = userAgent; 22 | this._jsExecutor = tsInstance; 23 | } 24 | 25 | /** 26 | * @override 27 | * @return {Promise} 28 | */ 29 | getImage() { 30 | let /** @type MutableImage */ image, viewportSize; 31 | const that = this; 32 | that._logger.verbose('Getting screenshot as base64...'); 33 | return that._tsInstance 34 | .takeScreenshot() 35 | .then(screenshot64 => { 36 | screenshot64 = screenshot64.replace(/\r\n/g, ''); // Because of SauceLabs returns image with line breaks 37 | 38 | that._logger.verbose('Done getting base64! Creating MutableImage...'); 39 | image = MutableImage.fromBase64(screenshot64, that._eyes._promiseFactory); 40 | }) 41 | .then(() => { 42 | if (that._eyes.getIsCutProviderExplicitlySet()) { 43 | return image; 44 | } 45 | 46 | const scaleRatio = that._eyes.getDevicePixelRatio(); 47 | return that._eyes.getViewportSize().then(originalViewportSize => { 48 | viewportSize = { 49 | width: Math.ceil(originalViewportSize.width * scaleRatio), 50 | height: Math.ceil(originalViewportSize.height * scaleRatio) 51 | }; 52 | 53 | that._logger.verbose(`logical viewport size: ${originalViewportSize}`); 54 | 55 | if (that._userAgent.getOS() === OSNames.IOS) { 56 | let topBarHeight = 20; 57 | let leftBarWidth = 0; 58 | let bottomBarHeight = 44; 59 | let rightBarWidth = 0; 60 | let urlBarHeight = 44; 61 | 62 | const imageWidth = image.getWidth(); 63 | const imageHeight = image.getHeight(); 64 | const displayLogicalWidth = Math.ceil(imageWidth / scaleRatio); 65 | const displayLogicalHeight = Math.ceil(imageHeight / scaleRatio); 66 | 67 | that._logger.verbose(`physical device pixel size: ${imageWidth} x ${imageHeight}`); 68 | that._logger.verbose(`physical device logical size: ${displayLogicalWidth} x ${displayLogicalHeight}`); 69 | 70 | if (displayLogicalHeight === 736 && displayLogicalWidth === 414) { // iPhone 5.5 inch 71 | that._logger.verbose('iPhone 5.5 inch detected'); 72 | topBarHeight = 18; 73 | } else if (displayLogicalHeight === 812 && displayLogicalWidth === 375) { // iPhone 5.8 inch p 74 | that._logger.verbose('iPhone 5.8 inch portrait detected'); 75 | topBarHeight = 44; 76 | bottomBarHeight = 83; 77 | } else if (displayLogicalWidth === 812 && displayLogicalHeight === 375) { // iPhone 5.8 inch l 78 | that._logger.verbose('iPhone 5.8 inch landscape detected'); 79 | leftBarWidth = 44; 80 | rightBarWidth = 44; 81 | } 82 | 83 | if (displayLogicalHeight < displayLogicalWidth) { 84 | that._logger.verbose('landscape mode detected'); 85 | topBarHeight = 0; 86 | if (displayLogicalWidth === 812 && displayLogicalHeight === 375) { // on iPhone X crop the home indicator. 87 | bottomBarHeight = 15; 88 | } else { 89 | bottomBarHeight = 0; 90 | } 91 | 92 | // on iPhone 5.5 inch with Safari 10 in landscape mode crop the tabs bar. 93 | if (displayLogicalWidth === 736 && displayLogicalHeight === 414 && 94 | parseInt(that._userAgent.getBrowserMajorVersion(), 10) < 11) { 95 | topBarHeight = 33; 96 | } 97 | } 98 | 99 | if (parseInt(that._userAgent.getBrowserMajorVersion(), 10) >= 11) { // Safari >= 11 100 | that._logger.verbose('safari version 11 or higher detected'); 101 | urlBarHeight = 50; 102 | } 103 | 104 | viewportSize = { 105 | width: Math.ceil(imageWidth - (leftBarWidth + rightBarWidth) * scaleRatio), 106 | height: Math.ceil(imageHeight - (topBarHeight + urlBarHeight + bottomBarHeight) * scaleRatio) 107 | }; 108 | 109 | that._logger.verbose(`computed physical viewport size: ${viewportSize}`); 110 | that._logger.verbose('cropping IOS browser image'); 111 | 112 | return image.cropImage({ 113 | left: Math.ceil(leftBarWidth * scaleRatio), 114 | top: Math.ceil((topBarHeight + urlBarHeight) * scaleRatio), 115 | width: viewportSize.width, 116 | height: viewportSize.height 117 | }); 118 | } 119 | 120 | if (!that._eyes.getForceFullPageScreenshot()) { 121 | const currentFrameChain = that._eyes.getDriver().getFrameChain(); 122 | 123 | let promise; 124 | if (currentFrameChain.size() === 0) { 125 | const positionProvider = new ScrollPositionProvider(that._logger, that._jsExecutor, that._eyes._promiseFactory); 126 | promise = positionProvider.getCurrentPosition(); 127 | } else { 128 | promise = that._eyes.getPromiseFactory().resolve(currentFrameChain.getDefaultContentScrollPosition()); 129 | } 130 | 131 | return promise.then(loc => image.cropImage({ 132 | left: loc.x * scaleRatio, 133 | top: loc.y * scaleRatio, 134 | width: viewportSize.width, 135 | height: viewportSize.height 136 | })); 137 | } 138 | 139 | return image; 140 | }); 141 | }); 142 | } 143 | } 144 | 145 | exports.SafariScreenshotImageProvider = SafariScreenshotImageProvider; 146 | -------------------------------------------------------------------------------- /src/capture/TakesScreenshotImageProvider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { ImageProvider, MutableImage } = require('eyes.sdk'); 4 | 5 | /** 6 | * An image provider based on WebDriver's interface. 7 | */ 8 | class TakesScreenshotImageProvider extends ImageProvider { 9 | /** 10 | * @param {Logger} logger A Logger instance. 11 | * @param {EyesWebDriver} tsInstance 12 | */ 13 | constructor(logger, tsInstance) { 14 | super(); 15 | 16 | this._logger = logger; 17 | this._tsInstance = tsInstance; 18 | } 19 | 20 | /** 21 | * @override 22 | * @return {Promise} 23 | */ 24 | getImage() { 25 | this._logger.verbose('Getting screenshot as base64...'); 26 | 27 | const that = this; 28 | return this._tsInstance.takeScreenshot().then(screenshot64 => { 29 | that._logger.verbose('Done getting base64! Creating MutableImage...'); 30 | return MutableImage.fromBase64(screenshot64, that._tsInstance.getPromiseFactory()); 31 | }); 32 | } 33 | } 34 | 35 | exports.TakesScreenshotImageProvider = TakesScreenshotImageProvider; 36 | -------------------------------------------------------------------------------- /test/Eyes.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var assert = require('assert'); 4 | var webdriver = require('selenium-webdriver'); 5 | var EyesSDK = require('../index'); 6 | 7 | var driver, eyes; 8 | describe('Eyes', function () { 9 | 10 | this.timeout(5 * 60 * 1000); 11 | 12 | before(function () { 13 | driver = new webdriver.Builder() 14 | .forBrowser('chrome') 15 | .build(); 16 | 17 | eyes = new EyesSDK.Eyes(); 18 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 19 | eyes._promiseFactory.setFactoryMethods(function (asyncAction) { 20 | return new Promise(asyncAction); 21 | }, undefined); 22 | }); 23 | 24 | describe('#open()', function () { 25 | it('should return EyesWebDriver', function () { 26 | return eyes.open(driver, this.test.parent.title, this.test.title, { 27 | width: 800, 28 | height: 560 29 | }).then(function (driver) { 30 | assert.strictEqual(driver instanceof EyesSDK.EyesWebDriver, true); 31 | return eyes.close(); 32 | }); 33 | }); 34 | 35 | it('should throw IllegalState: Eyes not open', function () { 36 | return eyes.check('test', EyesSDK.Target.window()).catch(function (error) { 37 | assert.strictEqual(error.message, 'checkWindow called with Eyes not open'); 38 | }); 39 | }); 40 | }); 41 | 42 | describe('#WebDriver', function () { 43 | it('type should be return thenableWebDriverProxy', function () { 44 | assert.strictEqual(driver.constructor.name, "thenableWebDriverProxy"); 45 | 46 | return eyes._promiseFactory.resolve(driver).then(function (value) { 47 | assert.strictEqual(value.constructor.name, "Driver"); 48 | }); 49 | }); 50 | 51 | it('type should be return EyesWebDriver', function () { 52 | var eyesDriver = new EyesSDK.EyesWebDriver(driver, eyes, eyes._logger); 53 | assert.strictEqual(eyesDriver.constructor.name, "EyesWebDriver"); 54 | 55 | return eyes._promiseFactory.resolve(eyesDriver).then(function (value) { 56 | assert.strictEqual(value.constructor.name, "EyesWebDriver"); 57 | }); 58 | }); 59 | }); 60 | 61 | describe('#WebElement', function () { 62 | var eyesDriver; 63 | before(function () { 64 | return eyes.open(driver, this.test.parent.title, this.test.title, { 65 | width: 800, 66 | height: 560 67 | }).then(function (driver) { 68 | eyesDriver = driver; 69 | }); 70 | }); 71 | 72 | after(function () { 73 | return eyes.close(); 74 | }); 75 | 76 | it('type should be return WebElement', function () { 77 | driver.get('https://astappiev.github.io/test-html-pages/'); 78 | 79 | var element = driver.findElement(webdriver.By.css("body > h1")); 80 | return element.then(function (value) { 81 | assert.strictEqual(value.constructor.name, "WebElement"); 82 | }) 83 | }); 84 | 85 | it('type should be return WebElement (due to WebElementPromise)', function () { 86 | eyesDriver.get('https://astappiev.github.io/test-html-pages/'); 87 | 88 | var element = eyesDriver.findElementByCssSelector("body > h1"); 89 | return element.then(function (value) { 90 | assert.strictEqual(value.constructor.name, "WebElement"); 91 | }) 92 | }); 93 | }); 94 | 95 | after(function () { 96 | return driver.quit().then(function (value) { 97 | return eyes.abortIfNotClosed(); 98 | }); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /test/IOSTest.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { Builder, Capabilities, By } = require('selenium-webdriver'); 4 | const { ConsoleLogHandler } = require('eyes.sdk'); 5 | const { TestUtils } = require('./TestUtils'); 6 | const { Eyes, Target, StitchMode } = require('../index'); 7 | 8 | describe('IOSTest', function () { 9 | this.timeout(10 * 60 * 1000); 10 | 11 | const batchInfo = 'Java3 Tests'; 12 | 13 | const dataProvider = []; 14 | dataProvider.push(...TestUtils.cartesianProduct( 15 | 'iPhone X Simulator', 16 | ['portrait', 'landscape'], 17 | '11.0', 18 | [false, true] 19 | )); 20 | 21 | // dataProvider.push(...TestUtils.cartesianProduct( 22 | // ['iPhone 7 Simulator', 'iPhone 6 Plus Simulator'], 23 | // ['portrait', 'landscape'], 24 | // ['10.0', '11.0'], 25 | // [false, true] 26 | // )); 27 | 28 | dataProvider.forEach(row => { 29 | const [deviceName, deviceOrientation, platformVersion, fully] = row; 30 | 31 | let testName = `${deviceName} ${platformVersion} ${deviceOrientation}`; 32 | if (fully) testName += ' fully'; 33 | 34 | it(testName, function () { 35 | const eyes = new Eyes(); 36 | eyes.setBatch(batchInfo); 37 | 38 | const caps = Capabilities.iphone(); 39 | caps.set('appiumVersion', '1.7.2'); 40 | caps.set('deviceName', deviceName); 41 | caps.set('deviceOrientation', deviceOrientation); 42 | caps.set('platformVersion', platformVersion); 43 | caps.set('platformName', 'iOS'); 44 | caps.set('browserName', 'Safari'); 45 | 46 | caps.set('username', process.env.SAUCE_USERNAME); 47 | caps.set('accesskey', process.env.SAUCE_ACCESS_KEY); 48 | 49 | const sauceUrl = 'http://ondemand.saucelabs.com/wd/hub'; 50 | const driver = new Builder().withCapabilities(caps).usingServer(sauceUrl).build(); 51 | 52 | eyes.setLogHandler(new ConsoleLogHandler(true)); 53 | eyes.setStitchMode(StitchMode.Scroll); 54 | 55 | eyes.addProperty('Orientation', deviceOrientation); 56 | eyes.addProperty('Stitched', fully ? 'True' : 'False'); 57 | 58 | return eyes.open(driver, 'Eyes Selenium SDK - iOS Safari Cropping', testName).then(driver => { 59 | driver.get('https://www.applitools.com/customers'); 60 | 61 | eyes.check('Initial view', Target.region(By.css('body')).fully(fully)); 62 | return eyes.close(); 63 | }).then(() => { 64 | eyes.abortIfNotClosed(); 65 | 66 | return driver.quit(); 67 | }); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /test/TestUtils.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * Collection of utility methods. 5 | */ 6 | class TestUtils { 7 | /** 8 | * Generates the cartesian product of the sets. 9 | * 10 | * @param {...(Array|Object)} sets - variable number of sets of n elements. 11 | * @return {Generator} yields each product as an array 12 | */ 13 | static* cartesianProduct(...sets) { 14 | const data = []; 15 | 16 | function* cartesianUtil(index) { 17 | if (index === sets.length) { 18 | return yield data.slice(); 19 | } 20 | 21 | if (!Array.isArray(sets[index])) { 22 | data[index] = sets[index]; 23 | yield* cartesianUtil(index + 1); 24 | } else { 25 | for (let i = 0; i < sets[index].length; i += 1) { 26 | data[index] = sets[index][i]; 27 | yield* cartesianUtil(index + 1); 28 | } 29 | } 30 | } 31 | 32 | yield* cartesianUtil(0); 33 | } 34 | } 35 | 36 | exports.TestUtils = TestUtils; 37 | -------------------------------------------------------------------------------- /test/appium/android-native-sause-test.js: -------------------------------------------------------------------------------- 1 | var webdriver = require('selenium-webdriver'); 2 | var Builder = webdriver.Builder; 3 | 4 | var SeleniumSDK = require('../../index'); 5 | var Eyes = SeleniumSDK.Eyes; 6 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 7 | 8 | var serverUrl = "http://" + process.env.SAUCE_USERNAME + ":" + process.env.SAUCE_ACCESS_KEY + "@ondemand.saucelabs.com:80/wd/hub"; 9 | 10 | var driver = null, eyes = null; 11 | describe('Eyes.Selenium.JavaScript - Android Native Appium via SauseLab', function () { 12 | 13 | this.timeout(5 * 60 * 1000); 14 | 15 | before(function () { 16 | driver = new Builder() 17 | .withCapabilities({ 18 | 'platformName': 'Android', 19 | 'deviceName': 'android-24-google_apis-x86_64-v24.4.1-wd-manager', 20 | 'platformVersion': '6.0', 21 | 'app': 'http://saucelabs.com/example_files/ContactManager.apk', 22 | 'browserName': '', 23 | 'clearSystemFiles': 'true', 24 | 'noReset': 'true' 25 | }) 26 | .usingServer(serverUrl) 27 | .build(); 28 | 29 | eyes = new Eyes(); 30 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 31 | eyes.setLogHandler(new ConsoleLogHandler(true)); 32 | }); 33 | 34 | beforeEach(function () { 35 | var appName = this.test.parent.title; 36 | var testName = this.currentTest.title; 37 | 38 | return eyes.open(driver, appName, testName).then(function (browser) { 39 | driver = browser; 40 | }); 41 | }); 42 | 43 | it("check window base", function () { 44 | 45 | eyes.checkWindow("Contact list!"); 46 | 47 | return eyes.close(); 48 | }); 49 | 50 | afterEach(function () { 51 | return driver.quit().then(function () { 52 | return eyes.abortIfNotClosed(); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/appium/ios-native-sause-test.js: -------------------------------------------------------------------------------- 1 | var webdriver = require('selenium-webdriver'); 2 | var Builder = webdriver.Builder; 3 | 4 | var SeleniumSDK = require('../../index'); 5 | var Eyes = SeleniumSDK.Eyes; 6 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 7 | 8 | var serverUrl = "http://" + process.env.SAUCE_USERNAME + ":" + process.env.SAUCE_ACCESS_KEY + "@ondemand.saucelabs.com:80/wd/hub"; 9 | 10 | var driver = null, eyes = null; 11 | describe('Eyes.Selenium.JavaScript - IOS Native Appium via SauseLab', function () { 12 | 13 | this.timeout(5 * 60 * 1000); 14 | 15 | before(function () { 16 | driver = new Builder() 17 | .withCapabilities({ 18 | 'platformName': 'iOS', 19 | 'deviceName': 'iPhone 7 Simulator', 20 | 'platformVersion': '10.0', 21 | 'app': 'https://store.applitools.com/download/iOS.TestApp.app.zip', 22 | 'browserName': '', 23 | 'clearSystemFiles': 'true', 24 | 'noReset': 'true' 25 | }) 26 | .usingServer(serverUrl) 27 | .build(); 28 | 29 | eyes = new Eyes(); 30 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 31 | eyes.setLogHandler(new ConsoleLogHandler(true)); 32 | }); 33 | 34 | beforeEach(function () { 35 | var appName = this.test.parent.title; 36 | var testName = this.currentTest.title; 37 | 38 | return eyes.open(driver, appName, testName).then(function (browser) { 39 | driver = browser; 40 | }); 41 | }); 42 | 43 | it("check window base", function () { 44 | 45 | eyes.checkWindow("Entire window"); 46 | 47 | return eyes.close(); 48 | }); 49 | 50 | afterEach(function () { 51 | return driver.quit().then(function () { 52 | return eyes.abortIfNotClosed(); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /test/appium/ios-simple-sause-test.js: -------------------------------------------------------------------------------- 1 | var webdriver = require('selenium-webdriver'); 2 | var Builder = webdriver.Builder; 3 | 4 | var SeleniumSDK = require('../../index'); 5 | var Eyes = SeleniumSDK.Eyes; 6 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 7 | 8 | var serverUrl = "http://" + process.env.SAUCE_USERNAME + ":" + process.env.SAUCE_ACCESS_KEY + "@ondemand.saucelabs.com:80/wd/hub"; 9 | 10 | var driver = null, eyes = null; 11 | describe('Eyes.Selenium.JavaScript - IOS Simple Appium via SauseLab', function () { 12 | 13 | this.timeout(5 * 60 * 1000); 14 | 15 | before(function () { 16 | driver = new Builder() 17 | /*.withCapabilities({ 18 | 'screenResolution': '1600x1200', 19 | 'version': '10.0', 20 | 'platform': 'macOS 10.12', 21 | 'browserName': 'Safari' 22 | })*/ 23 | .withCapabilities({ 24 | 'appiumVersion': '1.6.4', 25 | 'deviceName': 'iPhone SE Simulator', 26 | 'deviceOrientation': 'landscape', 27 | 'platformVersion': '10.3', 28 | 'platformName': 'iOS', 29 | 'browserName': 'Safari' 30 | }) 31 | .usingServer(serverUrl) 32 | .build(); 33 | 34 | eyes = new Eyes(); 35 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 36 | eyes.setLogHandler(new ConsoleLogHandler(true)); 37 | }); 38 | 39 | beforeEach(function () { 40 | var appName = this.test.parent.title; 41 | var testName = this.currentTest.title; 42 | 43 | return eyes.open(driver, appName, testName).then(function (browser) { 44 | driver = browser; 45 | }); 46 | }); 47 | 48 | it("check window base", function () { 49 | driver.get('https://astappiev.github.io/test-html-pages/'); 50 | 51 | eyes.checkWindow("Entire window"); 52 | 53 | return eyes.close(); 54 | }); 55 | 56 | afterEach(function () { 57 | return driver.quit().then(function () { 58 | return eyes.abortIfNotClosed(); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/protractor/check-interface-test.js: -------------------------------------------------------------------------------- 1 | var SeleniumSDK = require('../../index'); 2 | var Eyes = SeleniumSDK.Eyes; 3 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 4 | var StitchMode = SeleniumSDK.StitchMode; 5 | var Target = SeleniumSDK.Target; 6 | var MatchLevel = SeleniumSDK.MatchLevel; 7 | 8 | var eyes; 9 | 10 | describe("Eyes.Selenium.JavaScript - Protractor", function() { 11 | 12 | beforeAll(function(){ 13 | eyes = new Eyes(); 14 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 15 | eyes.setLogHandler(new ConsoleLogHandler(true)); 16 | eyes.getLogHandler().setPrintSessionId(true); 17 | eyes.setStitchMode(StitchMode.CSS); 18 | eyes.setForceFullPageScreenshot(true); 19 | }); 20 | 21 | beforeEach(function(done){ 22 | eyes.open(browser, global.appName, global.testName, {width: 1000, height: 700}).then(function () { 23 | done(); 24 | }); 25 | }); 26 | 27 | it("check interface", function(done) { 28 | browser.get("https://astappiev.github.io/test-html-pages/"); 29 | 30 | // Entire window, equivalent to eyes.checkWindow() 31 | eyes.check("Entire window", Target.window() 32 | .matchLevel(MatchLevel.Layout) 33 | .ignore(by.id("overflowing-div")) 34 | .ignore({element: element(by.name("frame1"))}) 35 | .ignore({left: 400, top: 100, width: 50, height: 50}, {left: 400, top: 200, width: 50, height: 100}) 36 | .floating({left: 500, top: 100, width: 75, height: 100, maxLeftOffset: 25, maxRightOffset: 10, maxUpOffset: 30, maxDownOffset: 15}) 37 | .floating({element: by.id("overflowing-div-image"), maxLeftOffset: 5, maxRightOffset: 25, maxUpOffset: 10, maxDownOffset: 25}) 38 | // .floating({element: element(by.tagName("h1")), maxLeftOffset: 10, maxRightOffset: 10, maxUpOffset: 10, maxDownOffset: 10}) 39 | ); 40 | 41 | // Region by rect, equivalent to eyes.checkFrame() 42 | eyes.check("Region by rect", Target.region({left: 50, top: 50, width: 200, height: 200}) 43 | // .floating({left: 50, top: 50, width: 60, height: 50, maxLeftOffset: 10, maxRightOffset: 10, maxUpOffset: 10, maxDownOffset: 10}) 44 | // .floating({left: 150, top: 75, width: 60, height: 50, maxLeftOffset: 10, maxRightOffset: 10, maxUpOffset: 10, maxDownOffset: 10}) 45 | ); 46 | 47 | // Region by element, equivalent to eyes.checkRegionByElement() 48 | eyes.check("Region by element", Target.region(element(by.css("body > h1")))); 49 | 50 | // Region by locator, equivalent to eyes.checkRegionBy() 51 | eyes.check("Region by locator", Target.region(by.id("overflowing-div-image"))); 52 | 53 | // Entire element by element, equivalent to eyes.checkElement() 54 | eyes.check("Entire element by element", Target.region(element(by.id("overflowing-div-image"))).fully()); 55 | 56 | // Entire element by locator, equivalent to eyes.checkElementBy() 57 | eyes.check("Entire element by locator", Target.region(by.id("overflowing-div")).fully().matchLevel(MatchLevel.Exact)); 58 | 59 | // Entire frame by locator, equivalent to eyes.checkFrame() 60 | eyes.check("Entire frame by locator", Target.frame(by.name("frame1"))); 61 | 62 | // Entire region in frame by frame name and region locator, equivalent to eyes.checkRegionInFrame() 63 | eyes.check("Entire region in frame by frame name and region locator", Target.region(by.id("inner-frame-div"), "frame1").fully()); 64 | 65 | eyes.close().then(function () { 66 | done(); 67 | }); 68 | }); 69 | 70 | afterEach(function(done) { 71 | eyes.abortIfNotClosed().then(function () { 72 | done(); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /test/protractor/simple-protractor-test.js: -------------------------------------------------------------------------------- 1 | var SeleniumSDK = require('../../index'); 2 | var Eyes = SeleniumSDK.Eyes; 3 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 4 | var FixedCutProvider = SeleniumSDK.FixedCutProvider; 5 | 6 | var eyes; 7 | 8 | describe("Eyes.Selenium.JavaScript - Protractor", function() { 9 | 10 | beforeAll(function(){ 11 | eyes = new Eyes(); 12 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 13 | eyes.setLogHandler(new ConsoleLogHandler(true)); 14 | eyes.getLogHandler().setPrintSessionId(true); 15 | }); 16 | 17 | beforeEach(function(done){ 18 | eyes.open(browser, global.appName, global.testName, {width: 800, height: 560}).then(function () { 19 | done(); 20 | }); 21 | }); 22 | 23 | it("simple protractor", function(done) { 24 | browser.get("https://astappiev.github.io/test-html-pages/"); 25 | 26 | eyes.addProperty("MyProp", "I'm correct!"); 27 | 28 | // cut params: header, footer, left, right. 29 | eyes.setImageCut(new FixedCutProvider(60, 100, 50, 120)); 30 | 31 | eyes.checkWindow("Entire window"); 32 | 33 | element(by.name("name")).sendKeys("Test User"); 34 | element(by.name("email")).sendKeys("username@example.com"); 35 | element(by.id("submit-form")).click(); 36 | 37 | eyes.checkWindow("Entire window with cut borders"); 38 | 39 | eyes.close().then(function () { 40 | done(); 41 | }); 42 | }); 43 | 44 | afterEach(function(done) { 45 | eyes.abortIfNotClosed().then(function () { 46 | done(); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/selenium/check-ignore-region-test.js: -------------------------------------------------------------------------------- 1 | require('chromedriver'); 2 | var webdriver = require('selenium-webdriver'); 3 | var Builder = webdriver.Builder; 4 | 5 | var SeleniumSDK = require('../../index'); 6 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 7 | var Target = SeleniumSDK.Target; 8 | var Eyes = SeleniumSDK.Eyes; 9 | 10 | var driver = null, eyes = null; 11 | describe('Eyes.Selenium.JavaScript - Selenium', function () { 12 | 13 | this.timeout(5 * 60 * 1000); 14 | 15 | before(function () { 16 | driver = new Builder().forBrowser('chrome').build(); 17 | 18 | eyes = new Eyes(); 19 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 20 | eyes.setLogHandler(new ConsoleLogHandler(true)); 21 | eyes.getLogHandler().setPrintSessionId(true); 22 | }); 23 | 24 | beforeEach(function () { 25 | var appName = this.test.parent.title; 26 | var testName = this.currentTest.title; 27 | 28 | return eyes.open(driver, appName, testName, {width: 800, height: 560}).then(function (browser) { 29 | driver = browser; 30 | }); 31 | }); 32 | 33 | it('TestCheckElementWithIgnoreRegion', function () { 34 | driver.get('http://applitools.github.io/demo/TestPages/FramesTestPage/'); 35 | 36 | eyes.check("Same element", Target.region(webdriver.By.id("overflowing-div-image")).ignore(webdriver.By.id("overflowing-div-image"))); 37 | 38 | eyes.check("Outside the viewport", Target.region(webdriver.By.id("overflowing-div-image")).ignore(webdriver.By.id("overflowing-div"))); 39 | 40 | return eyes.close(); 41 | }); 42 | 43 | afterEach(function () { 44 | return driver.quit().then(function () { 45 | return eyes.abortIfNotClosed(); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /test/selenium/check-interface-test.js: -------------------------------------------------------------------------------- 1 | require('chromedriver'); 2 | var webdriver = require('selenium-webdriver'); 3 | var Builder = webdriver.Builder; 4 | var By = webdriver.By; 5 | 6 | var SeleniumSDK = require('../../index'); 7 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 8 | var MatchLevel = SeleniumSDK.MatchLevel; 9 | var Eyes = SeleniumSDK.Eyes; 10 | var Target = SeleniumSDK.Target; 11 | 12 | var driver = null, eyes = null; 13 | describe('Eyes.Selenium.JavaScript - Selenium', function () { 14 | 15 | this.timeout(5 * 60 * 1000); 16 | 17 | before(function () { 18 | driver = new Builder() 19 | .forBrowser('chrome') 20 | //.usingServer('http://localhost:4444/wd/hub') 21 | .build(); 22 | 23 | eyes = new Eyes(); 24 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 25 | eyes.setLogHandler(new ConsoleLogHandler(true)); 26 | eyes.getLogHandler().setPrintSessionId(true); 27 | eyes.setStitchMode(Eyes.StitchMode.CSS); 28 | eyes.setForceFullPageScreenshot(true); 29 | }); 30 | 31 | beforeEach(function () { 32 | var appName = this.test.parent.title; 33 | var testName = this.currentTest.title; 34 | 35 | return eyes.open(driver, appName, testName, {width: 1000, height: 700}).then(function (browser) { 36 | driver = browser; 37 | }); 38 | }); 39 | 40 | it("check interface", function () { 41 | driver.get("https://astappiev.github.io/test-html-pages/"); 42 | 43 | // Entire window, equivalent to eyes.checkWindow() 44 | eyes.check("Entire window", Target.window() 45 | .matchLevel(MatchLevel.Layout) 46 | .ignore(By.id("overflowing-div")) 47 | .ignore({element: driver.findElement(By.name("frame1"))}) 48 | .ignore({left: 400, top: 100, width: 50, height: 50}, {left: 400, top: 200, width: 50, height: 100}) 49 | .floating({left: 500, top: 100, width: 75, height: 100, maxLeftOffset: 25, maxRightOffset: 10, maxUpOffset: 30, maxDownOffset: 15}) 50 | .floating({element: By.id("overflowing-div-image"), maxLeftOffset: 5, maxRightOffset: 25, maxUpOffset: 10, maxDownOffset: 25}) 51 | // .floating({element: driver.findElement(By.tagName("h1")), maxLeftOffset: 10, maxRightOffset: 10, maxUpOffset: 10, maxDownOffset: 10}) 52 | ); 53 | 54 | // Region by rect, equivalent to eyes.checkFrame() 55 | eyes.check("Region by rect", Target.region({left: 50, top: 50, width: 200, height: 200}) 56 | // .floating({left: 50, top: 50, width: 60, height: 50, maxLeftOffset: 10, maxRightOffset: 10, maxUpOffset: 10, maxDownOffset: 10}) 57 | // .floating({left: 150, top: 75, width: 60, height: 50, maxLeftOffset: 10, maxRightOffset: 10, maxUpOffset: 10, maxDownOffset: 10}) 58 | ); 59 | 60 | // Region by element, equivalent to eyes.checkRegionByElement() 61 | eyes.check("Region by element", Target.region(driver.findElement(By.css("body > h1")))); 62 | 63 | // Region by locator, equivalent to eyes.checkRegionBy() 64 | eyes.check("Region by locator", Target.region(By.id("overflowing-div-image"))); 65 | 66 | // Entire element by element, equivalent to eyes.checkElement() 67 | eyes.check("Entire element by element", Target.region(driver.findElement(By.id("overflowing-div-image"))).fully()); 68 | 69 | // Entire element by locator, equivalent to eyes.checkElementBy() 70 | eyes.check("Entire element by locator", Target.region(By.id("overflowing-div")).fully()); 71 | 72 | // Entire frame by locator, equivalent to eyes.checkFrame() 73 | eyes.check("Entire frame by locator", Target.frame(By.name("frame1"))); 74 | 75 | // Entire region in frame by frame name and region locator, equivalent to eyes.checkRegionInFrame() 76 | eyes.check("Entire region in frame by frame name and region locator", Target.region(By.id("inner-frame-div"), "frame1").fully()); 77 | 78 | return eyes.close(); 79 | }); 80 | 81 | afterEach(function () { 82 | return driver.quit().then(function () { 83 | return eyes.abortIfNotClosed(); 84 | }); 85 | }); 86 | }); 87 | -------------------------------------------------------------------------------- /test/selenium/hide-scrollbars-selenium-test.js: -------------------------------------------------------------------------------- 1 | require('chromedriver'); 2 | var webdriver = require('selenium-webdriver'); 3 | var Builder = webdriver.Builder; 4 | 5 | var SeleniumSDK = require('../../index'); 6 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 7 | var Target = SeleniumSDK.Target; 8 | var Eyes = SeleniumSDK.Eyes; 9 | 10 | var driver = null, eyes = null; 11 | describe('Eyes.Selenium.JavaScript - Selenium', function () { 12 | 13 | this.timeout(5 * 60 * 1000); 14 | 15 | before(function () { 16 | driver = new Builder() 17 | .forBrowser('chrome') 18 | .build(); 19 | 20 | eyes = new Eyes(); 21 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 22 | eyes.setLogHandler(new ConsoleLogHandler(true)); 23 | }); 24 | 25 | beforeEach(function () { 26 | var appName = this.test.parent.title; 27 | var testName = this.currentTest.title; 28 | 29 | return eyes.open(driver, appName, testName, {width: 800, height: 560}).then(function (browser) { 30 | driver = browser; 31 | }); 32 | }); 33 | 34 | it("hide scrollbars selenium", function () { 35 | driver.get('https://astappiev.github.io/test-html-pages/'); 36 | 37 | eyes.setHideScrollbars(true); 38 | eyes.setScrollRootElement(webdriver.By.id("overflowing-div-image")); 39 | eyes.check("Entire window", Target.window().fully(true)); 40 | 41 | return eyes.close(); 42 | }); 43 | 44 | afterEach(function () { 45 | return driver.quit().then(function () { 46 | return eyes.abortIfNotClosed(); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /test/selenium/scaling-methods-test.js: -------------------------------------------------------------------------------- 1 | require('chromedriver'); 2 | var webdriver = require('selenium-webdriver'); 3 | var chrome = require('selenium-webdriver/chrome'); 4 | var Builder = webdriver.Builder; 5 | var By = webdriver.By; 6 | 7 | var SeleniumSDK = require('../../index'); 8 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 9 | var Eyes = SeleniumSDK.Eyes; 10 | 11 | var driver = null, eyes = null; 12 | describe('Eyes.Selenium.JavaScript - Selenium', function () { 13 | 14 | this.timeout(5 * 60 * 1000); 15 | 16 | before(function () { 17 | var options = new chrome.Options().addArguments("--force-device-scale-factor=1.25"); 18 | driver = new Builder() 19 | .forBrowser('chrome') 20 | .setChromeOptions(options) 21 | .build(); 22 | 23 | eyes = new Eyes(); 24 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 25 | eyes.setLogHandler(new ConsoleLogHandler(true)); 26 | eyes.getLogHandler().setPrintSessionId(true); 27 | eyes.setForceFullPageScreenshot(true); 28 | }); 29 | 30 | beforeEach(function () { 31 | var appName = this.test.parent.title; 32 | var testName = this.currentTest.title; 33 | 34 | return eyes.open(driver, appName, testName, {width: 1000, height: 700}).then(function (browser) { 35 | driver = browser; 36 | }); 37 | }); 38 | 39 | it("scaling methods", function () { 40 | driver.get("https://astappiev.github.io/test-html-pages/"); 41 | 42 | eyes.checkWindow("Initial"); 43 | 44 | eyes.checkElementBy(By.id("overflowing-div"), null, "Text block"); 45 | 46 | eyes.checkElementBy(By.id("overflowing-div-image"), null, "Minions"); 47 | 48 | return eyes.close(); 49 | }); 50 | 51 | afterEach(function () { 52 | return driver.quit().then(function () { 53 | return eyes.abortIfNotClosed(); 54 | }); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /test/selenium/simple-selenium-test.js: -------------------------------------------------------------------------------- 1 | require('chromedriver'); 2 | var webdriver = require('selenium-webdriver'); 3 | var Builder = webdriver.Builder; 4 | 5 | var SeleniumSDK = require('../../index'); 6 | var ConsoleLogHandler = SeleniumSDK.ConsoleLogHandler; 7 | var FixedCutProvider = SeleniumSDK.FixedCutProvider; 8 | var Eyes = SeleniumSDK.Eyes; 9 | 10 | var driver = null, eyes = null; 11 | describe('Eyes.Selenium.JavaScript - Selenium', function () { 12 | 13 | this.timeout(5 * 60 * 1000); 14 | 15 | before(function () { 16 | driver = new Builder() 17 | .forBrowser('chrome') 18 | .build(); 19 | 20 | eyes = new Eyes(); 21 | eyes.setApiKey(process.env.APPLITOOLS_API_KEY); 22 | eyes.setLogHandler(new ConsoleLogHandler(true)); 23 | eyes.getLogHandler().setPrintSessionId(true); 24 | }); 25 | 26 | beforeEach(function () { 27 | var appName = this.test.parent.title; 28 | var testName = this.currentTest.title; 29 | 30 | return eyes.open(driver, appName, testName, {width: 800, height: 560}).then(function (browser) { 31 | driver = browser; 32 | }); 33 | }); 34 | 35 | it("simple selenium", function () { 36 | driver.get('https://astappiev.github.io/test-html-pages/'); 37 | 38 | eyes.addProperty("MyProp", "I'm correct!"); 39 | 40 | eyes.checkWindow("Entire window"); 41 | 42 | // cut params: header, footer, left, right. 43 | eyes.setImageCut(new FixedCutProvider(60, 100, 50, 120)); 44 | 45 | eyes.checkWindow("Entire window with cut borders"); 46 | 47 | return eyes.close(); 48 | }); 49 | 50 | afterEach(function () { 51 | return driver.quit().then(function () { 52 | return eyes.abortIfNotClosed(); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /typings/eyes.selenium-tests.ts: -------------------------------------------------------------------------------- 1 | // import EyesSelenium = require('eyes.selenium'); 2 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | /* Type definitions for eyes.selenium 0.0.1 */ 2 | // Project: https://github.com/applitools/eyes.selenium.javascript 3 | // Definitions by: Oleh Astappiev 4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped 5 | // TypeScript Version: 2.4 6 | 7 | /// 8 | 9 | import { WebDriver, WebElement, By, TargetLocator, WebElementPromise, AlertPromise, promise } from 'selenium-webdriver'; 10 | import { ElementFinder, ElementArrayFinder, ProtractorBy } from 'protractor'; 11 | 12 | import {PromiseFactory, Location, Region, RectangleSize, UserAgent} from 'eyes.utils'; 13 | import { PositionProvider, RegionProvider, Logger, CutProvider, ScaleProviderFactory, MatchSettings, CoordinatesType, 14 | EyesScreenshot, EyesBase, MutableImage, TestResults, ImageProvider } from 'eyes.sdk'; 15 | 16 | 17 | export { ArgumentGuard, GeneralUtils, GeometryUtils, ImageDeltaCompressor, ImageUtils, PromiseFactory, StreamUtils, 18 | PropertyHandler, SimplePropertyHandler, ReadOnlyPropertyHandler, Location, Region, RectangleSize } from 'eyes.utils'; 19 | 20 | export { ConsoleLogHandler, ContextBasedScaleProvider, ContextBasedScaleProviderFactory, CoordinatesType, CutProvider, 21 | EyesScreenshot, FileLogHandler, FixedCutProvider, FixedScaleProvider, FixedScaleProviderFactory, Logger, LogHandler, 22 | MatchSettings, MutableImage, NullCutProvider, NullLogHandler, NullScaleProvider, PositionProvider, RegionProvider, 23 | ScaleProvider, ScaleProviderFactory, ScaleProviderIdentityFactory, ServerConnector, TestResultsFormatter, Triggers, 24 | Trigger, RunningSession, BatchInfo, AppEnvironment, SessionStartInfo, TestResults} from 'eyes.sdk'; 25 | 26 | 27 | export interface FloatingRegion { 28 | left: number; 29 | top: number; 30 | width: number; 31 | height: number; 32 | maxLeftOffset: number; 33 | maxRightOffset: number; 34 | maxUpOffset: number; 35 | maxDownOffset: number; 36 | } 37 | 38 | 39 | export interface FloatingElement { 40 | element: WebElement|EyesRemoteWebElement|By; 41 | maxLeftOffset: number; 42 | maxRightOffset: number; 43 | maxUpOffset: number; 44 | maxDownOffset: number; 45 | } 46 | 47 | 48 | export declare class CssTranslatePositionProvider extends PositionProvider { 49 | constructor(logger: Logger, executor: EyesWebDriver, promiseFactory: PromiseFactory); 50 | /** 51 | * @return The scroll position of the current frame. 52 | */ 53 | getCurrentPosition(): Promise; 54 | /** 55 | * Go to the specified location. 56 | * @param location The position to scroll to. 57 | */ 58 | setPosition(location: Location): Promise; 59 | /** 60 | * @return The entire size of the container which the position is relative to. 61 | */ 62 | getEntireSize(): Promise; 63 | getState(): Promise; 64 | /** 65 | * @param state The initial state of position 66 | */ 67 | restoreState(state: any): Promise; 68 | } 69 | 70 | 71 | export declare class ElementPositionProvider extends PositionProvider { 72 | constructor(logger: Logger, eyesDriver: EyesWebDriver, element: EyesRemoteWebElement, promiseFactory: PromiseFactory); 73 | /** 74 | * @return The scroll position of the current frame. 75 | */ 76 | getCurrentPosition(): Promise; 77 | /** 78 | * Go to the specified location. 79 | * @param location The position to scroll to. 80 | */ 81 | setPosition(location: Location): Promise; 82 | /** 83 | * @return The entire size of the container which the position is relative to. 84 | */ 85 | getEntireSize(): Promise; 86 | getState(): Promise; 87 | /** 88 | * @param state The initial state of position 89 | */ 90 | restoreState(state: any): Promise; 91 | } 92 | 93 | 94 | /** 95 | * The main type - to be used by the users of the library to access all functionality. 96 | */ 97 | export declare class Eyes extends EyesBase { 98 | /** 99 | * @param serverUrl The Eyes server URL. 100 | * @param isDisabled set to true to disable Applitools Eyes and use the webdriver directly. 101 | **/ 102 | constructor(serverUrl?: string, isDisabled?: boolean); 103 | /** 104 | * Starts a test. 105 | * @param driver The web driver that controls the browser hosting the application under test. 106 | * @param appName The name of the application under test. 107 | * @param testName The test name. 108 | * @param viewportSize The required browser's viewport size (i.e., the visible part of the document's body) or to use the current window's viewport. 109 | * @return A wrapped WebDriver which enables Eyes trigger recording and frame handling. 110 | */ 111 | open(driver: WebDriver, appName: string, testName: string, viewportSize?: RectangleSize): Promise; 112 | /** 113 | * Ends the test. 114 | * @param [throwEx=true] If true, an exception will be thrown for failed/new tests. 115 | * @return The test results. 116 | */ 117 | close(throwEx?: boolean): Promise; 118 | /** 119 | * Preform visual validation 120 | * @param name A name to be associated with the match 121 | * @param target Target instance which describes whether we want a window/region/frame 122 | * @return A promise which is resolved when the validation is finished. 123 | */ 124 | check(name: string, target: Target): Promise; 125 | /** 126 | * Takes a snapshot of the application under test and matches it with the expected output. 127 | * @param [tag=] An optional tag to be associated with the snapshot. 128 | * @param [matchTimeout=-1] The amount of time to retry matching (Milliseconds). 129 | * @return A promise which is resolved when the validation is finished. 130 | */ 131 | checkWindow(tag?: string, matchTimeout?: number): Promise; 132 | /** 133 | * Matches the frame given as parameter, by switching into the frame and using stitching to get an image of the frame. 134 | * @param element The element which is the frame to switch to. (as would be used in a call to driver.switchTo().frame()). 135 | * @param [matchTimeout=-1] The amount of time to retry matching (milliseconds). 136 | * @param [tag=] An optional tag to be associated with the match. 137 | * @return A promise which is resolved when the validation is finished. 138 | */ 139 | checkFrame(element: EyesRemoteWebElement, matchTimeout?: number, tag?: string): Promise; 140 | /** 141 | * Takes a snapshot of the application under test and matches a specific element with the expected region output. 142 | * @param element The element to check. 143 | * @param [matchTimeout=-1] The amount of time to retry matching (milliseconds). 144 | * @param [tag=] An optional tag to be associated with the match. 145 | * @return A promise which is resolved when the validation is finished. 146 | */ 147 | checkElement(element: WebElement|EyesRemoteWebElement, matchTimeout?: number, tag?: string): Promise; 148 | /** 149 | * Takes a snapshot of the application under test and matches a specific element with the expected region output. 150 | * @param locator The element to check. 151 | * @param [matchTimeout=-1] The amount of time to retry matching (milliseconds). 152 | * @param [tag=] An optional tag to be associated with the match. 153 | * @return A promise which is resolved when the validation is finished. 154 | */ 155 | checkElementBy(locator: By, matchTimeout?: number, tag?: string): Promise; 156 | /** 157 | * Visually validates a region in the screenshot. 158 | * @param region The region to validate (in screenshot coordinates). 159 | * @param [tag=] An optional tag to be associated with the screenshot. 160 | * @param [matchTimeout=-1] The amount of time to retry matching. 161 | * @return A promise which is resolved when the validation is finished. 162 | */ 163 | checkRegion(region: Region, matchTimeout?: number, tag?: string): Promise; 164 | /** 165 | * Visually validates a region in the screenshot. 166 | * @param element The element defining the region to validate. 167 | * @param [tag=] An optional tag to be associated with the screenshot. 168 | * @param [matchTimeout=-1] The amount of time to retry matching. 169 | * @return A promise which is resolved when the validation is finished. 170 | */ 171 | checkRegionByElement(element: WebElement|EyesRemoteWebElement, tag?: string, matchTimeout?: number): Promise; 172 | /** 173 | * Visually validates a region in the screenshot. 174 | * @param by The WebDriver selector used for finding the region to validate. 175 | * @param [tag=] An optional tag to be associated with the screenshot. 176 | * @param [matchTimeout=-1] The amount of time to retry matching. 177 | * @return A promise which is resolved when the validation is finished. 178 | */ 179 | checkRegionBy(by: By, tag?: string, matchTimeout?: number): Promise; 180 | /** 181 | * Switches into the given frame, takes a snapshot of the application under test and matches a region specified by the given selector. 182 | * @param frameNameOrId The name or id of the frame to switch to. (as would be used in a call to driver.switchTo().frame()). 183 | * @param locator A Selector specifying the region to check. 184 | * @param [matchTimeout=-1] The amount of time to retry matching (Milliseconds). 185 | * @param [tag=] An optional tag to be associated with the snapshot. 186 | * @param [stitchContent=true] If {@code true}, stitch the internal content of the region (i.e., perform {@link #checkElement(By, number, String)} on the region. 187 | * @return A promise which is resolved when the validation is finished. 188 | */ 189 | checkRegionInFrame(frameNameOrId: string, locator: By, matchTimeout?: number, tag?: string, stitchContent?: boolean): Promise; 190 | /** 191 | * Get an updated screenshot. 192 | * @return The image of the new screenshot. 193 | */ 194 | getScreenShot(): Promise; 195 | getTitle(): Promise; 196 | getInferredEnvironment(): Promise; 197 | /** 198 | * Set the failure report. 199 | * @param mode Use one of the values in EyesBase.FailureReport. 200 | */ 201 | setFailureReport(mode: EyesBase.FailureReport): void; 202 | /** 203 | * Get the viewport size. 204 | * @return The viewport size. 205 | */ 206 | getViewportSize(): Promise; 207 | setViewportSize(size: RectangleSize): Promise; 208 | /** 209 | * Set the viewport size using the driver. Call this method if for some reason you don't want to call {@link #open(WebDriver, String, String)} (or one of its variants) yet. 210 | * @param driver The driver to use for setting the viewport. 211 | * @param size The required viewport size. 212 | * @return The viewport size of the browser. 213 | */ 214 | static setViewportSize(driver: WebDriver, size: RectangleSize): Promise; 215 | /** 216 | * Set the full page screenshot option. 217 | * @param force Whether to force a full page screenshot or not. 218 | */ 219 | setForceFullPageScreenshot(force: boolean): void; 220 | /** 221 | * Get whether to force a full page screenshot or not. 222 | * @return true if the option is on, otherwise false. 223 | */ 224 | getForceFullPageScreenshot(): boolean; 225 | /** 226 | * Set the image rotation degrees. 227 | * @param degrees The amount of degrees to set the rotation to. 228 | */ 229 | setForcedImageRotation(degrees: number): void; 230 | /** 231 | * Get the rotation degrees. 232 | * @return The rotation degrees. 233 | */ 234 | getForcedImageRotation(): number; 235 | /** 236 | * Hide the scrollbars when taking screenshots. 237 | * @param hide Whether to hide the scrollbars or not. 238 | */ 239 | setHideScrollbars(hide: boolean): void; 240 | /** 241 | * Hide the scrollbars when taking screenshots. 242 | * @return true if the hide scrollbars option is on, otherwise false. 243 | */ 244 | getHideScrollbars(): boolean; 245 | /** 246 | * Receives a selector and when doing hideScrollbars, it will set the overflow to hidden on that element. 247 | * @param element The element to hide scrollbars. 248 | */ 249 | setScrollRootElement(element: WebElement|By|EyesRemoteWebElement): void; 250 | /** 251 | * Receives a selector and when doing hideScrollbars, it will set the overflow to hidden on that element. 252 | * @return The element to hide scrollbars. 253 | */ 254 | getScrollRootElement(): WebElement; 255 | /** 256 | * Set the stitch mode. 257 | * @param mode The desired stitch mode settings. 258 | */ 259 | setStitchMode(mode: Eyes.StitchMode): void; 260 | /** 261 | * Sets the wait time between before each screen capture, including between screen parts of a full page screenshot. 262 | * @param waitBeforeScreenshots The wait time in milliseconds. 263 | */ 264 | setWaitBeforeScreenshots(waitBeforeScreenshots: number): void; 265 | /** 266 | * Get the wait time before each screenshot. 267 | * @return the wait time between before each screen capture, in milliseconds. 268 | */ 269 | getWaitBeforeScreenshots(): number; 270 | /** 271 | * Get the session id. 272 | * @return A promise which resolves to the webdriver's session ID. 273 | */ 274 | getAUTSessionId(): Promise; 275 | } 276 | 277 | export declare namespace Eyes { 278 | export enum StitchMode { 279 | /** Uses scrolling to get to the different parts of the page. */ 280 | Scroll = 'Scroll', 281 | /** Uses CSS transitions to get to the different parts of the page. */ 282 | CSS = 'CSS' 283 | } 284 | } 285 | 286 | /** 287 | * Wraps Protractor's ElementFinder to make sure we return our own Web Element. 288 | */ 289 | export declare class ElementFinderWrapper extends ElementFinder { 290 | constructor(finder: ElementFinder, eyesDriver: EyesWebDriver, logger: Logger); 291 | /** 292 | * Wrap the getWebElement function 293 | */ 294 | getWebElement(): EyesRemoteWebElement; 295 | /** 296 | * Wrap the click function 297 | */ 298 | click(): promise.Promise; 299 | /** 300 | * Wrap the functions that return objects that require pre-wrapping 301 | */ 302 | sendKeys(): promise.Promise; 303 | } 304 | 305 | 306 | /** 307 | * Wrapper for ElementArrayFinder object from Protractor 308 | */ 309 | export declare class ElementArrayFinderWrapper extends ElementArrayFinder { 310 | constructor(arrayFinder: ElementArrayFinder, eyesDriver: EyesWebDriver, logger: Logger); 311 | } 312 | 313 | 314 | export declare class EyesRegionProvider extends RegionProvider { 315 | constructor(logger: Logger, driver: EyesWebDriver, region: Region, coordinatesType: CoordinatesType); 316 | /** 317 | * @return A region with "as is" viewport coordinates. 318 | */ 319 | getRegion(): Region; 320 | /** 321 | * @return A region in selected viewport coordinates. 322 | */ 323 | getRegionInLocation(image: MutableImage, toCoordinatesType: CoordinatesType, promiseFactory: PromiseFactory): Promise; 324 | /** 325 | * @return The type of coordinates on which the region is based. 326 | */ 327 | getCoordinatesType(): CoordinatesType; 328 | } 329 | 330 | 331 | export declare class EyesRemoteWebElement extends WebElementPromise { 332 | constructor(remoteWebElement: WebElement, eyesDriver: EyesWebDriver, logger: Logger); 333 | static registerSendKeys(element: EyesRemoteWebElement, eyesDriver: EyesWebDriver, logger: Logger, args: any[]): Promise; 334 | static registerClick(element: EyesRemoteWebElement, eyesDriver: EyesWebDriver, logger: Logger): Promise; 335 | sendKeys(...var_args: any[]): promise.Promise; 336 | click(): promise.Promise; 337 | findElement(locator: By|ProtractorBy): EyesRemoteWebElement; 338 | findElements(locator: By|ProtractorBy): promise.Promise; 339 | /** 340 | * Returns the computed value of the style property for the current element. 341 | * @param propStyle The style property which value we would like to extract. 342 | * @return The value of the style property of the element, or {@code null}. 343 | */ 344 | getComputedStyle(propStyle: string): Promise; 345 | /** 346 | * Returns the computed value of the style property for the current element. 347 | * @param propStyle The style property which value we would like to extract. 348 | * @return The integer value of a computed style. 349 | */ 350 | getComputedStyleInteger(propStyle: string): Promise; 351 | /** 352 | * @return The value of the scrollLeft property of the element. 353 | */ 354 | getScrollLeft(): Promise; 355 | /** 356 | * @return The value of the scrollTop property of the element. 357 | */ 358 | getScrollTop(): Promise; 359 | /** 360 | * @return The value of the scrollWidth property of the element. 361 | */ 362 | getScrollWidth(): Promise; 363 | /** 364 | * @return The value of the scrollHeight property of the element. 365 | */ 366 | getScrollHeight(): Promise; 367 | /** 368 | * @return The width of the left border. 369 | */ 370 | getBorderLeftWidth(): Promise; 371 | /** 372 | * @return The width of the right border. 373 | */ 374 | getBorderRightWidth(): Promise; 375 | /** 376 | * @return The width of the top border. 377 | */ 378 | getBorderTopWidth(): Promise; 379 | /** 380 | * @return The width of the bottom border. 381 | */ 382 | getBorderBottomWidth(): Promise; 383 | /** 384 | * @return element's size 385 | */ 386 | getSize(): promise.Promise; 387 | /** 388 | * @return element's location 389 | */ 390 | getLocation(): promise.Promise; 391 | /** 392 | * Scrolls to the specified location inside the element. 393 | * @param location The location to scroll to. 394 | */ 395 | scrollTo(location: Location): Promise; 396 | /** 397 | * @return The overflow of the element. 398 | */ 399 | getOverflow(): Promise; 400 | /** 401 | * @return The original element object 402 | */ 403 | getRemoteWebElement(): Promise; 404 | } 405 | 406 | 407 | /** 408 | * A wrapper for an action to be performed before the actual switch is made. 409 | */ 410 | interface OnWillSwitch { 411 | /** 412 | * Will be called before switching into a frame. 413 | * @param {TargetType} targetType The type of frame we're about to switch into. 414 | * @param {WebElement} targetFrame The element about to be switched to, if available. Otherwise, null. 415 | */ 416 | willSwitchToFrame(targetType: TargetType, targetFrame: WebElement): void; 417 | /** 418 | * Will be called before switching into a window. 419 | * @param {string} nameOrHandle The name/handle of the window to be switched to. 420 | */ 421 | willSwitchToWindow(nameOrHandle: string): void; 422 | } 423 | 424 | 425 | export declare class EyesTargetLocator extends TargetLocator { 426 | /** 427 | * @param logger A Logger instance. 428 | * @param driver The WebDriver from which the targetLocator was received. 429 | * @param targetLocator The actual TargetLocator object. 430 | * @param onWillSwitch A delegate to be called whenever a relevant switch 431 | * @param promiseFactory 432 | */ 433 | constructor(logger: Logger, driver: EyesWebDriver, targetLocator: TargetLocator, onWillSwitch: OnWillSwitch, promiseFactory: PromiseFactory); 434 | frame(nameOrIndex: number|EyesRemoteWebElement): promise.Promise; 435 | parentFrame(): Promise; 436 | /** 437 | * Switches into every frame in the frame chain. This is used as way to switch into nested frames (while considering scroll) in a single call. 438 | * @param obj The path to the frame to switch to. Or the path to the frame to check. This is a list of frame names/IDs (where each frame is nested in the previous frame). 439 | * @return The WebDriver with the switched context. 440 | */ 441 | frames(obj: FrameChain|string[]): Promise; 442 | window(nameOrHandle: string): promise.Promise; 443 | defaultContent(): promise.Promise; 444 | activeElement(): EyesRemoteWebElement; 445 | alert(): AlertPromise; 446 | } 447 | 448 | export declare enum TargetType { 449 | FRAME = 1, 450 | PARENT_FRAME = 2, 451 | DEFAULT_CONTENT = 3 452 | } 453 | 454 | // EyesTargetLocator.TargetType = TargetType; 455 | 456 | export declare class EyesWebDriver extends WebDriver { 457 | constructor(remoteWebDriver: WebDriver, eyes: Eyes, logger: Logger); 458 | getEyes(): Eyes; 459 | getPromiseFactory(): PromiseFactory; 460 | getRemoteWebDriver(): WebDriver; 461 | setRemoteWebDriver(driver: WebDriver): void; 462 | getUserAgent(): Promise; 463 | findElement(locator: By|ProtractorBy): EyesRemoteWebElement; 464 | findElements(locator: By|ProtractorBy): promise.Promise; 465 | findElementByCssSelector(cssSelector: string): EyesRemoteWebElement; 466 | findElementsByCssSelector(cssSelector: string): Promise; 467 | findElementById(name: string): EyesRemoteWebElement; 468 | findElementsById(name: string): Promise; 469 | findElementByName(name: string): EyesRemoteWebElement; 470 | findElementsByName(name: string): Promise; 471 | switchTo(): EyesTargetLocator; 472 | /** 473 | * @param forceQuery If true, we will perform the query even if we have a cached viewport size. 474 | * @return The viewport size of the default content (outer most frame). 475 | */ 476 | getDefaultContentViewportSize(forceQuery: boolean): Promise; 477 | /** 478 | * @return A copy of the current frame chain. 479 | */ 480 | getFrameChain(): FrameChain; 481 | } 482 | 483 | 484 | export declare enum ScreenshotType { 485 | VIEWPORT = 1, 486 | ENTIRE_FRAME = 2 487 | } 488 | 489 | 490 | export declare class FirefoxScreenshotImageProvider implements ImageProvider { 491 | constructor(eyes: Eyes, logger: Logger, tsInstance: EyesWebDriver); 492 | getImage(): Promise; 493 | } 494 | 495 | 496 | export declare class SafariScreenshotImageProvider implements ImageProvider { 497 | constructor(eyes: Eyes, logger: Logger, tsInstance: EyesWebDriver, userAgent: UserAgent); 498 | getImage(): Promise; 499 | } 500 | 501 | 502 | export declare class TakesScreenshotImageProvider implements ImageProvider { 503 | constructor(logger: Logger, tsInstance: EyesWebDriver); 504 | getImage(): Promise; 505 | } 506 | 507 | 508 | export declare class ImageProviderFactory { 509 | static getImageProvider(userAgent: UserAgent, eyes: Eyes, logger: Logger, driver: EyesWebDriver): ImageProvider; 510 | } 511 | 512 | 513 | export declare class EyesWebDriverScreenshot extends EyesScreenshot { 514 | /** 515 | * @param logger A Logger instance. 516 | * @param driver The web driver used to get the screenshot. 517 | * @param image The actual screenshot image. 518 | * @param promiseFactory 519 | */ 520 | constructor(logger: Logger, driver: EyesWebDriver, image: MutableImage, promiseFactory: PromiseFactory); 521 | /** 522 | * @param screenshotType The screenshot's type (e.g., viewport/full page). 523 | * @param frameLocationInScreenshot The current frame's location in the screenshot. 524 | * @param frameSize The full internal size of the frame. 525 | */ 526 | buildScreenshot(screenshotType?: ScreenshotType, frameLocationInScreenshot?: Location, frameSize?: RectangleSize): Promise; 527 | /** 528 | * @return The region of the frame which is available in the screenshot, in screenshot coordinates. 529 | */ 530 | getFrameWindow(): Region; 531 | /** 532 | * @return A copy of the frame chain which was available when the screenshot was created. 533 | */ 534 | getFrameChain(): FrameChain; 535 | /** 536 | * Returns a part of the screenshot based on the given region. 537 | * @param region The region for which we should get the sub screenshot. 538 | * @param coordinatesType How should the region be calculated on the screenshot image. 539 | * @param throwIfClipped Throw an EyesException if the region is not fully contained in the screenshot. 540 | * @return A screenshot instance containing the given region. 541 | */ 542 | getSubScreenshot(region: Region, coordinatesType: CoordinatesType, throwIfClipped: boolean): Promise; 543 | /** 544 | * Converts a location's coordinates with the {@code from} coordinates type to the {@code to} coordinates type. 545 | * @param location The location which coordinates needs to be converted. 546 | * @param from The current coordinates type for {@code location}. 547 | * @param to The target coordinates type for {@code location}. 548 | * @return A new location which is the transformation of {@code location} to the {@code to} coordinates type. 549 | */ 550 | convertLocationFromLocation(location: Location, from: CoordinatesType, to: CoordinatesType): Location; 551 | getLocationInScreenshot(location: Location, coordinatesType: CoordinatesType): Location; 552 | getIntersectedRegion(region: Region, originalCoordinatesType: CoordinatesType, resultCoordinatesType: CoordinatesType): Region; 553 | /** 554 | * Gets the elements region in the screenshot. 555 | * @param element The element which region we want to intersect. 556 | * @return The intersected region, in {@code SCREENSHOT_AS_IS} coordinates type. 557 | */ 558 | getIntersectedRegionFromElement(element: WebElement): Promise; 559 | } 560 | 561 | 562 | export declare class Frame { 563 | /** 564 | * @param logger A Logger instance. 565 | * @param reference The web element for the frame, used as a reference to switch into the frame. 566 | * @param frameId The id of the frame. Can be used later for comparing two frames. 567 | * @param location The location of the frame within the current frame. 568 | * @param size The frame element size (i.e., the size of the frame on the screen, not the internal document size). 569 | * @param parentScrollPosition The scroll position the frame's parent was in when the frame was switched to. 570 | */ 571 | constructor(logger: Logger, reference: WebElement, frameId: string, location: Location, size: RectangleSize, parentScrollPosition: Location); 572 | getReference(): WebElement; 573 | getId(): string; 574 | getLocation(): Location; 575 | getSize(): RectangleSize; 576 | getParentScrollPosition(): Location; 577 | } 578 | 579 | 580 | export declare class FrameChain { 581 | /** 582 | * Creates a new frame chain. 583 | * @param logger A Logger instance. 584 | * @param other A frame chain from which the current frame chain will be created. 585 | */ 586 | constructor(logger: Logger, other: FrameChain); 587 | /** 588 | * Compares two frame chains. 589 | * @param c1 Frame chain to be compared against c2. 590 | * @param c2 Frame chain to be compared against c1. 591 | * @return True if both frame chains represent the same frame, false otherwise. 592 | */ 593 | static isSameFrameChain(c1: FrameChain, c2: FrameChain): boolean; 594 | /** 595 | * @return frames stored in chain 596 | */ 597 | getFrames(): Frame[]; 598 | /** 599 | * @param index Index of needed frame 600 | * @return frame by index in array 601 | */ 602 | getFrame(index: number): Frame; 603 | /** 604 | * @return The number of frames in the chain. 605 | */ 606 | size(): number; 607 | /** 608 | * Removes all current frames in the frame chain. 609 | */ 610 | clear(): void; 611 | /** 612 | * Removes the last inserted frame element. Practically means we switched back to the parent of the current frame 613 | */ 614 | pop(): Frame; 615 | /** 616 | * Appends a frame to the frame chain. 617 | * @param frame The frame to be added. 618 | */ 619 | push(frame: Frame): void; 620 | /** 621 | * @return The location of the current frame in the page. 622 | */ 623 | getCurrentFrameOffset(): Location; 624 | /** 625 | * @return The outermost frame's location, or NoFramesException. 626 | */ 627 | getDefaultContentScrollPosition(): Location; 628 | /** 629 | * The size of the current frame. 630 | */ 631 | getCurrentFrameSize(): RectangleSize; 632 | } 633 | 634 | 635 | export declare class ScrollPositionProvider extends PositionProvider { 636 | constructor(logger: Logger, executor: EyesWebDriver, promiseFactory: PromiseFactory); 637 | /** 638 | * @return The scroll position of the current frame. 639 | */ 640 | getCurrentPosition(): Promise; 641 | /** 642 | * Go to the specified location. 643 | * @param location The position to scroll to. 644 | */ 645 | setPosition(location: Location): Promise; 646 | /** 647 | * @return The entire size of the container which the position is relative to. 648 | */ 649 | getEntireSize(): Promise; 650 | getState(): Promise; 651 | /** 652 | * @param state The initial state of position 653 | */ 654 | restoreState(state: Location): Promise; 655 | } 656 | 657 | 658 | export declare class Target { 659 | constructor(region: Region|WebElement|EyesRemoteWebElement|By, frame: WebElement|EyesRemoteWebElement|String); 660 | /** 661 | * @param ms Milliseconds to wait 662 | */ 663 | timeout(ms: number): Target; 664 | fully(stitchContent?: boolean): Target; 665 | ignoreMismatch(ignoreMismatch?: boolean): Target; 666 | matchLevel(matchLevel: MatchSettings.MatchLevel): Target; 667 | ignoreCaret(ignoreCaret?: boolean): Target; 668 | ignore(...ignoreRegion: (Region|WebElement|EyesRemoteWebElement|By|{element: (WebElement|EyesRemoteWebElement|By)})[]): Target; 669 | floating(...floatingRegion: (FloatingRegion|FloatingElement)[]): Target; 670 | getRegion(): Region|WebElement|EyesRemoteWebElement|By|null; 671 | isUsingRegion(): boolean; 672 | getFrame(): WebElement|EyesRemoteWebElement|String|null; 673 | isUsingFrame(): boolean; 674 | getTimeout(): number|null; 675 | getStitchContent(): boolean; 676 | getIgnoreMismatch(): boolean; 677 | getMatchLevel(): boolean; 678 | getIgnoreCaret(): boolean|null; 679 | getIgnoreRegions(): Region[]; 680 | getIgnoreObjects(): {element: (WebElement|EyesRemoteWebElement|By)}[]; 681 | getFloatingRegions(): FloatingRegion[]; 682 | getFloatingObjects(): FloatingElement[]; 683 | /** 684 | * Validate current window 685 | */ 686 | static window(): Target; 687 | /** 688 | * Validate region (in current window or frame) using region's rect, element or element's locator 689 | * @param region The region to validate. 690 | * @param frame The element which is the frame to switch to. 691 | */ 692 | static region(region: Region|WebElement|EyesRemoteWebElement|By, frame?: WebElement|EyesRemoteWebElement|String): Target; 693 | /** 694 | * Validate frame 695 | * @param frame The element which is the frame to switch to. 696 | */ 697 | static frame(frame: EyesRemoteWebElement|WebElement|String): Target; 698 | } 699 | 700 | 701 | export declare class EyesSeleniumUtils { 702 | /** 703 | * Executes a script using the browser's executeScript function - and optionally waits a timeout. 704 | * @param browser The driver using which to execute the script. 705 | * @param script The code to execute on the given driver. 706 | * @param promiseFactory 707 | * @param stabilizationTimeMs The amount of time to wait after script execution to let the browser a chance to stabilize (e.g., finish rendering). 708 | * @return A promise which resolves to the result of the script's execution on the tab. 709 | */ 710 | static executeScript(browser: WebDriver, script: string, promiseFactory: PromiseFactory, stabilizationTimeMs?: number): Promise; 711 | /** 712 | * Returns the computed value of the style property for the current element. 713 | * @param browser The driver which will execute the script to get computed style. 714 | * @param propStyle The style property which value we would like to extract. 715 | * @param element 716 | * @return The value of the style property of the element, or {@code null}. 717 | */ 718 | static getComputedStyle(browser: WebDriver, element: WebElement, propStyle: string): Promise; 719 | /** 720 | * Returns a location based on the given location. 721 | * @param logger The logger to use. 722 | * @param element The element for which we want to find the content's location. 723 | * @param location The location of the element. 724 | * @param promiseFactory 725 | * @return The location of the content of the element. 726 | */ 727 | static getLocationWithBordersAddition(logger: Logger, element: WebElement|EyesRemoteWebElement, location: Location, promiseFactory: PromiseFactory): Promise; 728 | /** 729 | * Gets the device pixel ratio. 730 | * @param browser The driver which will execute the script to get the ratio. 731 | * @param promiseFactory 732 | * @return A promise which resolves to the device pixel ratio (float type). 733 | */ 734 | static getDevicePixelRatio(browser: WebDriver, promiseFactory: PromiseFactory): Promise; 735 | /** 736 | * Get the current transform of page. 737 | * @param browser The driver which will execute the script to get the scroll position. 738 | * @param promiseFactory 739 | * @return A promise which resolves to the current transform value. 740 | */ 741 | static getCurrentTransform(browser: WebDriver, promiseFactory: PromiseFactory): Promise; 742 | /** 743 | * Sets transforms for document.documentElement according to the given map of style keys and values. 744 | * @param browser The browser to use. 745 | * @param transforms The transforms to set. Keys are used as style keys and values are the values for those styles. 746 | * @param promiseFactory 747 | */ 748 | static setTransforms(browser: WebDriver, transforms: any, promiseFactory: PromiseFactory): Promise; 749 | /** 750 | * Set the given transform to document.documentElement for all style keys defined in JS_TRANSFORM_KEYS 751 | * @param browser The driver which will execute the script to set the transform. 752 | * @param transformToSet The transform to set. 753 | * @param promiseFactory 754 | * @return A promise which resolves to the previous transform once the updated transform is set. 755 | */ 756 | static setTransform(browser: WebDriver, transformToSet: string, promiseFactory: PromiseFactory): Promise; 757 | /** 758 | * CSS translate the document to a given location. 759 | * @param browser The driver which will execute the script to set the transform. 760 | * @param point 761 | * @param promiseFactory 762 | * @return A promise which resolves to the previous transform when the scroll is executed. 763 | */ 764 | static translateTo(browser: WebDriver, point: Location, promiseFactory: PromiseFactory): Promise; 765 | /** 766 | * Scroll to the specified position. 767 | * @param browser The driver which will execute the script to set the scroll position. 768 | * @param point 769 | * @param promiseFactory 770 | * @return A promise which resolves after the action is performed and timeout passed. 771 | */ 772 | static scrollTo(browser: WebDriver, point: Location, promiseFactory: PromiseFactory): Promise; 773 | /** 774 | * Gets the current scroll position. 775 | * @param browser The driver which will execute the script to get the scroll position. 776 | * @param promiseFactory 777 | * @return A promise which resolves to the current scroll position. 778 | */ 779 | static getCurrentScrollPosition(browser: WebDriver, promiseFactory: PromiseFactory): Promise; 780 | /** 781 | * Get the entire page size. 782 | * @param browser The driver used to query the web page. 783 | * @param promiseFactory 784 | * @return A promise which resolves to an object containing the width/height of the page. 785 | */ 786 | static getEntirePageSize(browser: WebDriver, promiseFactory: PromiseFactory): Promise; 787 | /** 788 | * Updates the document's documentElement "overflow" value (mainly used to remove/allow scrollbars). 789 | * @param browser The driver used to update the web page. 790 | * @param overflowValue The values of the overflow to set. 791 | * @param scrollRootElement 792 | * @param promiseFactory 793 | * @return A promise which resolves to the original overflow of the document. 794 | */ 795 | static setOverflow(browser: WebDriver, overflowValue: string, scrollRootElement: WebElement, promiseFactory: PromiseFactory): Promise; 796 | /** 797 | * Updates the document's body "overflow" value 798 | * @param browser The driver used to update the web page. 799 | * @param overflowValue The values of the overflow to set. 800 | * @param promiseFactory 801 | * @return A promise which resolves to the original overflow of the document. 802 | */ 803 | static setBodyOverflow(browser: WebDriver, overflowValue: string, promiseFactory: PromiseFactory): Promise; 804 | /** 805 | * Hides the scrollbars of the current context's document element. 806 | * @param browser The browser to use for hiding the scrollbars. 807 | * @param promiseFactory 808 | * @return The previous value of the overflow property (could be {@code null}). 809 | */ 810 | static hideScrollbars(browser: WebDriver, promiseFactory: PromiseFactory): Promise; 811 | /** 812 | * Tries to get the viewport size using Javascript. If fails, gets the entire browser window size! 813 | * @param browser The browser to use. 814 | * @param promiseFactory 815 | * @return The viewport size. 816 | */ 817 | static getViewportSize(browser: WebDriver, promiseFactory: PromiseFactory): Promise; 818 | /** 819 | * @param logger 820 | * @param browser The browser to use. 821 | * @param promiseFactory 822 | * @return The viewport size of the current context, or the display size if the viewport size cannot be retrieved. 823 | */ 824 | static getViewportSizeOrDisplaySize(logger: Logger, browser: WebDriver, promiseFactory: PromiseFactory): Promise; 825 | /** 826 | * @param logger 827 | * @param browser The browser to use. 828 | * @param requiredSize 829 | * @param promiseFactory 830 | */ 831 | static setBrowserSize(logger: Logger, browser: WebDriver, requiredSize: RectangleSize, promiseFactory: PromiseFactory): Promise; 832 | /** 833 | * @param logger 834 | * @param browser The browser to use. 835 | * @param actualViewportSize 836 | * @param requiredViewportSize 837 | * @param promiseFactory 838 | */ 839 | static setBrowserSizeByViewportSize(logger: Logger, browser: WebDriver, actualViewportSize: RectangleSize, requiredViewportSize: RectangleSize, promiseFactory: PromiseFactory): Promise; 840 | /** 841 | * Tries to set the viewport 842 | * @param logger 843 | * @param browser The browser to use. 844 | * @param requiredSize The viewport size. 845 | * @param promiseFactory 846 | */ 847 | static setViewportSize(logger: Logger, browser: WebDriver, requiredSize: RectangleSize, promiseFactory: PromiseFactory): Promise; 848 | /** 849 | * Capture screenshot from given driver 850 | */ 851 | static getScreenshot( 852 | browser: WebDriver, 853 | promiseFactory: PromiseFactory, 854 | imageProvider: ImageProvider, 855 | viewportSize: RectangleSize, 856 | positionProvider: PositionProvider, 857 | scaleProviderFactory: ScaleProviderFactory, 858 | cutProvider: CutProvider, 859 | fullPage: boolean, 860 | hideScrollbars: boolean, 861 | scrollRootElement: WebElement, 862 | useCssTransition: boolean, 863 | rotationDegrees: number, 864 | automaticRotation: boolean, 865 | automaticRotationDegrees: number, 866 | isLandscape: boolean, 867 | waitBeforeScreenshots: number, 868 | checkFrameOrElement: boolean, 869 | regionProvider?: RegionProvider, 870 | saveDebugScreenshots?: boolean, 871 | debugScreenshotsPath?: string 872 | ): Promise; 873 | } 874 | -------------------------------------------------------------------------------- /typings/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "lib": [ 5 | "es2015" 6 | ], 7 | "noImplicitAny": true, 8 | "noImplicitThis": true, 9 | "strictNullChecks": false, 10 | "strictFunctionTypes": true, 11 | "typeRoots": [ 12 | "./" 13 | ], 14 | "types": [], 15 | "noEmit": true, 16 | "forceConsistentCasingInFileNames": true 17 | }, 18 | "files": [ 19 | "index.d.ts", 20 | "eyes.selenium-tests.ts" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /typings/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "dtslint/dtslint.json", 3 | "rules": { 4 | "adjacent-overload-signatures": false, 5 | "array-type": false, 6 | "arrow-return-shorthand": false, 7 | "ban-types": false, 8 | "callable-types": false, 9 | "comment-format": false, 10 | "dt-header": false, 11 | "eofline": false, 12 | "export-just-namespace": false, 13 | "import-spacing": false, 14 | "interface-name": false, 15 | "interface-over-type-literal": false, 16 | "jsdoc-format": false, 17 | "max-line-length": false, 18 | "member-access": false, 19 | "new-parens": false, 20 | "no-any-union": false, 21 | "no-boolean-literal-compare": false, 22 | "no-conditional-assignment": false, 23 | "no-consecutive-blank-lines": false, 24 | "no-construct": false, 25 | "no-declare-current-package": false, 26 | "no-duplicate-imports": false, 27 | "no-duplicate-variable": false, 28 | "no-empty-interface": false, 29 | "no-for-in-array": false, 30 | "no-inferrable-types": false, 31 | "no-internal-module": false, 32 | "no-irregular-whitespace": false, 33 | "no-mergeable-namespace": false, 34 | "no-misused-new": false, 35 | "no-namespace": false, 36 | "no-object-literal-type-assertion": false, 37 | "no-padding": false, 38 | "no-redundant-jsdoc": false, 39 | "no-redundant-jsdoc-2": false, 40 | "no-redundant-undefined": false, 41 | "no-reference-import": false, 42 | "no-relative-import-in-test": false, 43 | "no-self-import": false, 44 | "no-single-declare-module": false, 45 | "no-string-throw": false, 46 | "no-unnecessary-callback-wrapper": false, 47 | "no-unnecessary-class": false, 48 | "no-unnecessary-generics": false, 49 | "no-unnecessary-qualifier": false, 50 | "no-unnecessary-type-assertion": false, 51 | "no-useless-files": false, 52 | "no-var-keyword": false, 53 | "no-var-requires": false, 54 | "no-void-expression": false, 55 | "no-trailing-whitespace": false, 56 | "object-literal-key-quotes": false, 57 | "object-literal-shorthand": false, 58 | "one-line": false, 59 | "one-variable-per-declaration": false, 60 | "only-arrow-functions": false, 61 | "prefer-conditional-expression": false, 62 | "prefer-const": false, 63 | "prefer-declare-function": false, 64 | "prefer-for-of": false, 65 | "prefer-method-signature": false, 66 | "prefer-template": false, 67 | "radix": false, 68 | "semicolon": false, 69 | "space-before-function-paren": false, 70 | "space-within-parens": false, 71 | "strict-export-declare-modifiers": false, 72 | "trim-file": false, 73 | "triple-equals": false, 74 | "typedef-whitespace": false, 75 | "unified-signatures": false, 76 | "void-return": false, 77 | "whitespace": false 78 | } 79 | } 80 | --------------------------------------------------------------------------------