Skip to content

Commit

Permalink
Added support for selectElement based on tebeka#238
Browse files Browse the repository at this point in the history
  • Loading branch information
jabbahotep committed Sep 1, 2023
1 parent e9100b7 commit 28963fb
Showing 1 changed file with 287 additions and 0 deletions.
287 changes: 287 additions & 0 deletions select.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
package selenium

import (
"fmt"
"strings"
)

// SelectElement WebElement that is specific to the Select Dropdown
type SelectElement struct {
element WebElement
isMulti bool
}

// Select Creates a SelectElement
// @param el The initial WebElement
func Select(el WebElement) (se SelectElement, err error) {
se = SelectElement{}

tagName, err := el.TagName()
if err != nil || strings.ToLower(tagName) != "select" {
err = fmt.Errorf(`element should have been "select" but was "%s"`, tagName)
return
}

se.element = el
mult, err2 := el.GetAttribute("multiple")
se.isMulti = (err2 != nil && strings.ToLower(mult) != "false")

return
}

// GetElement Gets the raw WebElement
func (s SelectElement) GetElement() WebElement {
return s.element
}

// IsMultiple Whether this select element support selecting multiple options at the same time? This
// is done by checking the value of the "multiple" attribute.
func (s SelectElement) IsMultiple() bool {
return s.isMulti
}

// GetOptions Returns all of the options of that Select
func (s SelectElement) GetOptions() ([]WebElement, error) {
return s.element.FindElements(ByTagName, "option")
}

// GetAllSelectedOptions Returns all of the options of that Select that are selected
func (s SelectElement) GetAllSelectedOptions() ([]WebElement, error) {
// return getOptions().stream().filter(WebElement::isSelected).collect(Collectors.toList());

var opts []WebElement
return opts, nil
}

// GetFirstSelectedOption Returns the first selected option of the Select Element
func (s SelectElement) GetFirstSelectedOption() (opt WebElement, err error) {
opts, err := s.GetAllSelectedOptions()
if err != nil {
return
}
opt = opts[0]
return
}

// SelectByVisibleText Select all options that display text matching the argument. That is,
// when given "Bar" this would select an option like:
//
// <option value="foo">Bar</option>
//
// @param text The visible text to match against
//
func (s SelectElement) SelectByVisibleText(text string) error {
// try to find the option via XPATH ...
options, err := s.element.FindElements(ByXPATH, `.//option[normalize-space(.) = "`+escapeQuotes(text)+`"]`)
if err != nil {
return err
}

for _, option := range options {
s.setSelected(option, true)
if !s.isMulti {
return nil
}
}

matched := len(options) > 0
if !matched && strings.Contains(text, " ") {
subStringWithoutSpace := getLongestSubstringWithoutSpace(text)
var candidates []WebElement
if subStringWithoutSpace == "" {
// hmm, text is either empty or contains only spaces - get all options ...
candidates, err = s.GetOptions()
} else {
// get candidates via XPATH ...
candidates, err = s.element.FindElements(ByXPATH, `.//option[contains(., "`+escapeQuotes(subStringWithoutSpace)+`")]`)
}

if err != nil {
return err
}

trimmed := strings.TrimSpace(text)

for _, option := range candidates {
o, err := option.Text()
if err != nil {
return err
}
if trimmed == strings.TrimSpace(o) {
s.setSelected(option, true)
if !s.isMulti {
return nil
}
matched = true
}
}
}
if !matched {
return fmt.Errorf("cannot locate option with text: %s", text)
}
return nil
}

// SelectByIndex Select the option at the given index. This is done by examining the "index" attribute of an
// element, and not merely by counting.
//
// @param idx The option at this index will be selected
func (s SelectElement) SelectByIndex(idx int) error {
return s.setSelectedByIndex(idx, true)
}

// SelectByValue Select all options that have a value matching the argument. That is, when given "foo" this
// would select an option like:
//
// <option value="foo">Bar</option>
//
// @param value The value to match against

func (s SelectElement) SelectByValue(value string) error {
opts, err := s.findOptionsByValue(value)
if err != nil {
return err
}
for _, option := range opts {
s.setSelected(option, true)
if !s.isMulti {
return nil
}
}
return nil
}

// DeselectAll Clear all selected entries. This is only valid when the SELECT supports multiple selections.
func (s SelectElement) DeselectAll() error {
if !s.isMulti {
return fmt.Errorf("you may only deselect all options of a multi-select")
}

opts, err := s.GetOptions()
if err != nil {
return err
}
for _, o := range opts {
err = s.setSelected(o, false)
if err != nil {
return err
}
}
return nil
}

// DeselectByValue Deselect all options that have a value matching the argument. That is, when given "foo" this
// would deselect an option like:
//
// <option value="foo">Bar</option>
//
// @param value The value to match against
func (s SelectElement) DeselectByValue(value string) error {
if !s.isMulti {
return fmt.Errorf("you may only deselect all options of a multi-select")
}

opts, err := s.findOptionsByValue(value)
if err != nil {
return err
}
for _, o := range opts {
err = s.setSelected(o, false)
if err != nil {
return err
}
}
return nil
}

// DeselectByIndex Deselect the option at the given index. This is done by examining the "index" attribute of an
// element, and not merely by counting.
//
// @param index The option at this index will be deselected
func (s SelectElement) DeselectByIndex(index int) error {
if !s.isMulti {
return fmt.Errorf("you may only deselect all options of a multi-select")
}

return s.setSelectedByIndex(index, false)
}

// DeselectByVisibleText Deselect all options that display text matching the argument. That is,
// when given "Bar" this would deselect an option like:
//
// <option value="foo">Bar</option>
//
// @param text The visible text to match against
func (s SelectElement) DeselectByVisibleText(text string) error {
if !s.isMulti {
return fmt.Errorf("you may only deselect all options of a multi-select")
}

options, err := s.element.FindElements(ByXPATH, `.//option[normalize-space(.) = "`+escapeQuotes(text)+`"]`)
if err != nil {
return err
}
if len(options) == 0 {
return fmt.Errorf("Cannot locate option with text: " + text)
}

for _, option := range options {
err = s.setSelected(option, false)
if err != nil {
return err
}
}
return nil
}

func escapeQuotes(str string) string {
str1 := strings.Replace(str, `"`, `\"`, -1)
return str1
}

func getLongestSubstringWithoutSpace(s string) string {
result := ""
st := strings.Split(s, " ")
for _, t := range st {
if len(t) > len(result) {
result = t
}
}
return result
}

func (s SelectElement) findOptionsByValue(value string) (opts []WebElement, err error) {
opts, err = s.element.FindElements(ByXPATH, `.//option[@value = "`+escapeQuotes(value)+`"]`)
if err != nil {
return
}
if len(opts) == 0 {
err = fmt.Errorf("Cannot locate option with value: " + value)
}

return
}

func (s SelectElement) setSelectedByIndex(index int, selected bool) error {
idx := fmt.Sprintf("%d", index)
opts, err := s.element.FindElements(ByXPATH, `.//option[@index = "`+idx+`"]`)
if err != nil {
return err
}
if len(opts) == 0 {
err = fmt.Errorf("Cannot locate option with index: " + idx)
return err
}

err = s.setSelected(opts[index], selected)

return err
}

func (s SelectElement) setSelected(option WebElement, selected bool) (err error) {
sel, err := option.IsSelected()
if sel != selected && err == nil {
err = option.Click()
}
return err
}

0 comments on commit 28963fb

Please sign in to comment.