📜 ⬆️ ⬇️

Working with AngularJS Protractor from C # and Java

Introduction


As the slogan on Angular.org proudly explains:
Angular is what HTML would be like: Angular is what HTML would have been - if it had been designed from the very beginning to create (web) applications . AngularJS was designed from the ground up to be tested. But many Selenium developers want to continue to use their existing Java or C # codebase and skills, but when they switch to AngularJS SPA and MVVM web applications for testing, Protractor , the leading AngularJS application testing tool, is also written in JavaScript.

Fortunately, Protractor is fairly easy to port to other languages ​​- it uses a small subset of the JsonWire protocol on which Selenium WebDriver is based, namely just one interface.


In a short time, the protractor-net project representing the port of the existing Protractor methods https://github.com/angular/protractor/blob/master/lib/clientsidescripts.js from Javascript in C # and then another project that performs the same task was added and developed. from java.
For testing, the site was chosen http://www.way2automation.com on which, among other things, there is a project for AngularJS,
http://www.way2automation.com/angularjs-protractor/banking .
')
tests represent the “standard” actions of the “client” and the “manager” of XYZ Bank to check the balance, create accounts, make payments, etc. - This allowed us to illustrate all the available methods. The test call is made from a project in C # and from Java

Code examples


C #


The “client” comes in, selects an account, deposits the amount, and when the transaction is over, checks the balance (there is also a withdrawal test - here it is not shown, see the archive).


image

   [TestFixture]
   public class Way2AutomationTests
   {
       private StringBuilder verificationErrors = new StringBuilder ();
       private IWebDriver driver;
       private NgWebDriver ngDriver;
       private WebDriverWait wait;
       private IAlert alert;
       private string alert_text;
       private Regex theReg;
       private MatchCollection theMatches;
       private Match theMatch;
       private Capture theCapture;
       private int wait_seconds = 3;
       private int highlight_timeout = 100;
       private Actions actions;
       private String base_url = "http://www.way2automation.com/angularjs-protractor/banking";

       [TestFixtureSetUp]
       public void SetUp ()
       {
           driver = new FirefoxDriver ();
           driver.Manage (). Timeouts (). SetScriptTimeout (TimeSpan.FromSeconds (60));
           ngDriver = new NgWebDriver (driver);
           wait = new WebDriverWait (driver, TimeSpan.FromSeconds (wait_seconds));
           actions = new Actions (driver);
       }

       [SetUp]
       public void NavigateToBankingExamplePage ()
       {
           driver.Navigate (). GoToUrl (base_url);
           ngDriver.Url = driver.Url;
       }

       [TestFixtureTearDown]
       public void TearDown ()
       {
           try
           {
               driver.Close ();
               driver.Quit ();
           }
           catch (Exception) {} 
           Assert.IsEmpty (verificationErrors.ToString ());
       }

       [Test]
       public void ShouldDeposit ()
       {
           ngDriver.FindElement (NgBy.ButtonText ("Customer Login")). Click ();
           ReadOnlyCollection <NgWebElement> ng_customers = ngDriver.FindElement (NgBy.Model ("custId")). FindElements (NgBy.Repeater ("cust in Customers"));
           // select customer to log in
           ng_customers.First (cust => Regex.IsMatch (cust.Text, "Harry Potter")). Click ();

           ngDriver.FindElement (NgBy.ButtonText ("Login")). Click ();
           ngDriver.FindElement (NgBy.Options ("account for account in Accounts")). Click ();


           NgWebElement ng_account_number_element = ngDriver.FindElement (NgBy.Binding ("accountNo"));
           int account_id = 0;
           int.TryParse (ng_account_number_element.Text.FindMatch (@ "(? <result> \ d +) $"), out account_id);
           Assert.AreNotEqual (0, account_id);

           int account_amount = -1;
           int.TryParse (ngDriver.FindElement (NgBy.Binding ("amount")). Text.FindMatch (@ "(? <result> \ d +) $"), out account_amount);
           Assert.AreNotEqual (-1, account_amount);

           ngDriver.FindElement (NgBy.PartialButtonText ("Deposit")). Click ();

           // core Selenium
           wait.Until (ExpectedConditions.ElementExists (By .CssSelector ("form [name = 'myForm']")));
           NgWebElement ng_form_element = new NgWebElement (ngDriver, driver.FindElement (By.CssSelector ("form [name = 'myForm']"))));


           NgWebElement ng_deposit_amount_element = ng_form_element.FindElement (NgBy.Model ("amount"));
           ng_deposit_amount_element.SendKeys ("100");

           NgWebElement ng_deposit_button_element = ng_form_element.FindElement (NgBy.ButtonText ("Deposit"));
           ngDriver.Highlight (ng_deposit_button_element);
           ng_deposit_button_element.Click ();
          
           // inspect status message
           var ng_message_element = ngDriver.FindElement (NgBy.Binding ("message"));
           StringAssert.Contains ("Deposit Successful", ng_message_element.Text);
           ngDriver.Highlight (ng_message_element);

           // re-read the amount
           int updated_account_amount = -1;            
           int.TryParse (ngDriver.FindElement (NgBy.Binding ("amount")). Text.FindMatch (@ "(? <result> \ d +) $"), out updated_account_amount);
           Assert.AreEqual (updated_account_amount, account_amount + 100);
       }

Java


The “client” enters, selects an account, looks at transactions, and knows how to find “Credit” records.


 @Test
     public void testListTransactions () throws Exception {
       // customer login
       ngDriver.findElement (NgBy.buttonText ("Customer Login")). click ();
       // select customer / account with transactions
       assertThat (ngDriver.findElement (NgBy.input ("custId")). getAttribute ("id"), equalTo ("userSelect"));

       Enumeration <WebElement> customers = Collections.enumeration (ngDriver.findElement (NgBy.model ("custId")). FindElements (NgBy.repeater ("cust in Customers"))));
      
       while (customers.hasMoreElements ()) {
         WebElement next_customer = customers.nextElement ();
         if (next_customer.getText (). indexOf ("Hermoine Granger")> = 0) {
           System.err.println (next_customer.getText ());
           next_customer.click ();
         }
       }
       NgWebElement login_element = ngDriver.findElement (NgBy.buttonText ("Login"));
       assertTrue (login_element.isEnabled ());
       login_element.click ();

       Enumeration <WebElement> accounts = Collections.enumeration (ngDriver.findElements (NgBy.options ("account for account in Accounts"))));
      
       while (accounts.hasMoreElements ()) {
         WebElement next_account = accounts.nextElement ();
         if (Integer.parseInt (next_account.getText ()) == 1001) {
           System.err.println (next_account.getText ());
           next_account.click ();
         }
       }
       // inspect transactions
       NgWebElement ng_transactions_element = ngDriver.findElement (NgBy.partialButtonText ("Transactions"));
       assertThat (ng_transactions_element.getText (), equalTo ("Transactions"));
       highlight (ng_transactions_element);
       ng_transactions_element.click ();
       wait.until (ExpectedConditions.visibilityOf (ngDriver.findElement (NgBy.repeater ("tx in transactions")). getWrappedElement ()));
       Iterator <WebElement> ng_transaction_type_columns = ngDriver.findElements (NgBy.repeaterColumn ("tx in transactions", "tx.type")). Iterator ();
       while (ng_transaction_type_columns.hasNext ()) {
         WebElement column = (WebElement) ng_transaction_type_columns.next ();
         if (column.getText (). isEmpty ()) {
           break;
         }
         if (column.getText (). equalsIgnoreCase ("Credit")) {
           highlight (column);
         }
       }
     }

For interactive testing, it is worth running a Selenium node and hub locally on port 4444

 @BeforeClass
 public static void setup () throws IOException {
   DesiredCapabilities capabilities = new DesiredCapabilities ("firefox", "", Platform.ANY);
   FirefoxProfile profile = new ProfilesIni (). GetProfile ("default");
   capabilities.setCapability ("firefox_profile", profile);
   seleniumDriver = new RemoteWebDriver (new URL ("http://127.0.0.1:4444/wd/hub"), capabilities);
   try {
     seleniumDriver.manage (). window (). setSize (new Dimension (600, 800));
     seleniumDriver.manage (). timeouts ()
       .pageLoadTimeout (50, TimeUnit.SECONDS)
       .implicitlyWait (20, TimeUnit.SECONDS)
       .setScriptTimeout (10, TimeUnit.SECONDS);
   } catch (Exception ex) {
     System.out.println (ex.toString ());
   }
   ngDriver = new NgWebDriver (seleniumDriver);
 }

For build we use

 @BeforeClass
 public static void setup () throws IOException {
   seleniumDriver = new PhantomJSDriver ();
   wait = new WebDriverWait (seleniumDriver, flexible_wait_interval);
   wait.pollingEvery (wait_polling_interval, TimeUnit.MILLISECONDS);
   actions = new Actions (seleniumDriver);
   ngDriver = new NgWebDriver (seleniumDriver);
 }

Special features


Synchronization


For dynamic pages, in addition to / instead of various, sometimes rather difficult to read, methods for checking how individual elements of a page look or what happens to them, which are offered by “core Selenium”:
elementSelectionStateToBe ( By locator, boolean selected)
checks that the item is selected or not
elementToBeClickable ( By locator)
item available
stalenessOf ( WebElement element)
until the item is no longer attached to the DOM
textToBePresentInElementLocated ( By locator, java.lang.String text)
the text is present in the element that this locator will find
textToBePresentInElementValue ( By locator, java.lang.String text)
the text present is present in the selected attribute of the element that this locator will find
visibilityOfAllElementsLocatedBy ( By locator)
checking that all items that the locator matches will be visible on the webpage

(snippet taken from https://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/support/ui/ExpectedConditions.html ) Protractor directly calls Angular

 public boolean isSelected () {
   this.ngDriver.WaitForAngular ();
   return this.element.isSelected ();
 }

 public void WaitForAngular () {
   if (! this.IgnoreSynchronization) {
     this.jsExecutor.executeAsyncScript (ClientSideScripts.WaitForAngular, this.rootElement);
   }
 }


sending

 var el = document.querySelector (arguments [0]);
 var callback = arguments [1];
 angular.element (el) .injector (). get ('$ browser'). notifyWhenNoOutstandingRequests (callback);

and / or

 var rootSelector = arguments [0];
 var callback = arguments [1];
 if (window.getAngularTestability) {
     window.getAngularTestability (el) .whenStable (callback);
     return;
 }

this method is called from all standard actions with page elements before the “core” method is called, for example:

 public bool Displayed
 {
   get {
     this.ngDriver.WaitForAngular ();
     return this.element.Displayed;
   }
 }


and as a result, the test site and the test script are well synchronized without any additional effort.

Creating tests



Instead of copying the CSS Selector and XPaths of the desired element, the test developer looks at the page template. www.way2automation.com/angularjs-protractor/banking/depositTx.html

   <span class = "error" ng-show = "message"> {{message}} </ span> <br>            


and its controller
www.way2automation.com/angularjs-protractor/banking/depositController.js

   if (txObj.success) {
       $ scope.message = "Deposit Successful";
   } else {
       $ scope.message = "Something went wrong. Please try again.";
   }


to check:
 // inspect message
 var ng_message = ngDriver.FindElement (NgBy.Binding ("message"));
 StringAssert.Contains ("Deposit Successful", ng_message.Text);
 ngDriver.Highlight (ng_message);



Additional features


Protractor allows not only to find , but also to calculate objects of interest to us:
   [Test]
   public void ShouldEvaluateTransactionDetails ()
   {
       ngDriver.FindElement (NgBy.ButtonText ("Customer Login")). Click ();
       // select customer / account with transactions
       ngDriver.FindElement (NgBy.Model ("custId")). FindElements (NgBy.Repeater ("cust in Customers")). First (cust => Regex.IsMatch (cust.Text, "Hermoine Granger")). Click ( );
       ngDriver.FindElement (NgBy.ButtonText ("Login")). Click ();
       ngDriver.FindElements (NgBy.Options ("account for account in Accounts")). First (account => Regex.IsMatch (account.Text, "1001")). Click ();

       // switch to transactions
       NgWebElement ng_transaction_list_button = ngDriver.FindElement (NgBy.PartialButtonText ("Transactions"));
       StringAssert.Contains ("Transactions", ng_transaction_list_button.Text);
       ngDriver.Highlight (ng_transaction_list_button);
       ng_transaction_list_button.Click ();

       // wait for transaction
       wait.Until (ExpectedConditions.ElementExists (NgBy.Repeater ("tx in transactions")));

       // examine first few transactions using Evaluate      
       ReadOnlyCollection <NgWebElement> ng_transactions = ngDriver.FindElements (NgBy.Repeater ("tx in transactions"));
       int cnt = 0;
       foreach (NgWebElement ng_current_transaction in ng_transactions) {
         if (cnt ++> 5) {break;  }
         StringAssert.IsMatch ("(? I: credit | debit)", ng_current_transaction.Evaluate ("tx.type"). ToString ());
         StringAssert.IsMatch (@ "(?: \ D +)", ng_current_transaction.Evaluate ("tx.amount"). ToString ());
         // 'tx.date' is in Javascript UTC format similar to UniversalSortableDateTimePattern in C # 
         var transaction_date = ng_current_transaction.Evaluate ("tx.date");
         StringAssert.IsMatch (@ "(?: \ D {4} \ - \ d {2} \ - \ d {2} T \ d {2}: \ d {2}: \ d {2}. \ D { 3} Z) ", transaction_date.ToString ());
       }
   }



The full list of tests for 08/01/2016:

C #




Java (desktop)


Java (CI / travis)



CI tests are simplified, testable pages are loaded directly from disk:

 String localFile = "bind_select_option_data_from_array_example.htm";
 URI uri = NgByIntegrationTest.class.getClassLoader (). GetResource (localFile) .toURI ();
 ngDriver.navigate (). to (uri.toString ()));

for a simplified script ex.
       Iterator <WebElement> options = ngDriver.findElements (NgBy.repeater ("option in options")). Iterator ();
       while (options.hasNext ()) {
                 WebElement option = (WebElement) options.next ();
        
         if (option.getText (). isEmpty ()) {
           break;
         }
         if (option.getText (). equalsIgnoreCase ("two")) {
                     option.click ();
                 }
             }
             NgWebElement element = ngDriver.findElement (NgBy.selectedOption ("myChoice"));
       assertThat (element.getText (), containsString ("two"));    


phantomJS driver is phantomJS

The article (more detailed version) was also published by me on the Code Project , and the most recent project archives are periodically downloaded there. Both projects on github:

- fully working, commits almost every day.

Source: https://habr.com/ru/post/274413/


All Articles