geb: RequiredPageContentNotPresent after login request - geb

I have the following setup:
class LoginPage extends Page {
def login() { //fill user/pass, click "login"}
}
class IndexPage extends Page {
static content = {
sideTabs { module SideTabs }
}
}
class TabAPage extends Page {
//...
}
class SideTabs extends Module {
static content = {
tabA(to: TabAPage) { $(".sidetab-label", text: "tab A") }
}
}
class TabALoginSpec extends GebReportingSpec {
def setup() {
to LoginPage
}
def "test foo"() { /* ... */ }
def "test bar"() {
when:
login()
// report "after login"
at IndexPage
sideTabs.tabA.click()
//...
then:
// ...
}
}
When I execute "test bar", I get:
geb.error.RequiredPageContentNotPresent: The required page content 'IndexPage -> sideTabs: SideTabs -> tabA: geb.navigator.EmptyNavigator' is not present
When I execute "test bar" and uncomment the report line, test passes.
So I'm guessing the problem lies in "at IndexPage" returning before the page has finished loading. I don't think login() should wait on any content, because if the login fails, then there's no content loading issue, and if login succeeds - the page transitions to Index. I've attempted adding wait:true to tabA's content definition, to no avail.
Funnily, on the screenshots geb produces in the wake of "test bar"'s failure, I can actually spot the tabA element...
Help appreciated.
Update:
adding
void onLoad(Page previousPage) {
waitFor { sideTabs.tabA }
}
to IndexPage seems to iron-out the problem so-far. I'm unclear why adding wait:true to the content definition did not yield the same result.

Adding a required:false should work but placed here:
class SideTabs extends Module {
static content = {
tabA(required:false, to: TabAPage) { $(".sidetab-label", text: "tab A") }
}
}

If you don't want to wait for all of the content to load before interacting with the page then use required: false when defining your content.
e.g.
class IndexPage extends Page {
static content = {
sideTabs(required: false) { module SideTabs }
}
}
The intended use of the required statement is for hidden content that can be made visible after page interaction. But it should help in this scenario.

Good practice is to add the at in the IndexPage class, check documentation here.
Your class should look like:
class IndexPage extends Page {
static at = {...}
static content = {
sideTabs { module SideTabs }
}
}
I think that using the report slows down the test, causing the page to render correctly.

Related

How to navigate to same page before running any test script in GEB SPOCK

I am creating one automation script using geb spock and groovy. In my test class I have multiple tests and I want to navigate to home page before running any of the test meaning
login to application and navigate to home page (default page after login is home page).
Click on Link1. Check Page.navigate to home page
Click on Link2. Check Page . navigate to home page
Click on Link3. Check Page. navigate to home page...
I created a method and try to reuse it in my geb test but getting the following error:
geb.error.PageInstanceNotInitializedException: Instance of page class pages.Test_HomePage has not been initialized. Please pass it to Browser.to(), Browser.via(), Browser.page() or Browser.at() before using it.
at geb.Page.uninitializedException(Page.groovy:521)
at geb.content.UninitializedPageContentSupport.getContent(UninitializedPageContentSupport.groovy:30)
at geb.content.PageContentSupport.propertyMissing(PageContentSupport.groovy:39)
at geb.Page.propertyMissing(Page.groovy:99)
at pages.Test_HomePage.clickOnHomePage(Test_HomePage.groovy:46)
at com.abc.vcctest.TestNavigationInitialTestSpec.Navigate to Home page of test portal(TestNavigationInitialTestSpec.groovy:44)
Here is my sample geb test:
#TestMixin(GrailsUnitTestMixin)
class TestNavigationInitialTestSpec extends LoginBaseTestSpec {
#Shared
Test_HomePage test_HomePage = new Test_HomePage()
def cleanupSpec() {
browser.close()
}
def setup() {
}
def cleanup() {
}
def navigateToHomePage() {
Test_HomePage test_HomePage = at Test_HomePage
test_HomePage.clickOnHomePage()
at Test_HomePage
}
def "Navigate to Home page of test portal"() {
given:
HomePage homePage = at HomePage
when: "Click on Home tab/link"
navigateToHomePage()
test_HomePage.clickOnHomePage()
def crashDisplayed = test_HomePage.crashPageDisplayed()
then: "You are on Home Page"
!crashDisplayed || { at Test_HomePage }
}
def "Navigate to Change Password page of testportal"() {
given:
at Test_HomePage
when: "Click on Change Password"
test_HomePage.clickOnChangePassword()
Test_ChangePassword test_ChangePassword = at Test_ChangePassword
def crashDisplayed1 = test_ChangePassword.crashPageDisplayed()
then: "You are on Change Password Page"
!crashDisplayed1 || { at Test_ChangePassword }
at Test_ChangePassword
}
This is my Test_HomePage class:
class Test_HomePage extends HomePage2{
static url = '/home'
static at = {
waitFor(message:"The Test Portal title is missing."){driver.title == "Test Portal"}
}
static content = {
pageCrash(required: false, cache: false) { $("h1") }
homePageLink {
$("a[href = '/Link1']")
}
changePasswordLink{
$("a[href='/Link2']")
}
updateEmailLink{
$ ("a[href = '/Link3']")
}
}
void crashPageDisplayed() {
if (pageCrash.isDisplayed()) {
driver.navigate().back()
}
}
void clickOnHomePage() {
homePageLink.click()
}
void clickOnChangePassword(){
changePasswordLink.click()
}
void clickUpdateEmail(){
updateEmailLink.click()
}
}
I am new to geb and spock and I might be doing a very silly mistake but any help would be really appreciated or suggestion to implement this in an effective way.
You should make you feature methods independent of each other, so they can be called in any order. Why don't you put something like to Test_HomePage into your setup() method? Then it will be called at the beginning of each feature method. Even if you do it in the given: block for each single feature method, you still need to before you can check at. I think you should study the Book of Geb a little bit. This is what the error message is trying to explain to you, by the way:
geb.error.PageInstanceNotInitializedException:
Instance of page class pages.Test_HomePage has not been initialized.
Please pass it to Browser.to(), Browser.via(), Browser.page() or Browser.at() before using it.
In navigateToHomePage(), your first command should therefore be:
Test_HomePage test_HomePage = to Test_HomePage

can't pass value to the input fied in test case

I would like to pass a value to the input fied, in the test case. however this is not working. Below is my code.
class Mypage extends Page {
static content = {
inputfield { withFrame ("myFrame") {$('input',name:'myInputField') }
}
}
}
Then tested it this way:
given:
String thevalue = "hello"
then:
to Mypage
and:
inputfield >> thevalue
With this code I get the
StaleElementReference error and the chrome driver information
I then tried the following by putting the thevalue variable in Mypage class:
class Mypage extends Page {
public String thevalue = "hello"
static content = {
inputfield { withFrame ("myFrame")
{$('input',name:'myInputField') }
}
}
}
Then tested it the same way with no given:
then:
to Mypage
and:
inputfield >> thevalue
I Still get the same error.
I then tried a third form:
class Mypage extends Page {
//public String thevalue = "hello"
static content = {
inputfield { withFrame ("myFrame")
{ thevalue -> $('input',name:'myInputField').value(thevalue ) }
}
}
}
Then tested it in 2 ways:
then:
to Mypage
and:
inputfield("hello")
and this way:
then:
to Mypage
and:
inputfield >> "hello"
The only way which is working is when I pass the value directly in the class
inputfield { withFrame ("myFrame")
{ $('input',name:'myInputField').value("hello") }
But the goal is to the pass the value in the test. How do I do that
If you really don't want to follow Rushby's suggestion and stick to the path you shown in your question then the following will work:
class Mypage extends Page {
static content = {
inputfield { thevalue ->
withFrame ("myFrame") {
$('input',name:'myInputField').value(thevalue)
}
}
}
}
and
then:
to Mypage
and:
inputfield("hello")
If you follow the Geb example from The Book of Geb you have your page and it contains an iframe. The iframe in the example has its own page object:
class PageWithFrame extends Page {
static content = {
myFrame(page: FrameDescribingPage) { $('#frame-id') }
}
}
//This is your iframe page object
class FrameDescribingPage extends Page {
static content = {
myTextField { $('input',name:'myInputField') }
}
}
Now to interact with it, you do this within your test method:
def "my test method"() {
when:
to PageWithFrame
withFrame(myFrame) {
myTextField.value("whatever")
}
//etc
}

Geb cannot see my class as content page

When i try to run my test i get this Exception:
groovy.lang.MissingPropertyException: Unable to resolve code as content for pages FactIndexPage, or as a property on its Navigator context. Is code a class you forgot to import?
Here my test class
import geb.spock.GebReportingSpec
import pages.*
import spock.lang.Stepwise
#Stepwise
class FactControllerSpec extends GebReportingSpec {
def "test de la presence du code dans la page d'acceuil"() {
given: "test que le champ code de facture est bien present"
when:
to IndexFactPage
then: $("form", name: code).find("input", type: "button")
}
}
And below the class who describe my page content
import geb.Page
class IndexFactPage extends Page {
static url = "/fact/mmapay"
static at = {
$("h2").text() == "Pour régler votre facture, veuillez saisir:" }
static content = {
codeField { $("input[name = code]") }
nomField { $("input[name = nom]") }
prenomField { $("input[name = prenom]") }
montantField { $("input[name = montant]") }
chercher1 { $("input", type: "button") }
abandonner1 { $("input", type: "button") }
}
}
Please help me to resolve my class IndexFactPage as a content page..
Thank you in advance..
The part causing you problems is this $("form", name: code).
$("form", name: "code") will find you a form named code, this may not be what you're after though based on your page and spec snippets.
Without seeing the page source there's no way to provide a proper solution but here's some observations from your IndexFactPage
static content = {
codeField { $("input[name = code]") }
nomField { $("input[name = nom]") }
prenomField { $("input[name = prenom]") }
montantField { $("input[name = montant]") }
chercher1 { $("input", type: "button") }
abandonner1 { $("input", type: "button") }
}
These fields codeField, nomField, prenomField, and montantField all look like they map to individual fields but chercher1 and abandonner1 will select the same button.
Based on what your spec states for it's purpose to be then possibly your spec should look like this -
import geb.spock.GebReportingSpec
import pages.*
import spock.lang.Stepwise
#Stepwise
class FactControllerSpec extends GebReportingSpec {
def "test the presence of the code field on the home page"() {
when: "we are on the home page"
to IndexFactPage
then: "the code field is present"
codeField.present
}
}

Getting error while running Geb test using spock and maven

I'm getting the following error while running groovy scripts with maven:
Signup Test Happy Path(SignUpPageSpec): The required page content
'usernamefield - SimplePageContent (owner: JiraSignupPage, args: [],
value: null') is not present.
Can anybody help me to resolve this error. Following are scripts:
JiraSignUpPage.groovy
package test.groovy.script
import geb.Page;
//import main.groovy.script.*;
class JiraSignupPage extends Page
{
static url = "/vcty-jira"
static at = { title == "System Dashboard - Velocity Jira" }
static content = {
usernamefield { $("input#login-form-username") }
passwordfield { $('input#login-form-password') }
submitButton(to: JiraSignUpResultPage) { $('button#login-form-submit') }
}
}
JiraSignUpResultPage.groovy
package test.groovy.script
import geb.Page;
public class JiraSignUpResultPage extends Page
{
static url = "/jira/secure/Dashboard.jspa"
static at = { title == "Amit - Jira Tracker" }
}
SignUpPageSpec.groovy
import spock.lang.Stepwise;
import geb.spock.GebReportingSpec;
import geb.Page;
//import main.groovy.script.*;
import test.groovy.script.*;
#Stepwise
public class SignUpPageSpec extends GebReportingSpec {
def "Signup Test Happy Path"() {
given: "I'm at the sign up form"
to JiraSignupPage
when: "I signup as a valid user"
usernamefield = "xyz"
passwordfield = "xyz"
submitButton.click()
then: "I'm at the result page"
at JiraSignUpResultPage
}
}
GebConfig.groovy
import org.openqa.selenium.firefox.FirefoxDriver
import geb.waiting.*;
driver = { new FirefoxDriver() }
baseUrl = "http://172.17.48.65:8080/"
reportsDir = new File("target/geb-reports")
reportOnTestFailureOnly = true
waiting {
timeout = 20
retryInterval = 0.5
}
I'm running mvn.test to run these test. It causes browser to open but ends up with error.
The error tells you that your usernamefield content definition returned an empty navigator - an input with id login-form-username has not been found on the page you navigated to.
Probably your page is not fully loaded.
Try adding usernamefield.displayed to 'at' block
or add 'wait' to page element
usernamefield(wait: true) { $("input#login-form-username") }

Is it possible to pass though a template option to a module (as a variable parameter)

I have a page that uses a module for defining form content. It is rather generically usable but needs different resulting pages after form actions have been triggered (button click, etc.).
class CreateOrganisationPage extends Page {
static url = "#contact/organisation/create"
static at = {
form.displayed
}
static content = {
form(wait: true) {
module BusinessEntityFormModule, businessEntityName: 'organisation'
}
}
}
The module implementing the form content contains a UI control saveCommand that requires that a page ViewBusinessEntityPage is navigated to after submitting. In order to keep that module more reusable across different tests I wanted to provide that page as a parameter.
What is the best approach to do so?
class BusinessEntityFormModule extends Module {
String businessEntityName = "entity"
String idPrefix = "edit"
static content = {
self {
def id = "$idPrefix-" + StringUtils.capitalize(businessEntityName)
$("form", id: id)
}
saveCommand(to: ViewBusinessEntityPage) {
$('[data-command="save"]')
}
}
}
Simply define a property for the destination page in your module and use it in the content definition:
class BusinessEntityFormModule extends Module {
Class postSavePage
static content = {
saveCommand(to: postSavePage) {
$('[data-command="save"]')
}
}
}
And then set it when defining the module as part of the page:
class CreateOrganisationPage extends Page {
static content = {
form(wait: true) {
module BusinessEntityFormModule, businessEntityName: 'organisation', postSavePage: ViewBusinessEntityPage
}
}
}