I want to set value for a datepicker, it has attribute 'readonly'. I tried removeAttr()
to remove the attribute, but got error like this:
Caught: groovy.lang.MissingMethodException: No signature of method: geb.navigator.NonEmptyNavigator.removeAttr() is applicable for argument types: (java.lang.String) values: [readonly]
How can I remove an attribute in Geb?
Souce code:
<input ng-show="!date" class="ui-form-control ng-isolate-scope" name="date" placeholder="StartTime" ui-datepicker-trigger="" selected-date="date" readonly="readonly" type="text">
my geb code:
$('input', placeholder: 'StartTime').removeAttr('readonly')
Thanks all
Because the OP said it was an AngularJS datepicker and I never had any contact with Angular, myself not being a front-end guy, I wanted to learn something and now I am sharing it with you, for whatever it is worth.
Here is a Geb test which does not use any JS, but gathers information from and interacts with the Angular datepicker purely by means of Geb or Selenium. The test shows how to
myNavigator.singleElement().getAttribute("value")
(also works for inactive controls),Page:
package de.scrum_master.stackoverflow
import geb.Page
import geb.navigator.Navigator
import java.time.Month
import java.time.format.TextStyle
import static java.util.Calendar.*
class DatePickerPage extends Page {
static url = "https://material.angularjs.org/1.1.2/demo/datepicker"
static at = { heading == "Datepicker" }
static now = new GregorianCalendar()
static content = {
heading { $("h2 > span.md-breadcrumb-page.ng-binding").text() }
datePickerButtons { $("md-datepicker > button") }
datePickerInputFields { $(".md-datepicker-input") }
activeDatePicker(required: false) { $(".md-datepicker-calendar-pane.md-pane-open") }
selectedDate { activeDatePicker.$(".md-calendar-selected-date") }
currentMonthLabel {
activeDatePicker
.$("td.md-calendar-month-label", text: "${getMonthShort(now)} ${now.get(YEAR)}")
}
selectedMonth(required: false) { $("td.md-calendar-selected-date") }
}
String getDatePickerValue(Navigator datePickerInputField) {
datePickerInputField.singleElement().getAttribute("value")
}
Navigator getMonthLabel(Calendar calendar) {
$(".md-calendar-month-label", text: "${calendar.get(YEAR)}").closest("tbody")
.$("span", text: getMonthShort(calendar)).closest("td")
}
Navigator getDayOfMonthLabel(Calendar calendar) {
activeDatePicker
.$(".md-calendar-month-label", text: "${getMonthShort(calendar)} ${calendar.get(YEAR)}")
.closest("tbody")
.$("span", text: "${calendar.get(DAY_OF_MONTH)}")
.closest("td")
}
String getMonthShort(Calendar calendar) {
Month
.of(calendar.get(MONTH) + 1)
.getDisplayName(TextStyle.FULL, new Locale("en"))
.substring(0, 3)
}
}
Test:
package de.scrum_master.stackoverflow
import geb.module.FormElement
import geb.spock.GebReportingSpec
import spock.lang.Unroll
import static java.util.Calendar.*
class DatePickerIT extends GebReportingSpec {
def now = new GregorianCalendar()
def xmas = new GregorianCalendar(now.get(YEAR), 12 - 1, 25)
@Unroll
def "Get selected date from Angular date label"() {
when: "a date picker on the Angular demo page is chosen"
DatePickerPage page = to DatePickerPage
def datePickerInputField = page.datePickerInputFields[datePickerIndex]
then: "that date picker instance is (in-)active as expected"
datePickerInputField.module(FormElement).enabled == isEnabled
when: "the active date is read from the date picker's text input field"
String activeDateString = page.getDatePickerValue(datePickerInputField)
String todayString = "${now.get(MONTH) + 1}/${now.get(DAY_OF_MONTH)}/${now.get(YEAR)}"
then: "it is today"
todayString == activeDateString
where:
datePickerIndex << [0, 1, 2, 3, 4, 5, 6, 7, 8]
isEnabled << [true, false, true, true, true, true, true, true, true]
}
@Unroll
def "Get selected date from opened Angular date picker"() {
when: "a date picker on the Angular demo page is chosen"
DatePickerPage page = to DatePickerPage
def datePickerButton = page.datePickerButtons[datePickerIndex]
then: "that date picker instance is active (not greyed out)"
datePickerButton.module(FormElement).enabled
when: "the calendar button for the date picker is clicked"
datePickerButton.click()
then: "the date picker pop-up opens, displaying the current month"
waitFor 3, { page.activeDatePicker.displayed }
when: "the active date's timestamp is read from the highlighted day in the calendar"
def selectedDateMillis = page.selectedDate.attr("data-timestamp") as long
def sinceMidnightMillis = now.getTimeInMillis() - selectedDateMillis
then: "it is today"
sinceMidnightMillis >= 0
sinceMidnightMillis < 24 * 60 * 60 * 1000
where:
datePickerIndex << [0, 2, 4, 5, 6, 7, 8]
}
def "Set date in Angular date picker"() {
when: "the first date picker's calendar button on the Angular demo page is clicked"
DatePickerPage page = to DatePickerPage
page.datePickerButtons[0].click()
then: "the date picker pop-up opens, displaying the current month"
waitFor 3, { page.activeDatePicker.displayed }
when: "the current month label is clicked"
page.currentMonthLabel.click()
then: "the month selection page opens"
waitFor 3, { page.selectedMonth.displayed }
page.selectedMonth.text() == page.getMonthShort(now)
when: "December for the current year is selected"
page.getMonthLabel(xmas).click()
then: "the month selection page closes again"
waitFor 3, { !page.selectedMonth.displayed }
when: "Xmas Day (25) is selected on the month calendar"
page.getDayOfMonthLabel(xmas).click()
then: "the date picker pop-up closes again"
waitFor 3, { !page.activeDatePicker.displayed }
when: "the active date is read from the date picker's text input field"
String activeDateString = page.getDatePickerValue(page.datePickerInputFields[0])
String xmasDayString = "${xmas.get(MONTH) + 1}/${xmas.get(DAY_OF_MONTH)}/${xmas.get(YEAR)}"
then: "it is Xmas Day of the current year"
xmasDayString == activeDateString
}
}
The advantage of this approach is obvious: You only interact with the page in a way the user would/could also do, thus avoiding to manipulate the page in a way impossible for a user (which would result in a bad test).
Update: I refactored the code into using a page object.