Announcement

Collapse
No announcement yet.
X
  • Filter
  • Time
Clear All
new posts

    Selenium scLocator for MenuItem can not be reached using ID

    We are in the process of "instrumenting" our SmartClient screen elements/components with ID attributes to be able to target them using Selenium and automate our UI testing.

    We were successfully able to locate menu using their ID with the help of scLocator inside Selenium IDE (MenuButton class).

    However, we are NOT able to locate menu items using their IDs (MenuItem class "under" a Menu object).

    Our app dynamically builds the menu content based on the user's authorities (it changes from user to user). Therefore, it is not possible to use a static position in the menu for our tests.

    I attached a few screenshots to illustrate what we do.

    #1
    In Chrome debugger, have a look in the console: you can see the menu has its ID (meiMenu_promotion). And, inside the MenuButton object, the menu's items all have their own unique IDs (meiMenu_promotionNew, meiMenu_promotionOpen). This has been structured in such a way so that we could navigate the menus.

    Click image for larger version

Name:	buildMenu.png
Views:	149
Size:	85.7 KB
ID:	232923


    Selecting the menu works fine in Selenium IDE:
    scLocator=//MenuButton[ID="meiMenu_promotion"]/

    As a 1st step, this is as expected.


    #2
    What the menu looks like in the app once it's generated with the above code fragment :
    Click image for larger version

Name:	promotionMenu.png
Views:	126
Size:	3.9 KB
ID:	232924

    #3
    As stated above, Selenium IDE is able to locate the top-menu items with its ID. But, the next click on the menu item does not "register" with the MenuItem's ID.

    scLocator instead drills into the menu using a non-reliable structure for our use:

    Click image for larger version

Name:	selenium.png
Views:	138
Size:	58.8 KB
ID:	232925
    The scLocator expression for the menu item is not reliable at all for us:
    1. Our app must be testable in multiple languages; using the "title" attribute to locate the content is not acceptable.
    2. As stated above, the content of the menu depends on the user's authorities. What is at position 1 for userA will not necessarily be at position 1 for userB.

    We were hoping to be able to use something as simple as:

    Code:
    scLocator=//MenuItem[ID="meiMenu_promotionOpen"]/
    since we explicitly identified the menu items with IDs.

    What are our options to select sub-menu items using ID in Selenium with the help of scLocator ?

    PS: I thought about using Selenium's right-click feature to locate the sub-menu, but faced the same problem as reported here (right-click selecting the menu item instead of showing the browser's contextual menu) : http://forums.smartclient.com/forum/...den-menu-items.

    Thanks !

    SmartClient_v91p_2015-10-28_Pro
    Selenium IDE 2.9.0
    Firefox 42.0 on Windows 7 Pro

    #2
    MenuItems don't have an ID property (see docs), so clearly that wouldn't be used in a locator.

    We see what you're trying to do, but it's unclear why. Clearly you're trying to use the same test script across different locales. However, you're specifically trying to design the test script so that it does not test whether the UI has been localized correctly.

    So what kind of issue is a script like this intended to catch? Something like a component being the wrong size, because someone provided a localized message that was too long?

    Comment


      #3
      I assumed that all SmartClient components supported the 'ID' attribute; thanks for pointing this out.

      Our intent is NOT to test localization of the menu content.

      We only want to use the menu in a reliable way in order to test something else down the road (and we want to use Selenium to simulate user menu clicks in such a way that it works with different kind of functional access/profiles; we can not assume the menu items index or name)

      We have a single-page app: navigating through the menu is NOT part of the tested items, it's something we have to do in order to get to the screens we want to test:

      Is there a different way to achieve this ?

      We do not want our tests to use shortcuts that the tests only would know about and not reflect what the users would do in the UI (we thought about handling URL parameters that the tests would be aware of to create such shortcuts but decided not to go this way).

      Thank you !

      Comment


        #4
        You didn't answer the question, which again was: what kind of errors are you expecting to catch by changing the locale, but not testing any of the effects of changing the locale?

        Regardless, you can use the information toward the end of this document (the section "Common Locator Syntax") to modify the locators for MenuItems so that they look up MenuItems by whatever attribute you choose. Note that Menu is a subclass of ListGrid, so finding MenuItems uses the same rules as finding ListGridRecords in a ListGrid.

        http://www.smartclient.com/smartgwte...gSelenium.html

        Comment


          #5
          Originally posted by Isomorphic View Post
          You didn't answer the question, which again was: what kind of errors are you expecting to catch by changing the locale, but not testing any of the effects of changing the locale?
          Our tests are not meant to care about the text content of screen items (we're not testing the translators nor the integration of their work in the app). For now, all we want is to check whether a button with a special business meaning is drawn (1st step) and then, check whether it's enabled vs disabled (2nd step).

          We're still in the POC phase and were planning to test translation in a future phase.

          The problem we have is we are not yet able to find a way to navigate the menu in [something we consider] a reliable way. We read the "Using Selenium" guide, but we were expecting that there could be a more "native" way of addressing/targeting sub-menu items and use them to navigate in the app.

          Is it possible to get access to the source code of the classes inside isomorphic_webdriver.jar ? This could help understand Selenium's way we don't still grasp. We could even contribute and provide utility shortcuts (if deemed useful) that would allow one to directly use :
          scLocator=//MenuItem[ID="meiMenu_promotionOpen"]/

          Thanks !

          -C

          Comment


            #6
            Please try to read more carefully.

            We already gave you a solution:

            Regardless, you can use the information toward the end of this document (the section "Common Locator Syntax") to modify the locators for MenuItems so that they look up MenuItems by whatever attribute you choose. Note that Menu is a subclass of ListGrid, so finding MenuItems uses the same rules as finding ListGridRecords in a ListGrid.

            http://www.smartclient.com/smartgwte...gSelenium.html
            And, you're still not answering our question. Again, you're saying you want a way to address menuItems that doesn't use the title, since the title could vary by locale. So the purpose of this would be to use the same test script in multiple locales. So again, our question is: what category of bugs are you trying to find by running your scripts in more than one locale?

            Comment


              #7
              We do not want to build our tests in a locale-agnostic way so we can find bugs in one locale vs another. We just want to build our tests so they are robust and they still work when menu item "Feature A" is renamed to "Great Feature K". Our experience with multi-locale support is using IDs is best than tying ourselves to one locale to run tests.

              We also read http://www.smartclient.com/smartgwte...gSelenium.html multiple times; especially the "Common scLocator syntax" section. Unfortunately, it doesn't provide much info about dynamic content. The examples it contains are quite basic (even though being able to get access to a specific cell is cool).

              We want to be able to open a menu and:
              1) get a count of how many menu items it contains
              2) identify the menu items using an ID/attribute/class without worrying about it's sequence in the presentation.
              3) drill down into sub-menus

              We tried:
              Code:
                      // final By by = ByScLocator.scLocator("//Menu[level=" + menuLevel + "]/body/");
                      // final By by = ByScLocator.scLocator("//Menu[level=" + menuLevel + "]/body");
                      // final By by = ByScLocator.scLocator("//Menu[level=0]/body/data/");
              
                      final By by = ByScLocator.scLocator("//Menu[level=0]/body/rows");
                      List<WebElement> findElements = driver.findElements(by);
              But, it never works. We were expecting to be able to call WebElement.getText() or WebElement.getAttribute("ID") on the return from "driver.findElements(by);". That did not work. We never get multiple WebElements back.

              What is the best way to achieve this ?

              Using
              Code:
              final By by = ByScLocator.scLocator("//Menu[level=0]/body/row[0]/col[0]");
              allows us to get the menu items at a specific position; fine. But, this is too weak, we don't want to re-order all the tests when we introduce a new feature at the top of the menu (hence the IDs we were looking for).


              Finally, as requested in a previous post on this thread:
              is it possible to get access to the source code from "com.isomorphic.webdriver.*" ? This would help us to trace into the code and understand what are the concepts behind the scenes and write more efficient tests (and bug you less often on the forum).

              Comment


                #8
                Regardless, you can use the information toward the end of this document (the section "Common Locator Syntax") to modify the locators for MenuItems so that they look up MenuItems by whatever attribute you choose. Note that Menu is a subclass of ListGrid, so finding MenuItems uses the same rules as finding ListGridRecords in a ListGrid.
                Under "Common Locator Syntax", we have:

                List Grid cells //ListGrid[ID="itemList"]/body/row[itemID=1996||itemName=Sugar White 1KG||SKU=85201400||1]/col[fieldName=SKU||1]
                In this example locator, the ListGrid record is being located by arbitrary attributes - in this case, itemID is the first tried, then itemName, then SKU, then finally the row index (1) is used as a fallback.

                So, if you add some custom attribute to your MenuItems (via setAttribute()), you can use a locator like the above to target it.

                To drill into a submenu, just send an event that would open it. There is a section in the "Using Selenium" doc dedicated to this specific problem titled "Recording Movement-Driven Interactions".

                The only way to obtain a count of the number of menu items is to execute a JavaScript expression via WebDriver, for example, [someMenuId].getData().getLength().

                About the webdriver source - no, it's not available, and also has nothing to do with how locators are captured or applied (which is all JavaScript in AutoTest.js). While this part of the source is readable, we would strongly recommend not diving into it - reading documentation is much more effective than reading source code. Even your novel requirement (not wanting to use title to identify MenuItems) is covered in the existing docs. Meanwhile, our repeated experience is that people who dive into source code end up more confused rather than less due the plethora of browser workarounds required, or end up relying on behaviors that are not documented hence may change, etc.

                Comment


                  #9
                  Originally posted by Isomorphic View Post
                  So, if you add some custom attribute to your MenuItems (via setAttribute()), you can use a locator like the above to target it.
                  Searching for setAttribute() in the reference documentation does not return something significant. We are using the JavaScript version; is setAttribute() in SmartGWT only ?

                  We ended up adding properties to MenuButton and MenuItem to be able to locate the menu items and it worked. Thank you for the more specific pointers.

                  Now, that we're able to validate the content of a menu; our next step is to make sure that only the expected menus are generated for a specific user.

                  Here is the code we have:
                  Code:
                      @Test
                      public void test_promotion_read()
                      {
                          final HomePage homePage = this.createHomePage("promotion_read");
                  
                          homePage.openMainMenuPromotion();
                          this.assertMenuContent(this.getDriver(), MAIN_MENU_PROMOTION, 0, MAIN_MENU_PROMOTION + "Open", MAIN_MENU_PROMOTION + "OpenRecent");
                          this.assertAllowedMainMenus(this.getDriver(), MAIN_MENU_HOME, MAIN_MENU_PROMOTION);
                      }
                  
                      // --------------------------------------------------------------------------------------------------------------------------------
                      /**
                       * Assumes the menu to inspect has already been opened.
                       */
                      // --------------------------------------------------------------------------------------------------------------------------------
                      private void assertMenuContent(final SmartClientWebDriver driver, final String menuId, final int menuLevel, final String... menuItems)
                      {
                          for (String oneMenuItem : menuItems)
                          {
                              this.assertMenuAvailable(driver, "//Menu[level=" + menuLevel + "]/body/row[meiMenuId=" + oneMenuItem + "]");
                          }
                  
                          driver.assertEval(menuId + ".menu.getData().getLength() == " + String.valueOf(menuItems.length), Boolean.TRUE);
                      }
                  
                      private void assertMenuAvailable(final SmartClientWebDriver driver, final String scLocatorValue)
                      {
                          final By menuBy = ByScLocator.scLocator(scLocatorValue);
                  
                          driver.waitForElementClickable(menuBy);
                  
                          final WebElement menuElement = driver.findElement(menuBy);
                          assertTrue(menuElement.isDisplayed());
                          assertTrue(menuElement.isEnabled());
                      }
                  
                      private void assertAllowedMainMenus(final SmartClientWebDriver driver, final String... mainMenuNames)
                      {
                          final By by = ByScLocator.scLocator("//MeiMenu[*]/");              // PROBLEM 1 (see below)
                          final List<WebElement> mainMenus = driver.findElements(by);
                  
                          // PROBLEM 2 (see below)
                          assertEquals(mainMenuNames.length + 1, mainMenus.size());
                      }
                  PROBLEM 1:
                  How to use WebDriver.findElements() to get access to all MeiMenu objects in the page ?
                  We tried different attempts, but we always end up with an empty list. What is the best option ?


                  PROBLEM 2:
                  Once we have "List<WebElement> mainMenus" filled, we were hoping to extract the "meiMenuId" attribute from each of the elements and then use org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder() to make sure the content is right. How can we achieve this ?

                  Thank you in advance !

                  Comment


                    #10
                    Searching for setAttribute() in the reference documentation does not return something significant. We are using the JavaScript version; is setAttribute() in SmartGWT only ?

                    We ended up adding properties to MenuButton and MenuItem to be able to locate the menu items and it worked. Thank you for the more specific pointers.
                    setAttribute() is a SmartGWT API and the equivalent in SmartClient is just adding properties, yes.

                    Note also that the previous documentation links we provided were also SmartGWT, and you should use the corresponding SmartClient docs, as there are slight differences (such as the above). The corresponding documents can be trivially found by searching on the title of the SmartGWT document, eg, a search for "Using Selenium" goes right to the corresponding SmartClient overview.

                    PROBLEM 1:
                    How to use WebDriver.findElements() to get access to all MeiMenu objects in the page ?
                    We tried different attempts, but we always end up with an empty list. What is the best option ?

                    PROBLEM 2:
                    Once we have "List<WebElement> mainMenus" filled, we were hoping to extract the "meiMenuId" attribute from each of the elements and then use org.hamcrest.collection.IsIterableContainingInAnyOrder.containsInAnyOrder() to make sure the content is right. How can we achieve this ?
                    Again for both of these, use JavaScript. Locators and WebDriver/Selenium are for recording and playing back user actions. Locators identify specific interactive areas of a widget and are not "selectors" in the sense of CSS selectors - they never identify groups of elements.

                    If you want to do any kind of programmatic validation that the UI has come up as expected, there is no reason to try to cram such testing through the WebDriver API; just use JavaScript, which is going to lead to much more understandable and straightforward code. Presumably, all of these menus you're trying to check appear together in a Menubar, or perhaps you have some other JavaScript data structure where they are all registered - just use that in JavaScript.

                    Comment

                    Working...
                    X