I'm writing a Geb test and I want to use a parameterized module, but I can't seem to get it working.
This is my page object:
class CreateNewOrganizationPage extends Page {
static url = "/organization/create"
static content = {
organizationTypes { module MultiSelect, id: "orgTypeIds" }
}
}
And this is my module:
class MultiSelect extends Module {
def id
static content = {
caret { $("#" + id + "-caret") }
}
}
And this is how I use it in a test:
when:
to CreateNewOrganizationPage
organizationTypes.caret.click()
It seems to be the same as the situation on this site:
http://swalsh.org/blog/2014/06/25/groovy-browser-automation-with-geb/
What am I doing wrong here? Because I get the following exception in the stacktrace:
groovy.lang.MissingMethodException: No signature of method: geb.content.PageContentTemplateFactoryDelegate.module() is applicable for argument types: (java.util.LinkedHashMap, java.lang.Class) values: [[id:orgTypeIds], class pages.management.MultiSelect]
Possible solutions: module(java.lang.Class), module(java.util.Map, java.lang.Class), module(geb.Module), module(java.lang.Class, java.lang.Object), module(java.util.Map, java.lang.Class, geb.navigator.Navigator), dump()
at geb.content.PageContentTemplateFactoryDelegate.module(PageContentTemplateFactoryDelegate.groovy:49)
at pages.management.CreateNewOrganizationPage._clinit__closure1$_closure3(CreateNewOrganizationPage.groovy:43)
at pages.management.CreateNewOrganizationPage._clinit__closure1$_closure3(CreateNewOrganizationPage.groovy)
at geb.content.PageContentTemplate.invokeFactory(PageContentTemplate.groovy:97)
at geb.content.PageContentTemplate.create_closure1(PageContentTemplate.groovy:59)
at geb.content.PageContentTemplate.create_closure1(PageContentTemplate.groovy)
at geb.content.PageContentTemplate.create(PageContentTemplate.groovy:82)
at geb.content.PageContentTemplate.get(PageContentTemplate.groovy:54)
at geb.content.DefaultPageContentSupport.getContent(DefaultPageContentSupport.groovy:42)
at geb.content.PageContentSupport.propertyMissing(PageContentSupport.groovy:39)
at geb.Page.propertyMissing(Page.groovy:112)
at geb.Browser.propertyMissing(Browser.groovy:216)
at geb.spock.GebSpec.propertyMissing(GebSpec.groovy:60)
at ManagementSpec.create new organization(ManagementSpec.groovy:21)
EDIT:
Problem solved:
specialDatePicker { module(new DatePicker(id: "someId")) }
Now it works!
Related
I am experimenting with some dynamic variable creation with GroovyShell and encountered an issue. First, the working code:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
defVar(glob)
shell.parse('test()').run()
This gives me the expected output:
--- hello ---
However, I want to call setVariable() dynamically when getVariable() is called, something like this:
static def defVar(def glob) {
glob.setVariable('test', new Test())
}
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable('test')) {
BindingTest.defVar(this)
}
return super.getVariable(name)
}
}
class Test {
def call() {
println("--- hello ---")
}
}
Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
//defVar(glob)
shell.parse('test()').run()
But this fails with the below error:
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
at Script1.run(Script1.groovy:1)
at Script1$run.call(Unknown Source)
at BindingTest.run(BindingTest.groovy:23)
When I added tracing code like this:
class MyBinding extends Binding {
def getVariable(String name) {
if (! hasVariable(name)) {
BindingTest.defVar(this)
}
println("getVariable: ${name}: ${super.getVariable(name).getClass().getName()}")
return super.getVariable(name)
}
void setVariable (String name, def val) {
println("setVariable: ${name}: ${val.getClass().getName()}")
super.setVariable(name, val)
}
def getProperty(String name) {
println("getProperty: ${name}: ${super.getProperty(name)}")
return super.getProperty(name)
}
void setProperty (String name, def val) {
println("setProperty: ${name}: ${val.getClass().getName()}")
super.setProperty(name, val)
}
}
In the working case, I get the below output:
setVariable: test: Test
--- hello ---
In the non-working case, I get this output:
setVariable: test: Test
getVariable: test: Test
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
...
Two questions:
In the working scenario, why is there no getVariable?
In the non-working scenario, why is the Test object returned by getVariable getting rejected?
Note that this issue is specific to callable values. If I set a simple value such as a string, to test, then both approaches work fine. E.g., with this sort of a change:
...
static def defVar(def glob) {
glob.setVariable('test', '--- hello ---')
}
...
shell.parse('println(test)').run()
I get the below identical output with both approaches:
setVariable: test: java.lang.String
getVariable: test: java.lang.String
setVariable: test: java.lang.String
--- hello ---
Though, I am not sure why setVariable gets called twice. I couldn't find any documentation explaining these puzzling behaviors. Could anybody here shed some light on them?
Please note, all the code snippets have been simplified for the ease of demonstrating the problem rather than for their intended purpose
When you use a property as a callable fallback, the Binding.getVariable() method does not get involved. This behavior is controlled by the metaclass, and in your case, it all drives to the execution of the MetaClassImpl.invokePropertyOrMissing() method. This method determines if
test()
should invoke test.call() (in case of an existing property), or should it fallback to the missingMethod() method. Here is what this method implementation looks like:
private Object invokePropertyOrMissing(Object object, String methodName, Object[] originalArguments, boolean fromInsideClass, boolean isCallToSuper) {
// if no method was found, try to find a closure defined as a field of the class and run it
Object value = null;
final MetaProperty metaProperty = this.getMetaProperty(methodName, false);
if (metaProperty != null)
value = metaProperty.getProperty(object);
else {
if (object instanceof Map)
value = ((Map)object).get(methodName);
}
if (value instanceof Closure) { // This test ensures that value != this If you ever change this ensure that value != this
Closure closure = (Closure) value;
MetaClass delegateMetaClass = closure.getMetaClass();
return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
}
if (object instanceof Script) {
Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
if (bindingVar != null) {
MetaClass bindingVarMC = ((MetaClassRegistryImpl) registry).getMetaClass(bindingVar);
return bindingVarMC.invokeMethod(bindingVar, CLOSURE_CALL_METHOD, originalArguments);
}
}
return invokeMissingMethod(object, methodName, originalArguments, null, isCallToSuper);
}
Source: https://github.com/apache/groovy/blob/GROOVY_2_5_X/src/main/groovy/groovy/lang/MetaClassImpl.java#L1262-L1287
Now, pay attention to the branch if (object instanceof Script) and how the binding variable gets retrieved. It tries to retrieve test variable from binding object using:
Object bindingVar = ((Script) object).getBinding().getVariables().get(methodName);
Your code would work if it was:
Object bindingVar = ((Script) object).getBinding().getVariable(methodName);
instead. But it's not.
You can make your second case working if you override getVariables() method instead of getVariable(String name), for instance:
class MyBinding extends Binding {
#Override
Map getVariables() {
return super.getVariables() + [
test: new Test()
]
}
}
Of course, your final implementation might be much more sophisticated. (E.g. you could get super.getVariables() map first, check which variables are missing and add a default variable only if the initial map was missing given variable.) But this is up to you.
Alternatively, consider using methodMissing instead of the binding variable fallback. It could make your code much easier to read and reason about.
Given the following setup in test.groovy:
class Main {
public static void main(String ... args) {
new Child().foo()
}
public static class Parent {
def foo() {
println 'from parent'
}
}
public static class Child extends Parent {
def foo() {
// def superRef = super.&foo // needed to try what’s commented below
def myClosure = {
super.foo() // doesn’t work, neither does anything of the following:
// this.super.foo()
// Child.super.foo()
// Child.this.super.foo()
// superRef()
println 'from child'
}
myClosure()
}
}
}
When I run groovy test.groovy (with Groovy 2.5.4 and at least all other versions I’ve tried), then I get the following error:
Caught: groovy.lang.MissingMethodException: No signature of method: static Main.foo() is applicable for argument types: () values: []
Possible solutions: any(), find(), use([Ljava.lang.Object;), is(java.lang.Object), any(groovy.lang.Closure), find(groovy.lang.Closure)
groovy.lang.MissingMethodException: No signature of method: static Main.foo() is applicable for argument types: () values: []
Possible solutions: any(), find(), use([Ljava.lang.Object;), is(java.lang.Object), any(groovy.lang.Closure), find(groovy.lang.Closure)
at Main$Child.methodMissing(test.groovy)
at Main$Child$_foo_closure1.doCall(test.groovy:16)
at Main$Child$_foo_closure1.doCall(test.groovy)
at Main$Child.foo(test.groovy:23)
at Main$Child$foo.call(Unknown Source)
at Main.main(test.groovy:3)
How can I refer to a super class method (Parent.foo) from a closure (myClosure) enclosed by the corresponding method of the subclass (Child.foo)?
(background: the closure in my actual code is needed to do something like myCloseable.withCloseable { super.foo(); … })
as soon as closure - it's another class (in terms of java), and you can't access super method from another class.
IHMO the only way:
class Main {
public static void main(String ... args) {
new Child().foo()
}
public static class Parent {
def foo() {
println 'from parent'
}
}
public static class Child extends Parent {
def superFoo(){
super.foo()
}
def foo() {
def myClosure = {
superFoo()
println 'from child'
}
myClosure()
}
}
}
I am trying to write a simple test with GEB and Spock. Following is the Page and Spec test:
Page
import geb.Page
class DashboardPage extends Page {
static url = "?root=dashboard"
static at = { pageTitle.text() == "Dashboard Content Area" }
static content = {
pageTitle(wait: 25) { $("div#content-view-title>h1") }
leaderBoardPeriodCombo { $("#leaderboardPeriod") }
//manualsMenu { module(ManualsMenuModule) }
}
def selectLeaderBoardPeriod(periodValue) {
leaderBoardPeriodCombo.value(periodValue)
}
}
Spec Test:
import geb.spock.GebSpec
import pages.DashboardPage
class LeaderboardSpec extends GebSpec {
def "change LeaderBoard type value"() {
when: to DashboardPage
then: at DashboardPage
when: DashboardPage.selectLeaderBoardPeriod("monthly")
then: at DashboardPage
}
}
But i am getting the following error:
groovy.lang.MissingMethodException:
No signature of method: static pages.DashboardPage.selectLeaderBoardPeriod() is applicable for argument types: (java.lang.String) values: [monthly]
Possible solutions: selectLeaderBoardPeriod(java.lang.String)
at specs.LeaderboardSpec.change LeaderBoard type value(LeaderboardSpec.groovy:13)
Results :
Tests in error:
LeaderboardSpec.change LeaderBoard type value:13 MissingMethod No signature of...
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0
The signature of the selectLeaderBoardPeriod it have a parameter. I tried to define explicitly the type as String but i am getting the same error.
Can someone spot what i am doing wrong?
Thank you in advance.
Best Regards
Your spec needs to be something like:
class LeaderboardSpec extends GebSpec {
def "change LeaderBoard type value"() {
when:
def page = to DashboardPage
and:
page.selectLeaderBoardPeriod("monthly")
then:
at DashboardPage
}
}
I am new to writing geb specs for funcational testing and appreciate your help with this issue. I am also new to Groovy, and the "Not Initialized" error might be to do with basic inexperience in that. I am trying to build a module to log me in automatically, at which point I can proceed with further tests, but I receive a geb.error.ModuleInstanceNotInitializedException. Previously, not using a module, I was able to log in to my page using only a spec. I was refactoring this to a module approach based on the answer on rcgeorge23's response here:
How to refactor common Geb test sequences.
The spec:
import spock.lang.Unroll
class FlowSpecs extends GebReportingSpec {
#Unroll
def "setup Spec"() {
given:
to QUserPage
when:
authModule.signIn("mwalle","password")
then:
assert { $("span", "class":"login-text z-label")[3].text() == "mwalle" }
}
the page:
package pages.app
import geb.Page
import AuthModule
class QUserPage extends Page {
static url = "/qsystem/quser"
static at = { title == "QSystem" }
static content = {
authModule { module AuthModule }
}
}
and the module:
import geb.Module
class AuthModule extends Module {
def idUser = js."zk.Widget.\$('\$usr').uuid" + "-real"
def idPass = js."zk.Widget.\$('\$pwd').uuid"
static content = {
loginUser { $("input", id:idUser) }
loginPass { $("input", id:idPass) }
loginButton { $("button",class:"login-button z-button")[0] }
}
void signIn(String username = "mwalle", String password = "password") {
loginUser.value(user)
loginPass.value(pass)
loginButton.click()
}
}
The full error is:
geb.error.ModuleInstanceNotInitializedException: Instance of module class AuthModule has not been initialized. Please pass it to Navigable.module() or Navigator.module() before using it.
at geb.Module.uninitializedException(Module.groovy:757)
at geb.Module.getJs(Module.groovy:96)
at AuthModule.<init>(AuthModule.groovy:5)
at geb.navigator.AbstractNavigator.module(AbstractNavigator.groovy:356)
at geb.content.NavigableSupport.module(NavigableSupport.groovy:207)
at geb.content.PageContentTemplateFactoryDelegate.module(PageContentTemplateFactoryDelegate.groovy:31)
at pages.app.QUserPage._clinit__closure2$_closure3(QUserPage.groovy:12)
at pages.app.QUserPage._clinit__closure2$_closure3(QUserPage.groovy)
at geb.content.PageContentTemplate.invokeFactory(PageContentTemplate.groovy:97)
at geb.content.PageContentTemplate.create_closure1(PageContentTemplate.groovy:59)
at geb.content.PageContentTemplate.create_closure1(PageContentTemplate.groovy)
at geb.content.PageContentTemplate.create(PageContentTemplate.groovy:82)
at geb.content.PageContentTemplate.get(PageContentTemplate.groovy:54)
at geb.content.DefaultPageContentSupport.getContent(DefaultPageContentSupport.groovy:42)
at geb.content.PageContentSupport.propertyMissing(PageContentSupport.groovy:39)
at geb.Page.propertyMissing(Page.groovy:112)
at geb.Browser.propertyMissing(Browser.groovy:216)
at geb.spock.GebSpec.propertyMissing(GebSpec.groovy:60)
at FlowSpecs.setup Spec(FlowSpecs.groovy:23)
The problem are these lines
def idUser = js."zk.Widget.\$('\$usr').uuid" + "-real"
def idPass = js."zk.Widget.\$('\$pwd').uuid"
js needs an initialized navigator, as you can see with at geb.Module.getJs(Module.groovy:96)
I would strongly advise to use another selector (css or otherwise) if you can instead of using javascript to lookup the id. If you cannot then move the lookup code into the content block.
static content = {
loginUser { $("input", id: js."zk.Widget.\$('\$usr').uuid" + "-real") }
loginPass { $("input", id: js."zk.Widget.\$('\$pwd').uuid") }
loginButton { $("button",class:"login-button z-button")[0] }
}
I work with tests on geb and I have problem. I need to save/print the address of the current page (function SaveUrl()).
Spock Test:
class TestSpec extends GebReportingSpec {
def "Google"() {
given: "go to google.com"
to GooglePage
when: "we at Google home page"
at GooglePage
then: "Search Yahoo"
Search("Yahoo")
SaveUrl()
}
}
GooglePage:
class GooglePage extends Page {
static url = "http://www.google.by"
static at = { $("title").text() == "Google"}
static content = {
theModule { module SearchModule }
}
def Search(String arg0) {
theModule.field.value(arg0)
theModule.search.click()
}
def SaveUrl() {
// need implement
}
}
Modile:
class SearchModule extends Module {
static content = {
field { $("input", name: "q") }
search { $("input", name: "btnK") }
}
}
Please help save/print current URL.
Thank You!
You can use the current url getter on WebDriver class. A WebDriver instance is stored as driver property on Browser. So in a Geb Spock test it is as simple as saying:
driver.currentUrl
EDIT
Since Geb 0.9.3 there is also a current url getter available on Browser.