Showing posts with label Selenium 2. Show all posts
Showing posts with label Selenium 2. Show all posts

Thursday, July 19, 2012

Selenium 2 Java client: How to resolve apostrophes problem in xPath

It's pretty much common problem how to handle apostrophes if you have to use them in attribute values or any text which you somehow  use to narrow the search scope.

Assume we need to find the element having name attribute containing pattern "Someone's". Like

<file name="Someone's name"/>

The obvious way is to use the xPath like this: //file[contains(@name,'Someone's')]. However such the xPath won't be parsed properly as we've just broken the language syntax by placing three sequential single-quotes.

The good approach to resolve such the problem is to use the concat construction so that the example above will look like this:

//file[contains(@name,concat('Someone',"'",'s'))]

Here we split the string by three parts and enclosed the single-quote with double-quotes. This is the good solution, however we still need the approach to build such the constructions whatever the number of single-quotes have the place and whatever combination with regular text they have as well

Fortunately Java provides convenient facility to work with the strings and here I suggest the function helping to handle such the problem in your Selenium scenario.

 String resolveAprostophes(String item){
  if(!item.contains("'")){
   return "'" + item + "'";
  }
  StringBuilder finalString = new StringBuilder();
  finalString.append("concat('");
  finalString.append(item.replace("'", "',\"'\",'"));
  finalString.append("')");
  return finalString.toString();
 }
So you can use it in such the way:

String safeXPathToLocateTheElement = "//file[contains(@name," + resolveAprostophes("Someone's") + ")]";

And then use it in your regular look-up patterns

Wednesday, July 11, 2012

Selenium 2: How to scroll to element using WebDriver


Check new series of the articles. Review and user experience on test management systems. Functionality and usability.
-------------------

Some actions you take with WebElement's may fail due to those elements are not currently in the browser's visible area. So to make them able to interact you should first scroll vertically (or sometimes even horizontally :) ). However WebDriver does not provide explicit scrolling functionality.
Nevertheless you can bring the required element to the viewport. To to that just do the following

((Locatable)element).getLocationOnScreenOnceScrolledIntoView();

Where element is that WebElement you're going to interact with.

Wednesday, May 30, 2012

Selenium v.2.22 Released

Yesterday the new version of  Selenium (v.2.22) was released. Looking forward to test drive it :)

Here are the changes


v2.22.0
=======

Project:
  * Code grant from Google acknowledged in our copyright
    headers. Thanks, Google!

WebDriver:
  * JRE dependency upped to Java 6.
  * IE driver now uses the IEDriverServer. You may need to download
    this. Set the "useLegacyInternalServer" to boolean true if you
    need the old behaviour.
  * Standardized colour values returned from getCssValue are
    normalized to RGBA.
  * IE can use synthesized events if the capability
    "enableNativeEvents" is set to false. This is experimental and not
    expected to work properly.
  * Native events added for Firefox 12. 
  * Native events retained for Firefox 10, 11, and 3.x
  * Selenium-backed WebDriver can now return WebElements from
    executeScript.
  * With WebElement.getAttribute() a boolean attribute will return
    "null" if not present on an element.
  * A NoSuchWindowException will be thrown if the currently selected
    window is closed and another command is sent.
  * SafariDriver improved: frame switching, snapshot taking and JS
    executing added.
  * SafariDriver: changed message protocol. The 2.22.0 SafariDriver will
    not be backwards compatible with Selenium 2.21.
  * FIXED: 185: Appending screenshots to remote exceptions is now
    optional. Controlled via the "webdriver.remote.quietExceptions"
    capability.
  * FIXED: 1089: Style attributes are no longer lower-cased by default.
  * FIXED: 1934: Firefox cleans up temporary directories more effectively.
  * FIXED: 3647: WebElement.sendKeys now works in Firefox on XHTML pages.
  * FIXED: 3758: Maximize windows from inside a frame works as expected.
  * FIXED: 3825: Alerts from a nested iframe are now handled properly.

Grid:

  * Fixing Firefox profile extraction if a grid node started from a
    network location (UNC path)

Atoms:
  * bot.actions.type now works as expected in Firefox 12.
  * Introduced better mouse and keyboard abstractions

Tuesday, May 22, 2012

Example of log4.xml to separate your selenium framework's logs from ones generated by webdriver

It is usefull to track the log messages generated as by your framework as by the selenium itself. Here is the example on how to separate the log streams so it is convenient to collect and to review
.
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
<log4j:configuration xmlns:log4j='http://jakarta.apache.org/log4j/'>
 <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
  <param name="Threshold" value="INFO"/>
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="%-5p [%d] [%c{1}] %x - %m%n" />
  </layout>
 </appender>
 <!-- Famework generated log files -->
 <appender name="FILE_DEBUG" class="org.apache.log4j.RollingFileAppender">
  <param name="Threshold" value="DEBUG"/>
  <param name="File" value="your.framework.debug.log" />
  <param name="append" value="true" />
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="%-5p [%d] [%c{1}] %x - %m%n" />
  </layout>
 </appender>
 <appender name="FILE_INFO" class="org.apache.log4j.RollingFileAppender">
  <param name="Threshold" value="INFO"/>
  <param name="File" value="your.framework.info.log" />
  <param name="append" value="true" />
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="%-5p [%d] [%c{1}] %x - %m%n" />
  </layout>
 </appender>
 <!-- Selenium generated log files -->
 <appender name="FILE_DEBUG_SEL" class="org.apache.log4j.RollingFileAppender">
  <param name="Threshold" value="DEBUG"/>
  <param name="File" value="selenium.debug.log" />
  <param name="append" value="true" />
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="%-5p [%d] [%c{1}] %x - %m%n" />
  </layout>
 </appender>
 <appender name="FILE_INFO_SEL" class="org.apache.log4j.RollingFileAppender">
  <param name="Threshold" value="INFO"/>
  <param name="File" value="selenium.info.log" />
  <param name="append" value="true" />
  <layout class="org.apache.log4j.PatternLayout">
   <param name="ConversionPattern" value="%-5p [%d] [%c{1}] %x - %m%n" />
  </layout>
 </appender>
 <!-- Mapping of the classes -->
 <logger name="com.your.framework.automation">
  <level value="DEBUG" />
  <appender-ref ref="FILE_INFO" />
  <appender-ref ref="FILE_DEBUG" />
  <appender-ref ref="CONSOLE" />
 </logger>
 <logger name="org.apache">
  <level value="DEBUG" />
  <appender-ref ref="FILE_INFO_SEL" />
  <appender-ref ref="FILE_DEBUG_SEL" />
 </logger>
</log4j:configuration>

.
Here we starts from describing separate appenders just to target them to separate files on the hard drive. We'd like to have here four files described. To keep INFO and DEBUG level messages of framework and to keep the same information got from WebDriver.
The area under the last comment means that we push all the DEBUG messages generated under the package (aka namespace com.your.framework.automation.*) to the appenders targeted to framework log files (they are filtered then according to the desired log level) and all the messages from org.apache.* to selenium's related logs in the same way.

Note that to make it work properly you should follow the pattern like

Logger logger = Logger.getLogger(YourClassThatGeneratesLogMessage.class);

as the fully qualified name of the specified class is used to finally determine the target file of the message generated by the logger object.

WebDriver: The way to obtain element hidden under another (but static) one. Or how to fight with elements covering targets of your interaction

UPD: If you'd just like to scroll to the element that's somewhere at the bottom of the page see the solution here.

Assume you have the page with static bar (as in my case). This bar resides at the very bottom ov the page and is actualy stuck there whatever scroll you perform, so the user has the access to some feature permanently. Since such the UX has been introduced in the site I'm currently testing, some of my tests started to fail with the exception like this:
Element is not clickable at point (316, 494). Other element would receive the click
This was caused by the fact that the element does actually exist and is found by corresponding methods of webdriver, however it resides under that bar (that is static and alway at the bottom of the visible screen) element which actually received the click.

That's why we have two ways to resolve here. One - to remove the element which receives the click by javascript. However that is quite non-representative approach so we're NOT going to review it here. Another way is to scroll the page unil our required element becomes visible.

First you need the facility to execute javascript in your browser. The common practice is to have the method like this:
 public void runJS(String jsToRun){
  logger.debug("Running custom java script: " + jsToRun);
  ((JavascriptExecutor)getDriver()).executeScript(jsToRun);
 }

where getDriver() just returns your current WebDriver instance.
So, how to scroll the page in the browser. Again it can be done with certain javascript function execution. Such the script is very simple:
window.scrollBy(0,50);
This script means that we're scrolling the page to 50 px down (vertically) and not scrolling any px horizontally. Obviousely if we scroll 50 px down that does not mean that's enough. Probably we still have to proceed. So, how to design the iterative procedure meeting our expectation and going to be working fine. My suggestion is to introduce additional method. Let it be named scrollUntilVisibleAndClick. Check the source of the method first and then I'll explain how it works.

 public void scrollUntilVisibleAndClick(WebElement element, int maxScrollCout) throws YourOwnAutomationException{
  try{
   element.click();
   logger.debug("Element [" + element + "] is visble and successfully clicked.");
  }catch(WebDriverException e){
   logger.debug("Looks like the element is not clickable. Attempt to scroll down. MaxScrollCount = " + maxScrollCout);
   logger.debug(e.getMessage());
   if(maxScrollCout > 0){
    runJS("window.scrollBy(0,50);");
    scrollUntilVisibleAndClick(element, --maxScrollCout);
   }else{
    throw new YourOwnAutomationException("Couldn't scroll to the requested element");
   }
  }
 }

Let's now see how it works. To make this approach work you have to locate the element first. Once it is done you passes the element to the described method along with the parameter maxScrollcount which restricts the number of tries. The method attepts to click the element (doesn't matter if it is problematic from our standpoint). If it does, WebDriverException exception gets thrown. However we do not pass it on, but catch it and try to scroll down the page. Then we recursively call the same method with decreased "time-to-leave" parameter.
As the result of the method execution we can get the two finals. Either the maxScrollCount will reach zero and script will fail, or some new attempt after certain scroll will be successfull and the element will receive the click. 

Tuesday, May 15, 2012

Selenium Java client: proper way of how to know that the certain element got disappeared

Im one my previous posts I suggested the way on how to ensure the certain element disappeated in your test scenario code. That usually the case of Web UI testing as it is not only important that something is shown on your click but also that something stopped being shown. Now let me introduce the right way on how to do that :)

So assume you have the following function devoted to looking up the fact the element on the page disappeared. You call it somewhere from your scenario code.

public boolean lookupXPathDoesNotExist(final String xpath) throws CustomAutomationException {
 
try{
 new WebDriverWait(getDriver(), AWAITING_THRESHOLD_MS/1000, 1000).until(
  new ExpectedCondition<Boolean>(){
   @Override
   public Boolean apply(WebDriver d) {
    if (d.findElements(By.xpath(xpath)).isEmpty()){
     return new Boolean(true);
    }else{
     return new Boolean(false);
    }
   }
  }
 );;
}catch(org.openqa.selenium.TimeoutException e){
 throw new CustomAutomationException("Timeout exceeded - however the element is still visible");
}
return true;
}

To make it work you should consider the following:
AWAITING_THRESHOLD_MS - is the constant holding the timeout you'preffer to fail your scenario after in case the element is still on the page
getDriver() - is the function that somehow returns the current acting webdriver
1000 (the last parameter of WebDriverWait method) - the interval between repeated tries

Read also How To Check That Element Is Present In Another Element

Sunday, March 04, 2012

When WebElement.click() doesn't work in Selenium2 WebDriver

Sometimes I face the problems when regular WebElement click() method does not work. For example I met such the problem when tried to locate the link under H2 like the following snippet:

<h2>
 <a href="something" title="something">Some text</a>
</h2>

The regular method of locating like driver.findElement(By.xpath("somexpath")).click() led to the exception saying some other element will get the click instead of one located by me. So I applied the following work-around:

 public void safeClick(WebElement element){
  Actions builder = new Actions(getDriver());
  builder.moveToElement(element).click().build().perform();
 }
where getDriver() just means your current WebDriver implementation. This made me capable to work-around the problem from one hand and even made click action closer to the actual action sequence performed by the user clicking anything on the page. 

Wednesday, February 15, 2012

Another view on how to simulate multi-user interaction in your Selenium 2 scenarios

I heard from a lot of people that the best pattern for keeping your web driver in the project is singleton. That looks doubtful. 
Since I got the requirement of multi-user interacting in my scenarios I used singleton pattern for webdriver and I had to use the following workflow for interaction
1. Log in with user A
2. Send message to user B
3. Log out with user A
4. Log in with user B
5. Handle the request from user A

That is not quite obvious and organic workflow. The much more convenient is to do something like this

1. Log in with user A
2. Send message to user B
3. Log in with user B
4. Handle the request from user A--- Finish the test or:
5. Log out with user A
6. Log out with user B

Add support for simultaneous work of several browsers

This cannot be implemented having singleton pattern as the one for your webdriver. This is my way of how to support several browsers simultaneously

First of all we need the factory to easily create new browser instances (aka new drivers)

 private static class WDFactory{
  public static WebDriver createWebDriver(Properties properties){
   String browser = properties.getProperty("browser");
   WebDriver driverToCreate;
   if(browser.toLowerCase().equals("*firefox")){
    driverToCreate = new FirefoxDriver();
   }else if(browser.toLowerCase().equals("*googlechrome")){
    driverToCreate = new ChromeDriver();
   }else{
    throw new UnsupportedOperationException("Not supported browser yet");
   }
   driverToCreate.get(properties.getProperty("domain"));
   driverToCreate.manage().timeouts().implicitlyWait(500, TimeUnit.MILLISECONDS);
   return driverToCreate;
  }
 }

Also we sure need the pool where we're going to store all the browser instances we're going to work with

public class WDPool {

 private HashMap<String, WebDriver> pool;
 
 public WDPool(){
  pool = new HashMap<String, WebDriver>();
 }
 
 public void closeDrivers(){
  for (String key: pool.keySet()){
   getDriver(key).close();
  }
 }
 
 public void addDriver(String id, WebDriver driver){
  ARTestCase.logger.debug("Adding driver with id [" + id + "]");
  driver.manage().deleteAllCookies();
  pool.put(id, driver);
 }
 
 public WebDriver getDriver(String id){
  ARTestCase.logger.debug("Returning driver with id [" + id + "]");
  return pool.get(id);
 }
}

Okay. Now you should consider your design. I have the class laying between the browser control engine (like selenium) and the business logic of the scenarios. So if you have one introduce the following method there. Otherwise introduce it into the class where your actual scenario is described

 public void pushDriver(String id){
  driver = WDFactory.createWebDriver(properties);
  stack.addDriver(id, driver);
 }

Here and after stack represents the instance of WDPool.

It will be used on the stage the new user is logging in.
and the facility to obtain the driver instance for the current user

 private WebDriver getDriver(){
  return stack.getDriver(ARTestCase.getCurrentUser());
 }

So now we're ready to instantiate the new browser each time the new user gets logged in. The browser instance is stored into the hashmap with the id holding the user's nick-name. There are only two things to do:

1. Change your log-in functionality of the scenario so that it requests pushDriver. In my case it has the following look:

    public void logIn(String username) throws ARAutomationException {
     getTestCase().getAdapter().pushDriver(username);
     getTestCase().setCurrentUser(username);
 // some actions to perform
    }

Where getTestCase().getAdapter() returns the interlayer of the test case we're currently executing holding all the stuff I'm writing here about.

2. Wherever  you used the pattern like driver.someMethod() you now should use getDriver().someMethod() described in two snippets above. That will make your framework to switch the browsers each time you either log in with new user or call

getTestCase().setCurrentUser(existingUserNameHoldingOneOfTheBrowserInstances);

For those who experience problems with Selenium 2 hovering

The common practice of simulating mouse hovering in Selenium 2 is to move the mouse cursor to the element located and wrapped with WebElement. This should look like this.
            Actions builder = new Actions(driver);
            builder.moveToElement(lookupXPathExists(xPath)).build().perform();
Where driver is your current WebDriver implementation and  lookupXPathExists(xPath) is a kind of procedure returning you the WebElement object by xPath (see my older posts for more details)

However a lot of people trying to apply such the approach for both FF and IE browsers experience the same issue: once your scenario tries to perform some next action after hovering (ex. click the button appeared after you have hovered the element) the hovering gets canceled.

So the only way for us is to wait until this issue is resolved. Use Chrome driver so far.. :)

Thursday, February 09, 2012

Selenium antipatterns or What is Selenium the worst for (or 'donts' for Selenium)

You probably know a lot of cases when selenium will help you a lot and will beat any competitor which means it is really the best solution on the market. However I'd like to share the set of the problems the selenium is better to put away from (selenium anti-patterns).

  • Prepare the base state. If you include the base state preparation procedures in your selenium-based framework, be ready you may get them failed which will cause the test fails as well. That will not mean your product functionality does not work in proper way. That will probably mean your base state generation procedure failed due to some instability which is certainly still the sort of seleniums feature combined with the specifics of http servers or network collisions :)
  • Run load testing. You definitely can simulate some load using the selenium just because you can simulate user's work flow with the help of it. Selenium drives the GUI browsers which consumes plenty of resources so even if you use HtmlUnit driver that won't help you much. More over the load generating framework supposes strong discipline of multi-threading beacuse if you have no such one, you may measure your own leaks aka the problems in your framework performance, not of your tested application's. Do not invent the wheel - use JMeter.
  • Accuracy testing. Once your web application delivers some analytics you probably will want to automatically check if the values in your tables are correct. That is bad practice for sure. Depending on your browser's locale the numbers may have different representation, so that is might cause the problems in string casting to number formats. The same is true for date and currency date types. Moreover you likely will have to maintain such the tests quite frequently due to HTML dynamic structure changes because in the most cases such the tables are drawn with the help of dedicated GUI framework which may vary the HTML representation slightly keeping GUI look unchanged for the user. And do not forget that being introducing accuracy assertions you will have to make sure your last assertion procedure matches the business logic of  the tested application
  • Third-party graphics testing like one provided by adobe flash, MS silverlight etc. Use dedicated products to test such the interactions
So those are the tings I'd not recommend to use selenium for. All other web testing stuff matches selenium capabilities pretty good.

Wednesday, November 30, 2011

Another robust way of how to locate ajax elements using Selenium 2 + webdriver

Check new series of the articles. Review and user experience on test management systems. Functionality and usability.
-------------------

Once you're going to test somehow complicated web-site you should investigate the way of what to consider as the succeess of your another action?

As it looks to me we have two kinds of success indicators of our action on UI
1. You boserve the element that is expected to be displayed (ex.: you're clicking the button and expect to see the dialog)
2. You do not observe the element that is not expected to be displayed. (ex.: you're closing the dialog and expect it disappears)

Also you should take into account that the elements on your page may appear and disappear without page re-loading. That's why I'd like to suggest the following design addressing such the points:

UPD: Here is the sophisticated and of-good-practice way of how to to that.

Introduce two methods:

public WebElement lookupXPathExists(String xpath) throws MyAutomationException{
  WebElement handledElement = repeatableLookupExists(xpath);
  if (handledElement!=null) {
   return handledElement;
  } else {
   throw new MyAutomationException("Lookup for element ["
     + xpath + "]" + " failed after "
     + AWAITING_THRESHOLD_MS + " ms awaiting.");
   
  }
 }
 
 public boolean lookupXPathDoesNotExist(String xpath) throws MyAutomationException {
  if (repeatableLookupDoesNotExist(xpath)) {
   return true;
  }else{
   throw new MyAutomationException("The xpath ["+xpath+"] is still observed after "+AWAITING_THRESHOLD_MS + " ms awaiting.");
  }
 }

Where AWAITING_THRESHOLD_MS is static final variable holding the period you'd like to wait until the element will appear. The first method returns the element found using your locator. The second one just checks if the element you're searching does not present on the page. Both of them call their repeatable helpers which are shown below:

private void validateFoundElements(List<webelement> elementList) throws SeleniumException{
  
  MyTestCase.logger.debug(testCase.wrapMessage("Validating found elements..."));
  
  if(elementList.isEmpty()){
   throw new SeleniumException("Cant find specified element..");
  }
  
  if(countVisibleItems(elementList) > 1){
   throw new SeleniumException("Specified xpath found several elements. Please concretize.");
  }
 }
 
 private int countVisibleItems(List<webelement> elementList){
  int visibleItemsNumber = 0;
  for(WebElement element: elementList){
   if(element.isDisplayed()){
    visibleItemsNumber++;
   }
  }
  return visibleItemsNumber;
 }
 
 private WebElement repeatableLookupExists(String xpath) {
  long start = System.currentTimeMillis();
  while (true) {
   try{
    List<webelement> listOfFoundElements = driver.findElements(By.xpath(xpath));
    validateFoundElements(listOfFoundElements);
    if(driver.findElement(By.xpath(xpath)).isDisplayed()){
     MyTestCase.logger.debug(testCase.wrapMessage("Found xPath [" + xpath + "]. Creating object.."));
     return driver.findElement(By.xpath(xpath));
    }
   }catch(SeleniumException se){
    try {
     Thread.sleep(AWAITING_UNIT_LENGTH_MS);
     MyTestCase.logger.debug(testCase.wrapMessage("Searching xPath [" + xpath + "]. Retry.."));
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
   if (System.currentTimeMillis()-start > AWAITING_THRESHOLD_MS){
    break;
   }
  }
  Statistics.totalLatency += System.currentTimeMillis() - start;
  return null;
 }

 private boolean repeatableLookupDoesNotExist(String xpath) {
  long start = System.currentTimeMillis();
  while (true) {
   boolean isPresent = driver.findElements(By.xpath(xpath)).isEmpty() ? false : true;
   if (!isPresent) {
    Statistics.totalLatency += System.currentTimeMillis() - start;
    MyTestCase.logger.debug(testCase.wrapMessage("Not found xPath [" + xpath + "]. Success.."));
    return true;
   }
   else{
    if(countVisibleItems(driver.findElements(By.xpath(xpath)))==0){
     MyTestCase.logger.debug(testCase.wrapMessage("Found xPath [" + xpath + "]. However it is not visible. Success.."));
     return true;
    }
    try {
     Thread.sleep(AWAITING_UNIT_LENGTH_MS);
     MyTestCase.logger.debug(testCase.wrapMessage("XPath [" + xpath + "] still can be found. Retry.."));
     if (System.currentTimeMillis()-start > AWAITING_THRESHOLD_MS){
      break;
     }
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
   }
  }
  Statistics.totalLatency += System.currentTimeMillis() - start;
  return false;
 }

Where AWAITING_UNIT_LENGTH_MS is final variable saying how often you'd like to check if the element appeared/disappeared.
That is how the solution work. The only thing you should always remember about is that once you want to check if the element is not present on the page and you realize that it isn't you should make sure it is not visible on the page because of the proper working of the tested product but not because of the page hasn't been completely loaded. To make that sure try to determine the checking element that should be obligatory visible while that one you do not expect to see isn't. So use the following pattern to ensure the element is not visible as expected:
1. lookupXPathExists("xpath of the element saying the page is loaded")
2. lookupXPathDoesNotExist("xpath you're expecting not to see on the page").

That is it.

Saturday, August 13, 2011

Selenium 2 tried released version

Okay. AFAIR I said that selenium 2 was not ready for enterprise testing. That time I used either beta or even alpha version of the webdriver. So today I decided to check out what's been changed since that time and probably to unsay :)
However I still cant do that :) What does prevent me now? First of all there is still lack of multiple-browser support. Internet explorer driver couldn't see the input elements for some reason however other drivers did so I couldn't even log in. Opera driver cant close the browser window on the test shut-down... So the latest build version is pretty much raw yet. Still don't recommend to move your frameworks to WebDriver.

Monday, May 30, 2011

That awful WebDriver or Selenium 2 disadvantages

Actually I disappointed. Selenium 2 seems to be not ready for enterprise testing. I had to move my test engine back to Selenium RC. Certainly its a pity I have to renounce the object-oriented model, but I still keep the design (actually this has nothing to do with real OO design) with the help of set of adapters to make me get back to Selenium 2 since it will become somehow  finalized.
The features of Selenium 2 that I liked a lot and I feel the most sorry for:
- selecting only visible elements by xPath
- capability of retrieving the list of the objects representing html elements
- controlling of key sending. The capability of sending the symbols one-by-one