How to test choices.js dropdown

my html contains the following:

<select class="form-select" id="code" name="code" autocomplete="off">
  <optgroup label="None">
    <option value="" selected>(none)</option>
  </optgroup>
  <optgroup label="General">
    <option value="CODE1">job code one (CODE1)</option>
  </optgroup>
</select>

choices.js changes that to the following on the client:

<div class="choices" data-type="select-one" tabindex="0" role="combobox" aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">
  <div class="choices__inner">
    <select class="form-select choices__input" id="code" name="code" autocomplete="off" hidden="" tabindex="-1" data-choice="active">
      <option value="" data-custom-properties="[object Object]">(none)</option>
    </select>
    <div class="choices__list choices__list--single">
      <div class="choices__item choices__item--selectable" data-item="" data-id="1" data-value="" data-custom-properties="[object Object]" aria-selected="true">
	    (none)
	  </div>
    </div>
  </div>
  <div class="choices__list choices__list--dropdown" aria-expanded="false">
    <input type="text" class="choices__input choices__input--cloned" autocomplete="off" autocapitalize="none" spellcheck="false" role="textbox" aria-autocomplete="list" aria-label="(none)" placeholder="">
    <div class="choices__list" role="listbox">
      <div class="choices__group " role="group" data-group="" data-id="324973987686" data-value="None">
        <div class="choices__heading">
		  None
		</div>
      </div>
      <div id="choices--code-item-choice-1" class="choices__item choices__item--choice is-selected choices__item--selectable is-highlighted" role="treeitem" data-choice="" data-id="1" data-value="" data-select-text="Press to select" data-choice-selectable="" aria-selected="true">
	    (none)
	  </div>
      <div class="choices__group " role="group" data-group="" data-id="1234391428105" data-value="General">
        <div class="choices__heading">
		  General
		</div>
      </div>
      <div id="choices--code-item-choice-2" class="choices__item choices__item--choice choices__item--selectable" role="treeitem" data-choice="" data-id="2" data-value="CODE1" data-select-text="Press to select" data-choice-selectable="">
	    job code one (CODE1)
	  </div>
    </div>
  </div>
</div>

In my test I have the following:

  • the verifyElementPresent is success
WebUI.verifyElementPresent(findTestObject("service/job/selectcode"), 2)
CustomKeywords.'idweb.IdwebCustom.setChoiceSelect'(findTestObject("service/job/selectcode"), "CODE1")

In my custom keyword, I currently have the following (this does not work):

public void setChoiceSelect(TestObject obj, String value) {
        # parent has null and error on following line
	TestObject parent = obj.getParentObject().getParentObject()
	TestObject dropdown = parent.findXpath('//div[contains(@class, "choices__list") and contains(@class, "choices__list--single")]')
	TestObject dropdownlist = parent.findXpath('//div[contains(@class, "choices__list") and contains(@class, "choices__list--dropdown")]')
	TestObject item = dropdownlist.findXpath('//div[@data-value="' + value + '"]')

        # click on dropdown
        # wait for dropdownlist to show
        # click on item
}

I need to do the following for my test in my keyword setChoiceSelect:

  • pass my TestObject, the html select which is hidden

  • click on the "<div class="choices__list choices__list--single">" with location relative to the passed select

  • wait for β€œ<div class="choices__list choices__list--dropdown" aria-expanded="false">” to show with location relative to passed select

  • click on β€œ<div id="choices--code-item-choice-2" data-value="CODE1" ...>” with location relative to passed select

My current error in my keyword setChoiceSelect is:
TestObject parent = obj.getParentObject() return null. The passed obj is good, but getParentObject() returns null and I don’t know why.

Using my passed TestObject how do I get my other related elements that I need to click on and wait to be shown?

Thanks.

2 Likes

I am impressed that your post above is very well witten. It provides enough information which enabled me to understand your problem well.


You seem to have a misunderstanding about what a TestObject instance is.

You think that a TestObject is an alias to an instance of org.openqa.selenium.WebElement.

You are wrong.

TestObject is a container of something like org.openqa.selenium.By. It does not point an HTML DOM node in a browser.

1 Like

See the following example.

Test Case:

import static com.kms.katalon.core.testobject.ObjectRepository.findTestObject

import com.kms.katalon.core.testobject.TestObject as TestObject

def printTestObject(TestObject tObj) {
	println "tObj=${tObj.toString()}"
	println "tObj.getClass().getName()=${tObj.getClass().getName()}"
	println "tObj.getParentObject()=${tObj.getParentObject().toString()}"
	println "tObj.getParentObject().getClass().getName()=${tObj.getParentObject().getClass().getName()}"
	println "tObj.getParentObject().getParentObject()=${tObj.getParentObject().getParentObject()}"
}
TestObject anchorTO = findTestObject("sub/a_Make_Appointment")
printTestObject(anchorTO)

Console output:

2023-09-01 08:32:52.742 INFO  c.k.katalon.core.main.TestCaseExecutor   - --------------------
2023-09-01 08:32:52.745 INFO  c.k.katalon.core.main.TestCaseExecutor   - START Test Cases/TC1
Test Cases/TC1
[:]
tObj=TestObject - 'Object Repository/sub/a_Make_Appointment'
tObj.getClass().getName()=com.kms.katalon.core.testobject.TestObject
tObj.getParentObject()=null
tObj.getParentObject().getClass().getName()=org.codehaus.groovy.runtime.NullObject
2023-09-01 08:32:53.530 ERROR c.k.katalon.core.main.TestCaseExecutor   - ❌ Test Cases/TC1 FAILED.
Reason:
java.lang.NullPointerException: Cannot invoke method getParentObject() on null object
	at TC1.printTestObject(TC1:10)
	at Script1693523810480$printTestObject.callCurrent(Unknown Source)
	at TC1.run(TC1:13)
	at com.kms.katalon.core.main.ScriptEngine.run(ScriptEngine.java:194)
	at com.kms.katalon.core.main.ScriptEngine.runScriptAsRawText(ScriptEngine.java:119)
	at com.kms.katalon.core.main.TestCaseExecutor.runScript(TestCaseExecutor.java:448)
	at com.kms.katalon.core.main.TestCaseExecutor.doExecute(TestCaseExecutor.java:439)
	at com.kms.katalon.core.main.TestCaseExecutor.processExecutionPhase(TestCaseExecutor.java:418)
	at com.kms.katalon.core.main.TestCaseExecutor.accessMainPhase(TestCaseExecutor.java:410)
	at com.kms.katalon.core.main.TestCaseExecutor.execute(TestCaseExecutor.java:285)
	at com.kms.katalon.core.main.TestCaseMain.runTestCase(TestCaseMain.java:144)
	at com.kms.katalon.core.main.TestCaseMain.runTestCase(TestCaseMain.java:135)
	at com.kms.katalon.core.main.TestCaseMain$runTestCase$0.call(Unknown Source)
	at TempTestCase1693524767255.run(TempTestCase1693524767255.groovy:25)

2023-09-01 08:32:53.574 ERROR c.k.katalon.core.main.TestCaseExecutor   - ❌ Test Cases/TC1 FAILED.
Reason:
java.lang.NullPointerException: Cannot invoke method getParentObject() on null object
	at TC1.printTestObject(TC1:10)
	at Script1693523810480$printTestObject.callCurrent(Unknown Source)
	at TC1.run(TC1:13)
	at com.kms.katalon.core.main.ScriptEngine.run(ScriptEngine.java:194)
	at com.kms.katalon.core.main.ScriptEngine.runScriptAsRawText(ScriptEngine.java:119)
	at com.kms.katalon.core.main.TestCaseExecutor.runScript(TestCaseExecutor.java:448)
	at com.kms.katalon.core.main.TestCaseExecutor.doExecute(TestCaseExecutor.java:439)
	at com.kms.katalon.core.main.TestCaseExecutor.processExecutionPhase(TestCaseExecutor.java:418)
	at com.kms.katalon.core.main.TestCaseExecutor.accessMainPhase(TestCaseExecutor.java:410)
	at com.kms.katalon.core.main.TestCaseExecutor.execute(TestCaseExecutor.java:285)
	at com.kms.katalon.core.main.TestCaseMain.runTestCase(TestCaseMain.java:144)
	at com.kms.katalon.core.main.TestCaseMain.runTestCase(TestCaseMain.java:135)
	at com.kms.katalon.core.main.TestCaseMain$runTestCase$0.call(Unknown Source)
	at TempTestCase1693524767255.run(TempTestCase1693524767255.groovy:25)

Test Cases/TC1
ERROR
2023-09-01 08:32:53.612 INFO  c.k.katalon.core.main.TestCaseExecutor   - END Test Cases/TC1

Please note that

  • TestObject.getParentObject() returned null.
  • TestObject.getParentObject().getParentObject() raised an Exception

This is the same as the problem of your keyword.

1 Like

You can read the source of TestObject at

Quoting from this:

public class TestObject implements SelectorCollector, ITestObject {

    private TestObject parentObject; // Typically is parent Frame

...

    /**
     * Get the parent object of this test object
     * 
     * @return the parent object of this test object
     */
    public TestObject getParentObject() {
        return parentObject;
    }

    /**
     * Set the parent object of this test object
     * 
     * @param parentObject the parent object to set
     */
    public void setParentObject(TestObject parentObject) {
        this.parentObject = parentObject;
    }

See

Please find that TestObject.getParentObject() will return the setting of the section labeled β€œHave parent object?”. If you leave this section to be β€œNo”, then TestObject.getParentObject() will return null.

2 Likes

kudos to both!!

1 Like

Alright, you sent me in the right direction and I am able to make what I need work by doing the following.

I have Object to find the parent element:
//form//select[@id="code"]["hidden"]/parent::div/parent::div

I call my custom Keyword with my TestObject:
CustomKeywords.'idweb.IdwebCustom.setChoiceSelect'(findTestObject("selectcodeparent"), GlobalVariable.jobcode)

I have my custom Keyword:

public void setChoiceSelect(TestObject obj, String value) {
	WebElement codeselect = WebUiCommonHelper.findWebElement(obj, 2)
	WebElement dropdown = codeselect.findElement(By.xpath('//div[contains(@class, "choices__list") and contains(@class, "choices__list--single")]'))
	WebElement dropdownlist = codeselect.findElement(By.xpath('//div[contains(@class, "choices__list") and contains(@class, "choices__list--dropdown")]'))
	WebElement dropdownitem = codeselect.findElement(By.xpath('//div[@data-value="' + value + '"]'))

	dropdown.click()
	WebUI.delay(1)
	dropdownitem.click()
}
1 Like

The dropdownlist variable is defined but not used. If so, you can drop a line: