How to access external website using Geb - grails

I am automating my project using Geb and Groovy. For Example: I am logging into Gmail and I define different Pages as - Inbox, Sent Item, Trash, Drafts, etc. Now in my spec I wanted to access an external website like yahoomail. How can I define it the spec to access an external Webiste.
I use "go" to navigate to yahoo mail as below in my spec
then: "I go to Yahoo mail page"
go "https://login.yahoo.com/"
and: "Signing into Yahoo mail "
at YahooLoginPage
In the YahooLoginPage.groovy it is not finding the Next button which I define as static at
static at = { $("#login-signin") }
Error message I get :
Condition not satisfied:
go "https://login.yahoo.com/"
|
null
Is there any other way to do this?

The problem is that you are using a method which has a void return type in a then: Spock block. Every statement is asserted in then: blocks and that method call evaluates to null because of its return type and hence the failure you're getting.
Basically you should not use Geb's go() method in a then: - use it in in a given: or when: block instead.

I think Erdi's and Jeff's answers above nail it, but since I was curious to find out if it was possible and for an example of how working code might look I went ahead and built a freestanding groovy script which runs a geb spec. The below script enters a username in the yahoo login flow and hits the next button.
#Grapes([
#Grab("org.gebish:geb-spock:2.1"),
#Grab("org.spockframework:spock-core:1.1-groovy-2.4"),
#Grab("org.seleniumhq.selenium:selenium-htmlunit-driver:2.52.0"),
#GrabExclude('org.codehaus.groovy:groovy-all')
])
import geb.*
import geb.spock.*
import spock.util.EmbeddedSpecRunner
import java.util.logging.*
import org.w3c.css.sac.ErrorHandler
import com.gargoylesoftware.htmlunit.SilentCssErrorHandler
new EmbeddedSpecRunner().runClass(YahooSpec)
class YahooSpec extends GebReportingSpec {
def setup() {
// disable css errors output - don't do this for real tests
browser.driver.webClient.cssErrorHandler = new SilentCssErrorHandler()
}
def "should be able to enter username at yahoo"() {
when: "I go to Yahoo mail page"
to YahooLoginPage
then: "there should be a button with value 'Next'"
nextButton.value() == "Next"
when: "I enter a username and click next"
username = "BobbaFett"
nextButton.click()
then: "I should end up at the password page"
at YahooPasswordPage
greeting.text() == "Hello BobbaFett"
}
}
class YahooPasswordPage extends Page {
static url = "https://login.yahoo.com/account/challenge/password"
static at = { title.trim() == "Yahoo" }
static content = {
greeting { $("h1", class: "username")}
}
}
class YahooLoginPage extends Page {
static url = "https://login.yahoo.com/"
static at = { title == "Yahoo - login" }
static content = {
username { $("input#login-username")}
nextButton(to: YahooPasswordPage) { $("input#login-signin") }
}
}
saving the above in a file test.groovy and running:
~> groovy test.groovy
executes the spec. It should be noted that the first run will take some time as the script is downloading dependencies. It should also be noted that using a username which does not exist will break the test as the test assumes that yahoo will send you to the password page after hitting next.
Tested on:
Groovy Version: 2.4.15 JVM: 1.8.0_161 Vendor: Oracle Corporation OS: Mac OS X

Related

Grails Multiple data source: org.springframework.beans.factory.NoUniqueBeanDefinitionException

I recently posted a question about multiple data sources. Things were going well until I hit this issue:
Controller
def doSomething() {
def user=userService.getCurrentUser()
}
Service
class UserService {
def getCurrentUser() {
def principal = springSecurityService.principal
String username = principal.username
return find(username)
}
def find(String user) {
return User.find{username==user}
}
}
This had been working previously on single DataSource but now with both enabled I see this on the browser:
Error 500: Internal Server Error URI /xxx/xxx Class
org.springframework.beans.factory.NoUniqueBeanDefinitionException
Message No qualifying bean of type
[org.springframework.transaction.PlatformTransactionManager] is
defined: expected single matching bean but found 3:
transactionManager,transactionManager_countrycity,$primaryTransactionManager
Okay this is now resolved.
I think I found the issue: under grails 3 with multiple data sources if you have this import :
import org.springframework.transaction.annotation.Transactional
You will run into the above problems:
If you how ever have :
import grails.transaction.Transactional
things will work as expected. I hadn;t paid attention and let ide choose wrong declaration

Setting username in application insights

I am new to application insights and have set it up using no custom events and I'm using all the defaults. The application is build on MVC 5. In the ApplicationInsights.config there's a comment saying:
"When implementing custom user tracking in your application, remove this telemetry initializer to ensure that the number of users is accurately reported to Application Insights."
We have a page where you are required to login so the default user logging isn't saying that much and we would much rather have the username being the unique identifier. Based on the comment it seems like this should be some kind of common modification and therefor easy to modify. When trying to google on "custom user tracking" I do not find anything interesting which seems a bit odd...
So how do I link the user in Application Insights to my username instead of going on some cookie which seems to be the default behaviour?
To link the user to your custom username, you can create the following telemetry initializer:
public class RealUserIDTelemetryInitializer:ITelemetryInitializer
{
public void Initialize(Microsoft.ApplicationInsights.Channel.ITelemetry telemetry)
{
// Replace this with your custom logic
if (DateTime.Now.Ticks % 2 > 0)
{
telemetry.Context.User.Id = "Ron Weasley";
}
else
{
telemetry.Context.User.Id = "Hermione Granger";
}
}
}
Then register this telemetry initializer in AI.config.
<TelemetryInitializers>
....
<Add Type="MyApp.RealUserIDTelemetryInitializer, MyApp" />
</TelemetryInitializers>

Grails spring security save login time in user domain

Using this link if we register a call back in grails, how to access springSecurityService in plain groovy/java class, so that we can get the current user domain class and save the login time?
Update:
I have done this using the below:
appCtx.springSecurityService.currentUser.id
If you are using the callback closures you can get the information from the AuthenticationSuccessEvent.
grails.plugin.springsecurity.onAuthenticationSuccessEvent = { e, appCtx ->
// handle AuthenticationSuccessEvent
println "User id ${e.authentication.principal.id} was authenticated"
}

How to setup and teardown functional test data in Geb grails

I have many working/passing functional geb/spock tests (each extending GebReportingSpec) that are testing a web application with test data all created from the BootStrap.groovy at the beginning of the functional test suite.
I want to move the test data creation into startup() / teardown() methods within each Spec, well actually I wanted to have them inherit it from a base class but apparently the StepWise has issues with inheritance.
So, at present each of my test spec classes look something like:
#Stepwise
class ExampleSpec extends GebReportingSpec {
def "valid root user logs in"() {
given: "I am at the login page"
to LoginPage
when: "I enter root's credentials"
username = "root"
password = "password"
and: "I click the login button"
loginButton.click()
then: "I am logged in and directed to the welcome page"
at WelcomePage
}
}
Now, my problem is that I can't seem to create a new test (above the first test) that can create test data. Without having a valid given/when/then statement the test doesnt appear to be executed and calling a method from within the existing test also doesnt appear to work. I have looked into the grails-remote-control plugin to help me and I believe this will allow me to successfully envoke closures to setup data but I am not sure on the best mechanism for calling this from within the GebReportSpecs (or some abstract parent).
Below is a brief outline of the kind of thing I want to be able to do, either by making 'setupData()' the first test or by calling that method from within a test... Neither appears to work.
def remote = new RemoteControl()
def setupData() {
def id = remote {
def ShiroUser user = new ShiroUser(username: "root", ...)
user.save()
user.id
}
println(id)
}
.... Tests then follow
Are there any annotations like #before etc that can force these methods to be invokved?
Any suggestions are appreciated.
Solution:
I have accepted dmahapatro's response below at the correct answer, but have also provided an example of my final solution below for those who may find it useful.
(Untested)
GebReportingSpec extends GebSpec which ultimately extends spock.lang.Specification which has Fixture Methods.
You can use them like:
#Stepwise
class ExampleSpec extends GebReportingSpec {
def setupSpec(){
super.setupSpec()
//setup your data
}
def cleanupSpec(){
super.cleanupSpec()
//I do not think you would need anything else here
}
def "This is test 1"(){
}
def "This is test 2"(){
}
}
You cannot use setup as one of your test method because the sate is not maintained for a single test case. It goes like this:-
setup called -> test1 -> teardown called
setup called -> test2 -> teardown called
setup called -> test3 -> teardown called
.........
## Solved ##
Thanks to dmahapatro (and erdi). I specifically glossed over setupSpec() and cleanup() as they are private in GebReportingSpec.
Just for completion sake I am going to post a simplified version of my final solution using the grails remote control plugin just in-case it helps anyone else. The only thing to note is that the setup/teardown appears to be called once per Spec, not before each test. Which for me is actually preferably as my test data is quite complex and takes time to be created. So you have a set of test data from the Spec which is modified through the tests in the Spec and then finally cleared down before your next Spec is executed.
#Stepwise
class TestDataBaseSpec extends GebReportingSpec {
protected void createTestUsers() {
def remote = new RemoteControl()
def created = remote {
def createUser = { name, roles, pwHash ->
def user = new ShiroUser(username: name, passwordHash: pwHash, passwordSetDate: new Date())
roles.each { user.addToRoles(it) }
user.save(failOnError: true)
return user
}
createUser("root", [ShiroRole.findByName("base_user")], pwHash)
// .. repeat for more
}
}
protected void deleteTestUsers() {
def remote = new RemoteControl()
def created = remote {
ShiroUser.findAll().each {
it.delete(flush: true)
}
return true
}
}
}
#Stepwise
class ExampleSpec extends TestDataBaseSpec {
def setupSpec() {
super.createTestUsers()
}
def cleanupSpec() {
super.deleteTestUsers()
}
def "valid root user logs in"() {
given: "I am at the login page"
to LoginPage
when: "I enter root's credentials"
username = "root"
password = "password"
and: "I click the login button"
loginButton.click()
then: "I am logged in and directed to the welcome page"
at WelcomePage
}
}

grails 2.1.1 command object service injection for custom validator

Grails 2.1.1
I can't seem to get a command object to be injected with a service so that I can use custom validator. I've tried several things, including
Grails command object data binding and
what the 2.1.1 docs on custom validator suggest, I just can't figure this one out..
Relevant Code:
class RegistrationCommand {
String username
def registrationService
static constraints = {
username validator: { val, obj ->
obj.registrationService.isUsernameUnique(val) }
}
}
class RegistrationService {
def isUsernameUnique(username){
def user = new User(username:username)
user.validate()
if(user.errors.hasFieldErrors("username")){
return false
}else{
return true
}
}
Resolved.. Issue was due to plugin.
I'm using a plugin for client side jquery validation (jquery-validation-ui-1.4.2). The command object being created by the plugin's controller wasn't getting injected with the service. The issue was reported https://github.com/limcheekin/jquery-validation-ui/issues/17 . The fix does work but has not been pushed upstream yet.

Resources