Package com.smartgwt.client.docs

# Interface AutomatedTesting

public interface AutomatedTesting

### Automated Testing

Smart GWT supports automated testing with a variety of tools. See the `AutoTest` class for information about how to generate and resolve `AutoTestLocators` and other utilities within the Smart GWT framework related to generating automated tests.

### Playwright

Smart GWT applications work smoothly with Playwright.

The SDK package provides an example `commands.js` file that defines Playwright fixtures and helpers designed to locate and interact with Smart GWT components, automatically handle asynchronous operations, record performance data, and more. For step-by-step guidance on using Playwright with Smart GWT, see `Integrating Smart GWT with Playwright`.

### Cypress

Smart GWT applications integrate seamlessly with Cypress.

The SDK package includes a sample `commands.js` configuration file with custom cypress commands to identify and interact with Smart GWT components, seamlessly wait for asynchronous operations, recording timing data and more. See the `Cypress integration overview` for details on how to use Cypress with Smart GWT.

### Selenium / Selenese

Smart GWT includes free support for Selenium for robust recording and playback of tests, including the ability to record on one browser and play back on others, via Selenese enhanced with Smart GWT-specific locators and commands that provide a stable means of locating Smart GWT widgets and ensuring they're ready for interaction.

To write Selenese, we recommend Selenium IDE 2.9, which is compatible with Firefox 52 ESR, and can directly load our user extensions, located in the `selenium/` directory. A user guide explaining how to create and interactively run selenese with the IDE can be found `here`. Selenium IDE 3, which requires Firefox Quantum, has just released support for plugins that should allow the eventual migration of our user extensions, but for now only Selenium IDE 2.9 can load Smart GWT locator and command extensions.

SeleneseRunner

For automated testing, Smart GWT provides SeleneseRunner, a tool that executes Smart GWT-enhanced Selenese created by Selenium IDE via emulation, since Selenium 3 no longer supports the Selenium RC APIs and thus can't execute Selenese that requires custom user extensions. Internally, `SeleneseRunner` makes use of the APIs in our WebDriver wrappers to resolve locators properly and execute Smart GWT-enhanced Selenese.

`SeleneseRunner` can be used to:

- execute Selenese directly from the command line
- execute Selenese from inside a Java program (eg, as part of a JUnit test)
- convert a Selenese test to Java code (as a JUnit test)

See the server-side JavaDoc linked above for more information on how to use these features.

### TestRunner

`TestRunner` is a system for automatically running a suite of Selenium tests, commiting the results to a database, and reporting any regressions (or fixes) via email.

### GwtTestCase

GWT includes a way to run a GWT application under JUnit, running your GWT application in a "headless" browser. This is a very limited testing approach appropriate for certain unit tests only - it cannot replace events such as clicks, because by default it doesn't run in actual browser (instead it runs in a simulator called HtmlUnit).

Note that running tests under HtmlUnit can lead to false failures in a variety of areas, including network communication and XML processing, where HtmlUnit's behaviors do not correspond to any real browser. Note that, if you find that a test fails under HtmlUnit but would not fail in a real browser, this will not be regarded as a SmartGWT bug.

If you use GwtTestCase at all, Isomorphic recommends that the majority of your tests are executed using the runStyle option that allows GwtTestCase to run under a real browser via Selenium.

Also note, GwtTestCase has a bug where it does not run onModuleLoad() for included GWT modules. To make sure SmartGWT's onModuleLoad() runs, add a `gwtSetUp()` implementation like so:

        public class SgwtTest extends GWTTestCase {
            public void gwtSetUp() {
                new SmartGwtEntryPoint().onModuleLoad();
            }
            ...

You may need to add similar manual calls for other GWT modules you inherit which expect to have their `onModuleLoad()` method called normally.

### Selenium WebDriver

WebDriver, supported since Selenium 2, uses a different basic architecture in which a driver is added to each browser to enable Selenium interaction, instead of doing so from JavaScript.

Support for WebDriver-based testing for Smart GWT is now available with the same custom locator strategies and custom commands as we provide for Selenese. However, we continue to recommend Selenese rather than WebDriver-based Selenium, because Webdriver requires Java programming skills. Tests created in Selenium IDE and stored in Selenese can be executed by a variety of tools without requiring Java skills, including our own `TestRunner`. Most ways of running WebDriver tests involve Java coding skills or at least the ability to work with a Java IDE. This tends to mean that all QA personnel must either have Java skills or drain the time of Java developers on repetitive tasks.

Ultimately, our current recommendation is to use Selenium IDE and Selenese exclusively or at least primarily. If there are critically important tests that you can only build via WebDriver, use WebDriver for those tests only, or use manual testing for those tests.

WebDriver Usage

When using WebDriver, we recommend using Selenum IDE as a starting point to record and store tests. You can then call `SeleneseRunner` to convert that Selenese to Java code that uses Smart GWT locators and invokes the appropriate APIs on our WebDriver wrappers.

Once you become familiar with what code is generated for common interactions, you may want to write tests directly without using Selenium IDE. In this case, you can retrieve locators for specific elements in a couple of ways. The `AutoTest.installLocatorShortcut()` method allows developers to retieve a locator for the element under the mouse via a simple key-combo plus click. Alternatively you can use `AutoTest` APIs, such as `AutoTest.getLocator()`, which takes a `Canvas` or DOM element, to get the locators you need. These can be invoked by evaluating script while a Smart GWT page is loaded (from the `Developer Console` or from the native browser console).

NOTE: Selenium IDE has an option to export tests as WebDriver-compatible code. Do not use this feature, it exports useless code that doesn't understand custom commands, custom locators, or other key features of Selenium IDE. Use `SeleneseRunner` instead.

WebDriver Classes overview

Storing and executing Selenese tests recorded in the Selenium IDE is recommended as the primary approach for using WebDriver. However, for certain rare tests it can make sense to use WebDriver Java support directly.

Smart GWT support for WebDriver is based around 3 different Java classes:

1. ByScLocator: This implements the ability to find WebElements or WebDriver "By" objects using Smart GWT Locator strings. See `UsingSelenium` for more background on Locator strings and how to obtain them. Given a locator String, example usage is:

         ByScLocator.scLocator("//ListGrid[ID=\"countryList\"]/body/row[countryCode=US||0]/col[fieldName=countryCode||0]")

2. SmartClientWebDriver: This is an abstract class which provides a number of different methods for interacting with the browser, such as:
   - open a browser at a particular URL
   - find the element or elements which match a given "By" object (either ByScLocator, or a standard WebDriver locator)
   - perform events and operations (click, drag, select etc)
   - perform custom Smart GWT validations / state checks, such as whether a grid has loaded data
   Three concrete implementations of SmartClientWebDriver are provided: SmartClientFireFoxDriver, SmartClientChromeDriver and SmartClientIEDriver. There is also a SmartClientRemoteWebdriver class which allows the injection of a manually configured RemoteWebDriver instance. This might be necessary, for example, for use with Selenium Grid.

3. ScActions: a Smart GWT-specific version of the standard WebDriver "Action" class, providing a builder pattern to create a sequence of operations which can then be perform()ed.

These classes are packaged in the library isomorphic_webdriver.jar, which can be found in the directory lib-WebDriverSupport (along with several 3rd-party supporting libraries).This directory can be found at the top level of the downloaded Smart GWT zip package.

General information regarding WebDriver can be found here. Setup for WebDriver is more complex than for classic Selenium. Drivers can be downloaded for Firefox, Google Chrome, Internet Explorer, and MS Edge.

JUnit + WebDriver

Explore `JUnit + Selenium WebDriver`, where we walk through a JUnit test targeting a Smart GWT Showcase sample.

File Upload Example Test

As discussed above, one advantage which WebDriver does have over Classic Selenium is the ability to test file upload. It is still limited in that if a click is triggered on the file selection button an OS native file selection dialog will be triggered in which case the test will be suspended until the file is manually selected. To avoid this, the sendKeys() method can be used to enter the file location.

Sample code:

         /**
          * The following test runs against localhost and requires a small (< 5mb) image to be in /tmp/image.jpg
          */
         public void fileUploadGWT() throws Exception {
             final String basePath = "//VLayout[ID=\"isc_Showcase_1_0\"]/member[Class=HLayout||index=0||length=2|"
                                    +"|classIndex=0||classLength=1]/member[Class=HLayout||index=0||length=2||classIndex=0|"
                                    +"|classLength=1]/member[Class=Canvas||index=1||length=2||classIndex=0||classLength=1]"
                                    +"/child[Class=TabSet||index=0||length=1||classIndex=0||classLength=1]/paneContainer/"
                                    +"member[Class=VLayout||index=1||length=2||classIndex=0||classLength=1]/"
                                    +"member[Class=VLayout||index=1||length=2||classIndex=0||classLength=1]/"
                                    +"member[Class=HLayout||index=1||length=2||classIndex=0||classLength=1]/"
                                    +"member[Class=HLayout||index=0||length=1||classIndex=0||classLength=1]/";
             final String formPath = basePath + "member[Class=DynamicForm||index=0||length=3||classIndex=0||classLength=1]";
             final String tilesPath = basePath + "member[Class=VLayout||index=2||length=3||classIndex=0||classLength=1]/"
                                               + "member[Class=TileGrid||index=2||length=4||classIndex=0||classLength=1]/tile";
             SmartClientWebDriver driver = new SmartClientFirefoxDriver();
             driver.setBaseUrl("http://localhost:8888/");
             driver.get("index.html#upload_sql", true);

             final int origSize = driver.findElements(ByScLocator.scLocator(tilesPath)).size();
             By uploadForm = ByScLocator.scLocator(formPath);
             WebElement form = driver.findElement(uploadForm);

             By titleInput = ByScLocator.scLocator(formPath + "/item[name=title||title=Title||index=0||Class=TextItem]/element");
             driver.click(titleInput);
             driver.sendKeys(titleInput, "test image: " + origSize);

             WebElement findElement = form.findElement(By.xpath("//input[@type='FILE']"));
             /*
              * The following causes a native dialog to be created which prevents further progress. Do NOT uncomment!
              * We just have to sendKeys() to it
              */
             //findElement.click();

             findElement.sendKeys("/tmp/image.jpg"); // A local file. Please change accordingly

             By saveButton = ByScLocator.scLocator(formPath + "/item[title=Save||index=2||Class=ButtonItem]/button/");
             driver.waitForElementClickable(saveButton);
             driver.click(saveButton);
             /*
              * Note the following fails once the grid contains more than 3 rows of data as the index becomes inconsistent
              * as tiles scrolled out of site are removed and the indices change
              */
             By tile = ByScLocator.scLocator(tilesPath + "[Class=SimpleTile||index="+(origSize)+"||length="+(origSize+1)
                                                           + "||classIndex="+(origSize)+"||classLength="+(origSize+1)+"]/");
             driver.waitForElementClickable(tile);
             WebElement tile1 = driver.findElement(tile);
             assertEquals("test image: " + origSize, tile1.getText());
             assertEquals(origSize + 1, driver.findElements(ByScLocator.scLocator(tilesPath)).size());
             driver.close();
             driver.quit();
         }

WebDriver Troubleshooting

There is a known issue that native events do not work with IE in Windows 8/8.1 that may manifest in WebDriver as clicks having no effect. One potential workaround is to disable native events:

         DesiredCapabilities caps = DesiredCapabilities.internetExplorer();
         caps.setCapability("nativeEvents",false);
         SmartClientWebDriver driver = new SmartClientIEDriver(caps);

It's also been reported that changing the second line above to:

         caps.setCapability("requireWindowFocus", true);

also resolves the issue, with the side effect that WebDriver then moves the mouse cursor.

In some versions of Internet Explorer, it's been reported that you must add the URL targeted by WebDriver to the "Trusted Sites" under Internet Options >> Security in order to allow the browser to communicate properly with Selenium. A discussion of the setup needed to use WebDriver's InternetExplorerDriver can be found here.

Other tools

Smart GWT supports a special JavaScript API to allow other test tools to integrate in the same manner as Selenium and WebDriver. This API allows the test tool to record an abstract "locator" string representing the logical name for an interactive DOM element, and then during test playback, retrieve a DOM element given a locator.

This is critical because, like many modern Ajax systems, Smart GWT generates different DOM elements in different browsers, in different skins, and in different versions of Smart GWT. Testing tools that try to directly record the generated Smart GWT DOM produce extremely brittle tests because they are effectively recording undocumented internals.

Using the "locator" API allows you to record or write tests that will run in any browser supported by Smart GWT, in any version of Smart GWT, and in any skin. It also makes tests more readable and easier to understand and maintain.

Different testing tools vary in how easily they can be configured to use the locator API, and in some older tools it can be a large effort. We highly recommend using our Selenium extensions - it often makes sense to use them even if you have to use them in parallel with another, older testing tool. If you are forced to use another tool exclusively:

- Refer to the SmartClient documentation for the AutoTest class (because it's a JavaScript API). It can be found here
- Read over the source code of our Selenium extensions to get a clear understanding of how the Selenium integration works, because this will be analogous to the work you'll need to do
- Search the forums for other developers who are trying to use the same test tool with Smart GWT, and share efforts

See Also:
  - `EventStream.getAsSeleneseHTML()`
  - `EventStream.getAsSeleneseCommands()`