Testing a user interface is a challenging task in programming. However, new tools have emerged to make this better feasible. Using the setup of an web app using the SAFE template, canopy is a testing library that enables UI testing in the browser.
One of the best programming practices is the use of a REPL (Run Evaluate Print Loop). F# uses script files and the FSI for this process. Normally I develop code in an script file run it in the REPL and continue until I have created a specific feature. Ideally, the same can be applied to setting up and running tests.
The first step is to import the needed libraries in the script file. With the pakket manager Paket, there is an option to have a script file generated that will reference all the needed libraries for a specific project. This can be done command line:
Or a directive can be added in the paket.dependencies file:
The resulting references script file can be loaded in the script file used to create the tests. First the project is started up for development. Then the script file with tests is run in the REPL. This will fire up a browser, load the application and run the UI tests:
#load "./../../../.paket/load/net472/UITests/uitests.group.fsx" open System.IO open canopy.runner.classic open canopy.configuration open canopy.classic open canopy.types let filepath = __SOURCE_DIRECTORY__ let driverpath = @"./../../../packages/uitests/Selenium.WebDriver.ChromeDriver/driver/win32" let chromepath = Path.Combine(filepath, driverpath) type PageIds = | Parent of string * PageIds list | Child of string module Literals = // starting div for the elmish app [<Literal>] let appId = "#elmish-app" [<Literal>] let homePageId = "#homepage" [<Literal>] let appBarId = "#appbar" [<Literal>] let bottomBarId = "#bottombar" [<Literal>] let bodyId = "#body" [<Literal>] let title = "GenPRES" let checkPageDivs ids = let rec check previd ids = match ids with |  -> () | p :: cs -> match p with | Child id -> if previd = "" then sprintf "checking single: %s" id |> describe displayed id else sprintf "checking child: %s of parent: %s" id previd |> describe (element previd |> elementWithin id) |> displayed | Parent(p, cs_) -> if previd = "" then sprintf "checking main: %s" p |> describe displayed p else sprintf "checking child: %s of parent: %s" p previd |> describe (element previd |> elementWithin p) |> displayed check p cs_ check previd cs check "" ids chromeDir <- chromepath suites <- [ suite() ] start chrome "check if the app is correctly loaded" &&& fun _ -> // go to the url url "http://localhost:8080/" describe "checking loaded page ids for the home page" [ Parent(Literals.appId, [ Parent(Literals.homePageId, [ Child Literals.appBarId Child Literals.bodyId Child Literals.bottomBarId ]) ]) ] |> checkPageDivs describe "check if the page title is GenPRES" "GenPRES" === title() describe "check if the appbar title is GenPRES" let title = (element "#appbar" |> elementWithin "#title") "GenPRES" === (read title) run() quit()
The results look like: