From eb142f3ff4eb7adc992aab66543be19060f2f7d2 Mon Sep 17 00:00:00 2001 From: Khan Thompson Date: Fri, 10 Nov 2017 16:11:57 +1100 Subject: [PATCH] Update to aff 4 Changed the order of the callbacks. Changed the FFI to use EffFnAff instead of Aff directly. Added a reference to purescript-aff-promise as Affs need to be run to promises to pass them back into FFI land. --- bower.json | 5 +- src/Selenium.js | 182 +++++++-------- src/Selenium.purs | 380 +++++++++++++++++++++++-------- src/Selenium/ActionSequence.js | 8 +- src/Selenium/ActionSequence.purs | 12 +- src/Selenium/Builder.js | 4 +- src/Selenium/Builder.purs | 11 +- src/Selenium/FFProfile.js | 4 +- src/Selenium/FFProfile.purs | 12 +- test/Main.purs | 7 +- 10 files changed, 405 insertions(+), 220 deletions(-) diff --git a/bower.json b/bower.json index d509002..799c25a 100644 --- a/bower.json +++ b/bower.json @@ -17,8 +17,9 @@ ], "license": "Apache-2.0", "dependencies": { - "purescript-aff": "^3.1.0", - "purescript-aff-reattempt": "^3.0.0", + "purescript-aff": "^4.0.0", + "purescript-aff-reattempt": "d0bdeaf46c2bf24baa4c0742c339227669dd905d", + "purescript-aff-promise": "^1.0.0", "purescript-dom": "^4.3.1" } } diff --git a/src/Selenium.js b/src/Selenium.js index baa4b6e..f5a4a5d 100644 --- a/src/Selenium.js +++ b/src/Selenium.js @@ -7,17 +7,17 @@ var webdriver = require("selenium-webdriver"), fs = require("fs"), path = require("path"); -exports.get = function(driver) { +exports._get = function(driver) { return function(url) { - return function(cb, eb) { + return function(eb, cb) { driver.get(url).then(cb, eb); }; }; }; -exports.setFileDetector = function(driver) { +exports._setFileDetector = function(driver) { return function(detector) { - return function (cb, eb) { + return function (eb, cb) { try { driver.setFileDetector(detector); return cb(); @@ -28,14 +28,14 @@ exports.setFileDetector = function(driver) { }; }; -exports.wait = function(check) { +exports._wait = function(promise) { return function(timeout) { return function(driver) { - return function(cb, eb) { - var p = new webdriver.promise.Promise(check); - driver.wait(p, timeout) + return function(eb, cb) { + try { + driver.wait(promise, timeout) .then(function() { - p.then(function(res) { + promise().then(function(res) { if (res) { cb(); } else { @@ -43,19 +43,23 @@ exports.wait = function(check) { } }, eb); }, eb); + } + catch (e) { + eb(e); + } }; }; }; }; -exports.quit = function(driver) { - return function(cb, eb) { +exports._quit = function(driver) { + return function(eb, cb) { driver.quit().then(cb,eb); }; }; -exports.byClassName = function(className) { - return function(cb, eb) { +exports._byClassName = function(className) { + return function(eb, cb) { try { return cb(By.className(className)); } @@ -65,8 +69,8 @@ exports.byClassName = function(className) { }; }; -exports.byCss = function(selector) { - return function(cb, eb) { +exports._byCss = function(selector) { + return function(eb, cb) { try { return cb(By.css(selector)); } @@ -76,8 +80,8 @@ exports.byCss = function(selector) { }; }; -exports.byId = function(id) { - return function(cb, eb) { +exports._byId = function(id) { + return function(eb, cb) { try { return cb(By.id(id)); } @@ -87,8 +91,8 @@ exports.byId = function(id) { }; }; -exports.byName = function(name) { - return function(cb, eb) { +exports._byName = function(name) { + return function(eb, cb) { try { return cb(By.name(name)); } @@ -98,8 +102,8 @@ exports.byName = function(name) { }; }; -exports.byXPath = function(xpath) { - return function(cb, eb) { +exports._byXPath = function(xpath) { + return function(eb, cb) { try { return cb(By.xpath(xpath)); } @@ -113,7 +117,7 @@ function _find(nothing) { return function(just) { return function(driver) { return function(by) { - return function(cb) { + return function(eb, cb) { driver.findElement(by).then(function(el) { return cb(just(el)); }, function() { @@ -127,7 +131,7 @@ function _find(nothing) { function _exact(driver) { return function(by) { - return function(cb, eb) { + return function(eb, cb) { driver.findElement(by).then(cb, eb); }; }; @@ -137,8 +141,8 @@ exports.showLocator = function(locator) { return locator.toString(); } -exports.findExact = _exact; -exports.childExact = _exact; +exports._findExact = _exact; +exports._childExact = _exact; exports._findElement = _find; @@ -146,7 +150,7 @@ exports._findChild = _find; function _finds(parent) { return function(by) { - return function(cb, eb) { + return function(eb, cb) { return parent.findElements(by).then(function(children) { return cb(children); }, eb) @@ -157,17 +161,17 @@ function _finds(parent) { exports._findElements = _finds; exports._findChildren = _finds; -exports.sendKeysEl = function(keys) { +exports._sendKeysEl = function(keys) { return function(el) { - return function(cb, eb) { + return function(eb, cb) { el.sendKeys(keys).then(cb, eb); }; }; }; -exports.getCssValue = function(el) { +exports._getCssValue = function(el) { return function(str) { - return function(cb, eb) { + return function(eb, cb) { return el.getCssValue(str).then(cb, eb); }; }; @@ -177,7 +181,7 @@ exports._getAttribute = function(nothing) { return function(just) { return function(el) { return function(str) { - return function(cb, eb) { + return function(eb, cb) { return el.getAttribute(str).then(function(attr) { if (attr === null) { cb(nothing); @@ -191,64 +195,64 @@ exports._getAttribute = function(nothing) { }; }; -exports.getText = function(el) { - return function(cb, eb) { +exports._getText = function(el) { + return function(eb, cb) { return el.getText().then(cb, eb); }; }; -exports.isDisplayed = function(el) { - return function(cb, eb) { +exports._isDisplayed = function(el) { + return function(eb, cb) { return el.isDisplayed().then(function(is) { return cb(is); }, eb); }; }; -exports.isEnabled = function(el) { - return function(cb, eb) { +exports._isEnabled = function(el) { + return function(eb, cb) { return el.isEnabled().then(function(is) { return cb(is); }, eb); }; }; -exports.getCurrentUrl = function(driver) { - return function(cb, eb) { +exports._getCurrentUrl = function(driver) { + return function(eb, cb) { return driver.getCurrentUrl().then(cb, eb); }; }; -exports.getTitle = function(driver) { - return function(cb, eb) { +exports._getTitle = function(driver) { + return function(eb, cb) { return driver.getTitle().then(cb, eb); }; }; -exports.navigateBack = function(driver) { - return function(cb, eb) { +exports._navigateBack = function(driver) { + return function(eb, cb) { var n = new webdriver.WebDriver.Navigation(driver); return n.back().then(cb, eb); }; }; -exports.navigateForward = function(driver) { - return function(cb, eb) { +exports._navigateForward = function(driver) { + return function(eb, cb) { var n = new webdriver.WebDriver.Navigation(driver); return n.forward().then(cb, eb); }; }; -exports.refresh = function(driver) { - return function(cb, eb) { +exports._refresh = function(driver) { + return function(eb, cb) { var n = new webdriver.WebDriver.Navigation(driver); return n.refresh().then(cb, eb); }; }; -exports.navigateTo = function(url) { +exports._navigateTo = function(url) { return function(driver) { - return function(cb, eb) { + return function(eb, cb) { var n = new webdriver.WebDriver.Navigation(driver); return n.to(url).then(cb, eb); }; @@ -256,66 +260,66 @@ exports.navigateTo = function(url) { }; -exports.getInnerHtml = function(el) { - return function(cb, eb) { +exports._getInnerHtml = function(el) { + return function(eb, cb) { el.getInnerHtml().then(cb, eb); }; }; -exports.getSize = function(el) { - return function(cb, eb) { +exports._getSize = function(el) { + return function(eb, cb) { el.getSize().then(cb, eb); }; }; -exports.getLocation = function(el) { - return function(cb, eb) { +exports._getLocation = function(el) { + return function(eb, cb) { el.getLocation().then(cb, eb); }; }; function execute(driver) { return function(action) { - return function(cb, eb) { + return function(eb, cb) { driver.executeScript(action).then(cb, eb); }; }; } -exports.executeStr = execute; +exports._executeStr = execute; -exports.affLocator = function(aff) { - return function(cb, eb) { +exports._affLocator = function(elementToAff) { + return function(eb, cb) { return cb(function(el) { - return new webdriver.promise.Promise(aff(el)); + return elementToAff(el)(); }); }; }; -exports.clearEl = function(el) { - return function(cb, eb) { +exports._clearEl = function(el) { + return function(eb, cb) { el.clear().then(cb, eb); }; }; -exports.clickEl = function(el) { - return function(cb, eb) { +exports._clickEl = function(el) { + return function(eb, cb) { el.click().then(cb, eb); }; }; -exports.takeScreenshot = function(driver) { - return function(cb, eb) { +exports._takeScreenshot = function(driver) { + return function(eb, cb) { driver.takeScreenshot() .then(cb, eb); }; }; -exports.saveScreenshot = function(fileName) { +exports._saveScreenshot = function(fileName) { return function(driver) { - return function(cb, eb) { + return function(eb, cb) { driver.takeScreenshot() .then(function(str) { fs.writeFile(path.resolve(fileName), @@ -334,8 +338,8 @@ exports.saveScreenshot = function(fileName) { }; -exports.getWindow = function(window) { - return function(cb, eb) { +exports._getWindow = function(window) { + return function(eb, cb) { try { return cb(window.manage().window()); } @@ -345,47 +349,47 @@ exports.getWindow = function(window) { }; }; -exports.getWindowPosition = function(window) { - return function(cb, eb) { +exports._getWindowPosition = function(window) { + return function(eb, cb) { return window.getPosition() .then(cb, eb); }; }; -exports.getWindowSize = function(window) { - return function(cb, eb) { +exports._getWindowSize = function(window) { + return function(eb, cb) { return window.getSize() .then(cb, eb); }; }; -exports.maximizeWindow = function(window) { - return function(cb, eb) { +exports._maximizeWindow = function(window) { + return function(eb, cb) { return window.maximize() .then(cb, eb); }; }; -exports.setWindowPosition = function(loc) { +exports._setWindowPosition = function(loc) { return function(window) { - return function(cb, eb) { + return function(eb, cb) { return window.setPosition(loc.x, loc.y) .then(cb, eb); }; }; }; -exports.setWindowSize = function(size) { +exports._setWindowSize = function(size) { return function(window) { - return function(cb, eb) { + return function(eb, cb) { return window.setSize(size.width, size.height) .then(cb, eb); }; }; }; -exports.getWindowScroll = function(driver) { - return function(cb, eb) { +exports._getWindowScroll = function(driver) { + return function(eb, cb) { driver.executeScript(function() { return { x: window.scrollX, @@ -395,28 +399,28 @@ exports.getWindowScroll = function(driver) { }; }; -exports.getWindowHandle = function(driver) { - return function(cb, eb) { +exports._getWindowHandle = function(driver) { + return function(eb, cb) { driver.getWindowHandle().then(cb, eb); }; }; exports._getAllWindowHandles = function(driver) { - return function(cb, eb) { + return function(eb, cb) { driver.getAllWindowHandles().then(cb, eb); }; }; -exports.switchTo = function(handle) { +exports._switchTo = function(handle) { return function(driver) { - return function(cb, eb) { + return function(eb, cb) { return driver.switchTo().window(handle).then(cb, eb); }; }; }; -exports.close = function(driver) { - return function(cb, eb) { +exports._close = function(driver) { + return function(eb, cb) { return driver.close().then(cb, eb); }; }; diff --git a/src/Selenium.purs b/src/Selenium.purs index faa0704..0dbfff7 100644 --- a/src/Selenium.purs +++ b/src/Selenium.purs @@ -51,9 +51,14 @@ module Selenium ) where import Prelude + import Control.Monad.Aff (Aff, attempt) +import Control.Monad.Aff.Compat (EffFnAff, fromEffFnAff) +import Control.Monad.Eff (Eff) import Control.Monad.Eff.Exception (error) import Control.Monad.Error.Class (throwError) +import Control.Promise (Promise) +import Control.Promise (fromAff) as Promise import Data.Array (uncons) import Data.Either (either) import Data.Foreign (Foreign) @@ -61,67 +66,65 @@ import Data.Maybe (Maybe(..)) import Data.Time.Duration (Milliseconds) import Data.Tuple (Tuple(..)) import Data.Unfoldable (class Unfoldable, unfoldr) +import Selenium.Types (SELENIUM, Driver, WindowHandle, Location, Window, Size, Element, FileDetector, Locator) + +foreign import _get + ∷ ∀ e + . Driver + → String + → EffFnAff (selenium ∷ SELENIUM|e) Unit + +foreign import _wait + ∷ ∀ e + . Eff (selenium ∷ SELENIUM|e) (Promise Boolean) + → Milliseconds + → Driver + → EffFnAff (selenium ∷ SELENIUM|e) Unit + +foreign import _quit + ∷ ∀ e + . Driver + → EffFnAff (selenium ∷ SELENIUM|e) Unit -import Selenium.Types - ( SELENIUM - , Driver - , WindowHandle - , Location - , Window - , Size - , Element - , FileDetector - , Locator - ) - --- | Go to url -foreign import get +get ∷ ∀ e . Driver → String → Aff (selenium ∷ SELENIUM|e) Unit +get driver str = fromEffFnAff $ _get driver str -- | Wait until first argument returns 'true'. If it returns false an error will be raised -foreign import wait +wait ∷ ∀ e . Aff (selenium ∷ SELENIUM|e) Boolean → Milliseconds → Driver → Aff (selenium ∷ SELENIUM|e) Unit +wait action time driver = fromEffFnAff $ _wait (Promise.fromAff action) time driver -- | Finalizer -foreign import quit +quit ∷ ∀ e . Driver → Aff (selenium ∷ SELENIUM|e) Unit +quit driver = fromEffFnAff $ _quit driver -- LOCATOR BUILDERS -foreign import byClassName - ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator -foreign import byCss - ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator -foreign import byId - ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator -foreign import byName - ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator -foreign import byXPath - ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator --- | Build locator from asynchronous function returning element. --- | I.e. this locator will find first visible element with `.common-element` class --- | ```purescript --- | affLocator \el → do --- | commonElements ← byCss ".common-element" >>= findElements el --- | flagedElements ← traverse (\el → Tuple el <$> isVisible el) commonElements --- | maybe err pure $ foldl foldFn Nothing flagedElements --- | where --- | err = throwError $ error "all common elements are not visible" --- | foldFn Nothing (Tuple el true) = Just el --- | foldFn a _ = a --- | ``` -foreign import affLocator +foreign import _byClassName + ∷ ∀ e. String → EffFnAff (selenium ∷ SELENIUM|e) Locator +foreign import _byCss + ∷ ∀ e. String → EffFnAff (selenium ∷ SELENIUM|e) Locator +foreign import _byId + ∷ ∀ e. String → EffFnAff (selenium ∷ SELENIUM|e) Locator +foreign import _byName + ∷ ∀ e. String → EffFnAff (selenium ∷ SELENIUM|e) Locator +foreign import _byXPath + ∷ ∀ e. String → EffFnAff (selenium ∷ SELENIUM|e) Locator + +foreign import _affLocator ∷ ∀ e - . (Element → Aff (selenium ∷ SELENIUM|e) Element) - → Aff (selenium ∷ SELENIUM|e) Locator + . (Element → Eff (selenium ∷ SELENIUM|e) (Promise Element)) + → EffFnAff (selenium ∷ SELENIUM|e) Locator foreign import showLocator ∷ Locator @@ -133,7 +136,7 @@ foreign import _findElement → (a → Maybe a) → Driver → Locator - → Aff (selenium ∷ SELENIUM|e) (Maybe Element) + → EffFnAff (selenium ∷ SELENIUM|e) (Maybe Element) foreign import _findChild ∷ ∀ e a @@ -141,38 +144,99 @@ foreign import _findChild → (a → Maybe a) → Element → Locator - → Aff (selenium ∷ SELENIUM|e) (Maybe Element) + → EffFnAff (selenium ∷ SELENIUM|e) (Maybe Element) foreign import _findElements ∷ ∀ e . Driver → Locator - → Aff (selenium ∷ SELENIUM|e) (Array Element) + → EffFnAff (selenium ∷ SELENIUM|e) (Array Element) foreign import _findChildren ∷ ∀ e . Element → Locator - → Aff (selenium ∷ SELENIUM|e) (Array Element) + → EffFnAff (selenium ∷ SELENIUM|e) (Array Element) -foreign import findExact +foreign import _findExact ∷ ∀ e . Driver → Locator - → Aff (selenium ∷ SELENIUM|e) Element + → EffFnAff (selenium ∷ SELENIUM|e) Element -foreign import childExact +foreign import _childExact ∷ ∀ e . Element → Locator - → Aff (selenium ∷ SELENIUM|e) Element + → EffFnAff (selenium ∷ SELENIUM|e) Element + +-- LOCATOR BUILDERS +byClassName + ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator +byClassName className = fromEffFnAff $ _byClassName className + +byCss + ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator +byCss css = fromEffFnAff $ _byCss css + +byId + ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator +byId id = fromEffFnAff $ _byId id + +byName + ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator +byName name = fromEffFnAff $ _byName name + +byXPath + ∷ ∀ e. String → Aff (selenium ∷ SELENIUM|e) Locator +byXPath xpath = fromEffFnAff $ _byXPath xpath + +-- | Build locator from asynchronous function returning element. +-- | I.e. this locator will find first visible element with `.common-element` class +-- | ```purescript +-- | affLocator \el → do +-- | commonElements ← byCss ".common-element" >>= findElements el +-- | flagedElements ← traverse (\el → Tuple el <$> isVisible el) commonElements +-- | maybe err pure $ foldl foldFn Nothing flagedElements +-- | where +-- | err = throwError $ error "all common elements are not visible" +-- | foldFn Nothing (Tuple el true) = Just el +-- | foldFn a _ = a +-- | ``` +affLocator + ∷ ∀ e + . (Element → Aff (selenium ∷ SELENIUM|e) Element) + → Aff (selenium ∷ SELENIUM|e) Locator +affLocator locator = fromEffFnAff $ _affLocator (Promise.fromAff <<< locator) -- | Tries to find an element starting from `document`; will return `Nothing` if there -- | is no element can be found by locator findElement ∷ ∀ e. Driver → Locator → Aff (selenium ∷ SELENIUM|e) (Maybe Element) -findElement = - _findElement Nothing Just +findElement driver = + fromEffFnAff <<< _findElement Nothing Just driver + +-- | Same as `findElement` but starts searching from custom element +findChild + ∷ ∀ e + . Element + → Locator + → Aff (selenium ∷ SELENIUM|e) (Maybe Element) +findChild element = fromEffFnAff <<< _findChild Nothing Just element + +findExact + ∷ ∀ e + . Driver + → Locator + → Aff (selenium ∷ SELENIUM|e) Element +findExact driver = fromEffFnAff <<< _findExact driver + +childExact + ∷ ∀ e + . Element + → Locator + → Aff (selenium ∷ SELENIUM|e) Element +childExact element locator = fromEffFnAff $ _childExact element locator -- | Tries to find element and throws an error if it succeeds. loseElement @@ -190,15 +254,9 @@ loseElement driver locator = do findElements ∷ ∀ e f. (Unfoldable f) ⇒ Driver → Locator → Aff (selenium ∷ SELENIUM|e) (f Element) findElements driver locator = - map fromArray $ _findElements driver locator + map fromArray $ fromEffFnAff $ _findElements driver locator --- | Same as `findElement` but starts searching from custom element -findChild - ∷ ∀ e. Element → Locator → Aff (selenium ∷ SELENIUM|e) (Maybe Element) -findChild = - _findChild Nothing Just - --- | Same as `findElements` but starts searching from custom element +-- | Same a `findElements` but starts searching from custom element findChildren ∷ ∀ e f . (Unfoldable f) @@ -206,89 +264,201 @@ findChildren → Locator → Aff (selenium ∷SELENIUM|e) (f Element) findChildren el locator = - map fromArray $ _findChildren el locator + map fromArray $ fromEffFnAff $ _findChildren el locator + +foreign import _setFileDetector + ∷ ∀ e. Driver → FileDetector → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _navigateBack + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _navigateForward + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _refresh + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _navigateTo + ∷ ∀ e. String → Driver → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _getCurrentUrl + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) String +foreign import _getTitle + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) String +foreign import _executeStr + ∷ ∀ e. Driver → String → EffFnAff (selenium ∷ SELENIUM|e) Foreign +foreign import _sendKeysEl + ∷ ∀ e. String → Element → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _clickEl + ∷ ∀ e. Element → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _getCssValue + ∷ ∀ e. Element → String → EffFnAff (selenium ∷ SELENIUM|e) String +foreign import _getAttribute + ∷ ∀ e a + . Maybe a + → (a → Maybe a) + → Element + → String + → EffFnAff (selenium ∷ SELENIUM|e) (Maybe String) -foreign import setFileDetector +setFileDetector ∷ ∀ e. Driver → FileDetector → Aff (selenium ∷ SELENIUM|e) Unit -foreign import navigateBack +setFileDetector driver detector = fromEffFnAff $ _setFileDetector driver detector + +navigateBack ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) Unit -foreign import navigateForward +navigateBack driver = fromEffFnAff $ _navigateBack driver + +navigateForward ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) Unit -foreign import refresh +navigateForward driver = fromEffFnAff $ _navigateForward driver + +refresh ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) Unit -foreign import navigateTo +refresh driver = fromEffFnAff $ _refresh driver + +navigateTo ∷ ∀ e. String → Driver → Aff (selenium ∷ SELENIUM|e) Unit -foreign import getCurrentUrl +navigateTo uri driver = fromEffFnAff $ _navigateTo uri driver + +getCurrentUrl ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) String -foreign import getTitle +getCurrentUrl driver = fromEffFnAff $ _getCurrentUrl driver + +getTitle ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) String +getTitle driver = fromEffFnAff $ _getTitle driver + -- | Executes javascript script from `String` argument. -foreign import executeStr +executeStr ∷ ∀ e. Driver → String → Aff (selenium ∷ SELENIUM|e) Foreign -foreign import sendKeysEl +executeStr driver strToExecute = fromEffFnAff $ _executeStr driver strToExecute + +sendKeysEl ∷ ∀ e. String → Element → Aff (selenium ∷ SELENIUM|e) Unit -foreign import clickEl +sendKeysEl key element = fromEffFnAff $ _sendKeysEl key element + +clickEl ∷ ∀ e. Element → Aff (selenium ∷ SELENIUM|e) Unit -foreign import getCssValue +clickEl element = fromEffFnAff $ _clickEl element + +getCssValue ∷ ∀ e. Element → String → Aff (selenium ∷ SELENIUM|e) String -foreign import _getAttribute - ∷ ∀ e a - . Maybe a - → (a → Maybe a) - → Element - → String - → Aff (selenium ∷ SELENIUM|e) (Maybe String) +getCssValue element attribute = fromEffFnAff $ _getCssValue element attribute -- | Tries to find an element starting from `document`; will return `Nothing` if there -- | is no element can be found by locator getAttribute ∷ ∀ e. Element → String → Aff (selenium ∷ SELENIUM|e) (Maybe String) -getAttribute = _getAttribute Nothing Just +getAttribute element string = fromEffFnAff $ _getAttribute Nothing Just element string + +foreign import _getText + ∷ ∀ e. Element → EffFnAff (selenium ∷ SELENIUM|e) String +foreign import _isDisplayed + ∷ ∀ e. Element → EffFnAff (selenium ∷ SELENIUM|e) Boolean +foreign import _isEnabled + ∷ ∀ e. Element → EffFnAff (selenium ∷ SELENIUM|e) Boolean +foreign import _getInnerHtml + ∷ ∀ e. Element → EffFnAff (selenium ∷ SELENIUM|e) String +foreign import _getSize + ∷ ∀ e. Element → EffFnAff (selenium ∷ SELENIUM|e) Size +foreign import _getLocation + ∷ ∀ e. Element → EffFnAff (selenium ∷ SELENIUM|e) Location +foreign import _clearEl + ∷ ∀ e. Element → EffFnAff (selenium ∷ SELENIUM|e) Unit + +foreign import _takeScreenshot + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM |e) String +foreign import _saveScreenshot + ∷ ∀ e. String → Driver → EffFnAff (selenium ∷ SELENIUM |e) Unit -foreign import getText +foreign import _getWindow + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) Window +foreign import _getWindowPosition + ∷ ∀ e. Window → EffFnAff (selenium ∷ SELENIUM|e) Location +foreign import _getWindowSize + ∷ ∀ e. Window → EffFnAff (selenium ∷ SELENIUM|e) Size +foreign import _maximizeWindow + ∷ ∀ e. Window → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _setWindowPosition + ∷ ∀ e. Location → Window → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _setWindowSize + ∷ ∀ e. Size → Window → EffFnAff (selenium ∷ SELENIUM|e) Unit +foreign import _getWindowScroll + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) Location +foreign import _getWindowHandle + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) WindowHandle +foreign import _getAllWindowHandles + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) (Array WindowHandle) + +getText ∷ ∀ e. Element → Aff (selenium ∷ SELENIUM|e) String -foreign import isDisplayed +getText element = fromEffFnAff $ _getText element + +isDisplayed ∷ ∀ e. Element → Aff (selenium ∷ SELENIUM|e) Boolean -foreign import isEnabled +isDisplayed element = fromEffFnAff $ _isDisplayed element + +isEnabled ∷ ∀ e. Element → Aff (selenium ∷ SELENIUM|e) Boolean -foreign import getInnerHtml +isEnabled element = fromEffFnAff $ _isEnabled element + +getInnerHtml ∷ ∀ e. Element → Aff (selenium ∷ SELENIUM|e) String -foreign import getSize +getInnerHtml element = fromEffFnAff $ _getInnerHtml element + +getSize ∷ ∀ e. Element → Aff (selenium ∷ SELENIUM|e) Size -foreign import getLocation +getSize element = fromEffFnAff $ _getSize element + +getLocation ∷ ∀ e. Element → Aff (selenium ∷ SELENIUM|e) Location +getLocation element = fromEffFnAff $ _getLocation element + -- | Clear `value` of element, if it has no value will do nothing. -- | If `value` is weakly referenced by `virtual-dom` (`purescript-halogen`) -- | will not work -- to clear such inputs one should use direct signal from -- | `Selenium.ActionSequence` -foreign import clearEl +clearEl ∷ ∀ e. Element → Aff (selenium ∷ SELENIUM|e) Unit +clearEl element = fromEffFnAff $ _clearEl element -- | Returns png base64 encoded png image -foreign import takeScreenshot +takeScreenshot ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM |e) String +takeScreenshot driver = fromEffFnAff $ _takeScreenshot driver -- | Saves screenshot to path -foreign import saveScreenshot +saveScreenshot ∷ ∀ e. String → Driver → Aff (selenium ∷ SELENIUM |e) Unit -foreign import getWindow +saveScreenshot name driver = fromEffFnAff $ _saveScreenshot name driver + +getWindow ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) Window -foreign import getWindowPosition +getWindow driver = fromEffFnAff $ _getWindow driver + +getWindowPosition ∷ ∀ e. Window → Aff (selenium ∷ SELENIUM|e) Location -foreign import getWindowSize +getWindowPosition window = fromEffFnAff $ _getWindowPosition window + +getWindowSize ∷ ∀ e. Window → Aff (selenium ∷ SELENIUM|e) Size -foreign import maximizeWindow +getWindowSize window = fromEffFnAff $ _getWindowSize window + +maximizeWindow ∷ ∀ e. Window → Aff (selenium ∷ SELENIUM|e) Unit -foreign import setWindowPosition +maximizeWindow window = fromEffFnAff $ _maximizeWindow window + +setWindowPosition ∷ ∀ e. Location → Window → Aff (selenium ∷ SELENIUM|e) Unit -foreign import setWindowSize +setWindowPosition location window = fromEffFnAff $ _setWindowPosition location window + +setWindowSize ∷ ∀ e. Size → Window → Aff (selenium ∷ SELENIUM|e) Unit -foreign import getWindowScroll +setWindowSize size window = fromEffFnAff $ _setWindowSize size window + +getWindowScroll ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) Location -foreign import getWindowHandle +getWindowScroll driver = fromEffFnAff $ _getWindowScroll driver + +getWindowHandle ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) WindowHandle -foreign import _getAllWindowHandles - ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) (Array WindowHandle) +getWindowHandle driver = fromEffFnAff $ _getWindowHandle driver getAllWindowHandles ∷ ∀ f e @@ -296,14 +466,22 @@ getAllWindowHandles ⇒ Driver → Aff (selenium ∷ SELENIUM |e) (f WindowHandle) getAllWindowHandles driver = - map fromArray $ _getAllWindowHandles driver + map fromArray $ fromEffFnAff $ _getAllWindowHandles driver fromArray ∷ ∀ a f. (Unfoldable f) ⇒ Array a → f a fromArray = unfoldr (\xs → (\rec → Tuple rec.head rec.tail) <$> uncons xs) -foreign import switchTo +foreign import _switchTo + ∷ ∀ e. WindowHandle → Driver → EffFnAff (selenium ∷ SELENIUM |e) Unit +foreign import _close + ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM |e) Unit + +switchTo ∷ ∀ e. WindowHandle → Driver → Aff (selenium ∷ SELENIUM |e) Unit -foreign import close +switchTo handle driver = fromEffFnAff $ _switchTo handle driver + +close ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM |e) Unit +close driver = fromEffFnAff $ _close driver diff --git a/src/Selenium/ActionSequence.js b/src/Selenium/ActionSequence.js index e7641d2..c3bb38a 100644 --- a/src/Selenium/ActionSequence.js +++ b/src/Selenium/ActionSequence.js @@ -1,8 +1,8 @@ // module Selenium.ActionSequence var webdriver = require("selenium-webdriver"); -exports.newSequence = function(driver) { - return function(cb, eb) { +exports._newSequence = function(driver) { + return function(eb, cb) { try { return cb(new webdriver.ActionSequence(driver)); } @@ -12,8 +12,8 @@ exports.newSequence = function(driver) { }; }; -exports.performSequence = function(sequence) { - return function(cb, eb) { +exports._performSequence = function(sequence) { + return function(eb, cb) { try { return sequence.perform().then(cb, eb); } diff --git a/src/Selenium/ActionSequence.purs b/src/Selenium/ActionSequence.purs index dd94f15..99342c7 100644 --- a/src/Selenium/ActionSequence.purs +++ b/src/Selenium/ActionSequence.purs @@ -19,13 +19,12 @@ module Selenium.ActionSequence import Prelude import Control.Monad.Aff (Aff) +import Control.Monad.Aff.Compat (EffFnAff, fromEffFnAff) import Control.Monad.Writer (Writer, execWriter) import Control.Monad.Writer.Class (tell) - import Data.Foldable (foldl) import Data.Function.Uncurried (Fn3, Fn2, runFn3, runFn2) import Data.List (List, singleton) - import Selenium.MouseButton (leftButton) import Selenium.Types (ActionSequence, Location, Element, ControlKey, MouseButton, SELENIUM, Driver) @@ -127,9 +126,14 @@ interpret commands initSeq = foldFn seq (DnDToLocation el tgt) = runFn3 _dndToLocation seq el tgt -foreign import newSequence ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) ActionSequence +foreign import _newSequence ∷ ∀ e. Driver → EffFnAff (selenium ∷ SELENIUM|e) ActionSequence +foreign import _performSequence ∷ ∀ e. ActionSequence → EffFnAff (selenium ∷ SELENIUM |e) Unit + +newSequence ∷ ∀ e. Driver → Aff (selenium ∷ SELENIUM|e) ActionSequence +newSequence = fromEffFnAff <<< _newSequence -foreign import performSequence ∷ ∀ e. ActionSequence → Aff (selenium ∷ SELENIUM |e) Unit +performSequence ∷ ∀ e. ActionSequence → Aff (selenium ∷ SELENIUM |e) Unit +performSequence = fromEffFnAff <<< _performSequence foreign import _click ∷ Fn3 ActionSequence MouseButton Element ActionSequence foreign import _doubleClick ∷ Fn3 ActionSequence MouseButton Element ActionSequence diff --git a/src/Selenium/Builder.js b/src/Selenium/Builder.js index 3749f70..d97594e 100644 --- a/src/Selenium/Builder.js +++ b/src/Selenium/Builder.js @@ -2,7 +2,7 @@ var webdriver = require("selenium-webdriver"); -exports._newBuilder = function(cb, eb) { +exports._newBuilder = function(eb, cb) { try { return cb(new webdriver.Builder()); } @@ -12,7 +12,7 @@ exports._newBuilder = function(cb, eb) { }; exports._build = function(builder) { - return function(cb, eb) { + return function(eb, cb) { try { return cb(builder.build()); } diff --git a/src/Selenium/Builder.purs b/src/Selenium/Builder.purs index ca17428..3a3209e 100644 --- a/src/Selenium/Builder.purs +++ b/src/Selenium/Builder.purs @@ -12,14 +12,13 @@ module Selenium.Builder import Prelude import Control.Monad.Aff (Aff) +import Control.Monad.Aff.Compat (EffFnAff, fromEffFnAff) import Control.Monad.Writer (Writer, execWriter) import Control.Monad.Writer.Class (tell) - import Data.Foldable (foldl) import Data.Function.Uncurried (Fn2, runFn2) import Data.List (List(..), singleton) import Data.Tuple (Tuple(..)) - import Selenium.Browser (Browser, browserCapabilities, platformCapabilities, versionCapabilities) import Selenium.Capabilities (Capabilities, emptyCapabilities) import Selenium.Types (Builder, ScrollBehaviour, Driver, SELENIUM, SafariOptions, ProxyConfig, OperaOptions, LoggingPrefs, IEOptions, FirefoxOptions, ControlFlow, ChromeOptions) @@ -82,10 +81,10 @@ browser = withCapabilities <<< browserCapabilities build ∷ ∀ e. Build Unit → Aff (selenium ∷ SELENIUM|e) Driver build dsl = do - builder ← _newBuilder + builder ← fromEffFnAff _newBuilder case execWriter $ unBuild dsl of Tuple capabilities commands → - _build $ runFn2 _withCapabilities (interpret commands builder) capabilities + fromEffFnAff $ _build $ runFn2 _withCapabilities (interpret commands builder) capabilities interpret ∷ List Command → Builder → Builder interpret commands initialBuilder = foldl foldFn initialBuilder commands @@ -96,8 +95,8 @@ interpret commands initialBuilder = foldl foldFn initialBuilder commands foldFn b _ = b -foreign import _newBuilder ∷ ∀ e. Aff (selenium ∷ SELENIUM|e) Builder -foreign import _build ∷ ∀ e. Builder → Aff (selenium ∷ SELENIUM|e) Driver +foreign import _newBuilder ∷ ∀ e. EffFnAff (selenium ∷ SELENIUM|e) Builder +foreign import _build ∷ ∀ e. Builder → EffFnAff (selenium ∷ SELENIUM|e) Driver foreign import _usingServer ∷ Fn2 Builder String Builder foreign import _setScrollBehaviour ∷ Fn2 Builder ScrollBehaviour Builder diff --git a/src/Selenium/FFProfile.js b/src/Selenium/FFProfile.js index 3ccab3b..a7d8dc3 100644 --- a/src/Selenium/FFProfile.js +++ b/src/Selenium/FFProfile.js @@ -1,6 +1,6 @@ // module Selenium.FFProfile -exports._newFFProfile = function(cb, eb) { +exports._newFFProfile = function(eb, cb) { var FirefoxProfile = require('selenium-webdriver/firefox/profile.js').Profile; return cb(new FirefoxProfile()); }; @@ -15,7 +15,7 @@ exports._setFFPreference = function(key) { }; exports._encode = function(p) { - return function(cb, eb) { + return function(eb, cb) { p.encode() .then(function(c) { return cb({firefox_profile: c});}, eb); }; diff --git a/src/Selenium/FFProfile.purs b/src/Selenium/FFProfile.purs index 1f5d99d..a49159c 100644 --- a/src/Selenium/FFProfile.purs +++ b/src/Selenium/FFProfile.purs @@ -16,16 +16,14 @@ module Selenium.FFProfile import Prelude import Control.Monad.Aff (Aff) +import Control.Monad.Aff.Compat (EffFnAff, fromEffFnAff) import Control.Monad.Writer (Writer, execWriter) import Control.Monad.Writer.Class (tell) - import Data.Foldable (foldl) import Data.Foreign (Foreign) import Data.List (List, singleton) - import Selenium.Capabilities (Capabilities) import Selenium.Types (SELENIUM) - import Unsafe.Coerce (unsafeCoerce) foreign import data FFProfile ∷ Type @@ -73,8 +71,8 @@ setBoolPreference key = setPreference key <<< boolToFFPreference buildFFProfile ∷ ∀ e. FFProfileBuild Unit → Aff (selenium ∷ SELENIUM|e) Capabilities buildFFProfile commands = do - profile ← interpret (execWriter $ unFFProfileBuild commands) <$> _newFFProfile - _encode profile + profile ← interpret (execWriter $ unFFProfileBuild commands) <$> fromEffFnAff _newFFProfile + fromEffFnAff $ _encode profile interpret ∷ List Command → FFProfile→ FFProfile interpret commands b = foldl foldFn b commands @@ -84,8 +82,8 @@ interpret commands b = foldl foldFn b commands foreign import _setFFPreference ∷ String → FFPreference → FFProfile → FFProfile -foreign import _newFFProfile ∷ ∀ e. Aff (selenium ∷ SELENIUM|e) FFProfile -foreign import _encode ∷ ∀ e. FFProfile → Aff (selenium ∷ SELENIUM|e) Capabilities +foreign import _newFFProfile ∷ ∀ e. EffFnAff (selenium ∷ SELENIUM|e) FFProfile +foreign import _encode ∷ ∀ e. FFProfile → EffFnAff (selenium ∷ SELENIUM|e) Capabilities intToFFPreference ∷ Int → FFPreference diff --git a/test/Main.purs b/test/Main.purs index 1ef6c3c..ca4ae01 100644 --- a/test/Main.purs +++ b/test/Main.purs @@ -1,6 +1,7 @@ module Test.Main where import Prelude + import Control.Monad.Aff (launchAff, delay) import Control.Monad.Aff.Console (log) import Control.Monad.Eff (Eff) @@ -8,7 +9,7 @@ import Control.Monad.Eff.Console (CONSOLE) import Control.Monad.Eff.Exception (EXCEPTION) import Data.Maybe (maybe) import Data.Time.Duration (Milliseconds(..)) -import Selenium (findElement, byName, get, getTitle, quit, wait, clickEl, sendKeysEl) +import Selenium (byCss, byName, clickEl, findElement, get, getTitle, quit, sendKeysEl, wait) import Selenium.Browser (Browser(..)) import Selenium.Builder (browser, build) import Selenium.Types (SELENIUM) @@ -22,11 +23,11 @@ main = do findElement driver >>= maybe noInput (goInput driver) where - noInput = void $ log "No input, sorry :(" + noInput = void (log "No input, sorry :(") goInput driver el = do sendKeysEl "webdriver" el - byName "btnG" >>= + byCss ".ds .lsbb button.lsb" >>= findElement driver >>= maybe noButton (goButton driver)