My testing environment is Geb+Spock.
I have one test case scenario in which I perform some operation(e.g.creating a user) in one tab[URL1] of chrome browser and after performing that operation I want to verify it on the other tab[URL2].
Both tab's URLs are different.
I have tried below approach but it didn't work:
class checkUserSpec {
def 'create new users'(){
given:'URL of creating user page'
browser.at(createUserPage)
when:'create user'
createUser()
then:'User should be created'
withNewWindow({js.exec("window.open(URL2', 'opennewtab', '')")} ,page:verifyUserPage, wait: true) {
at verifyUserPage
verifyUserPage.with{
verifyCreatedUser()
}
}
}
}
class createUserPage {
static URL = 'URL1'
static at ={
$(title: "Create User Page")
}
def createUser(){
......
......
}
}
class verifyUserPage {
static URL = 'URL2'
static at ={
$(title: "Verify User Page")
}
def verifyCreatedUser(){
......
......
}
}
Related
I have the two domain clases:
class Persona {
String nombre
String apellidos
static hasMany = [pertenencias: Pertenencia]
static constraints = {
}
static mapping = {
pertenencias cascade: "all-delete-orphan"
}
}
class Pertenencia {
String nombre
static belongsTo = [persona:Persona]
static constraints = {
}
}
The service:
class MembresiaService {
#Transactional
def saveAll() {
def p = new Persona(nombre: 'carlos', apellidos: 'gm')
p.addToPertenencias(nombre: 'auto')
p.addToPertenencias(nombre: 'computadora')
p.addToPertenencias(nombre: 'casa')
p.save()
}
#Transactional
def deletePertenencias() {
def p = Persona.get(1)
p.pertenencias?.clear()
}
}
And the controller:
class TestController {
def membresiaService
def index() {}
def saveAll() {
membresiaService.saveAll()
redirect(action: "index")
}
def deletePertenencias() {
membresiaService.deletePertenencias()
redirect(action: "index")
}
}
When I execute saveAll() method from controller it saves the data in the database, when I execute deletePertenencias() from controller it deletes the "pertenecias" collection of Persona from the database (as expected).
I have installed the Grails console plugin , first time I execute the lines of saveAll() service method in the console, the result is the "persona" and its "pertenencias" in database. Then I execute the lines of deletePertenencias() service method in console but it doesn't delete the data of database and the "persona" object mantains the "pertenencias" (as if I had not run deletePertenencias() code).
Anyone kwnow why the code executed from console gives unexpected results?
I expect the result was the same from controller and console but the behaviour is different.
Suppose I have multiple Geb/Spock tests that beings with logging in. For example:
#Stepwise
Class AddNewPictureSpec extends GebSpec {
def "User at login page"() {
given: "User beings from login page"
to LoginPage
}
def "User gets redirected to Main page"() {
given: "User at Login page"
at LoginPage
when: "User signs in"
signIn "username", "pw"
to MainPage
then:
at MainPage
def "other test sequences follow...."() {
}
}
And another test spec with the exact same start sequence:
#Stepwise
Class EditPictureSpec extends GebSpec {
def "User at login page"() {
given: "User beings from login page"
to LoginPage
}
def "User gets redirected to Main page"() {
given: "User at Login page"
at LoginPage
when: "User signs in"
signIn "username", "pw"
to MainPage
then:
at MainPage
def "other test sequences follow...."() {
}
}
How do I refactor/extract out the common login "steps" so that I do not have duplicate code? Or am I writing my tests wrongly? Thanks.
I think the 'geb' way to do this is to use modules.
You can create a login module like this:
class LoginModule extends Module {
static content = {
loginForm {$("form")}
loginButton {$("input", value: "Sign in")}
}
void login(String username, String password = "Passw0rd!") {
loginForm.j_username = username
loginForm.j_password = password
loginButton.click()
}
}
Include it in your LoginPage:
class LoginPage extends Page {
static url = "login/auth"
static at = {title == "My Grails Application"}
static content = {
loginModule { module LoginModule }
}
}
Then in your test, you can reference your module's login method:
#Stepwise
class EditPictureSpec extends GebSpec {
def setupSpec() {
to LoginPage
loginModule.login(loginUsername)
}
def "some test"() {
...
}
}
You can create a login method and put it in a BaseSpec (that you would also create), which you would then extend in your tests. Eg:
class BaseSpec extends GebReportingSpec {
def login(name, pw) {
to LoginPage
// login code here...
}
}
Since you're using #StepWise, I'm assuming you're logging in once per spec, so use setupSpec() thusly...
Class AddNewPictureSpec extends BaseSpec {
def setupSpec() {
login("username", "password")
}
}
One possibility is to have one Spec for verifying the actual login behavior (e.g. LoginSpec) that is completely written out as it is now. For other Specs that need to login before doing the actual test you can abstract the entire login process behind a method in the LoginPage. Like you do now with singIn.
When you have a lot of Specs that need to login before they can really start testing the functionality they intend to test, doing the login steps through the browser again and again can take a lot of time.
An alternative can be to create a specific controller that is only loaded in the dev/test environments and that offers a login action.
So instead of going through all steps (go to page, enter name, enter password, ...) you can simply go to the URL /my-app/testLogin/auth?username=username.
Below an example how we do this in our Grails + Spring Security setup. We also bundle other utility methods in that controller that are used in the setup of multiple Specs and that would otherwise require several clicks in the browser, e.g. changing the interface language.
// Example TestLoginController when using the Spring Security plugin
class TestLoginController {
def auth = { String userName, String startPage = 'dashboard' ->
// Block the dev login functionality in production environments
// Can also be done with filter, ...
Environment.executeForCurrentEnvironment {
production {
render(status: HttpServletResponse.SC_NOT_FOUND)
return
}
}
def endUser = getYourEndUserDataByUsername()
if (endUser) {
// Logout existing user
new SecurityContextLogoutHandler().logout(request, null, null)
// Authenticate the user
UserDetails userDetails = new User(endUser)
def authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, userDetails.password, userDetails.authorities)
SecurityContextHolder.context.setAuthentication(authenticationToken)
// Bind the security context to the (new) session
session.SPRING_SECURITY_CONTEXT = SecurityContextHolder.context
redirect(action: "index", controller: startPage)
}
}
The correct solution is to create methods on a geb Page which encapsulate common functionality:
class LoginPage extends Page {
static url = "login/auth"
static at = {title == "Login"}
static content = {
username { $("#user") }
password { $("#password") }
}
def login(String email, String passwd) {
emailInput.value(email)
passwordInput.value(passwd)
passwordInput << Keys.ENTER
}
}
Then your test looks like this:
#Stepwise
class ThingSpec extends GebSpec {
def setupSpec() {
to LoginPage
page.login("user", "pass")
}
def "some test"() {
...
}
}
From an OOP perspective this is the best solution as the login procedure is only applicable to the login page. It doesn't make sense to use a module because no other page has a login box (unless it does, then modules make sense.)
It also doesn't make sense to use inheritance, you'll end up with an unorganized pile of methods and class names like "BaseSpec", bleh.
I having a GSP when it is accessed by two or more user at same time, It will throw
Row was updated or deleted by another transaction
Is there any way to share the render safely
Note: There is no update or save operation while rendering
Dude ,Have you tired on the action who updates ,list and read or connected with the row can be
#Transactional or do some concurrencyStuff exception handling
//Assume your domains are implemented just like or to be like this
class Author {
String name
Integer age
static hasMany = [books: Book]
}
//option one in your controllers or service class
Author.withTransaction { status ->
new Author(name: "Stephen King", age: 40).save()
status.setRollbackOnly()
}
Author.withTransaction { status ->
new Author(name: "Stephen King", age: 40).save()
}
//or
#Transactionl
def AuthorController () {
[list:list]
}
//or define a service like this
import org.springframework.transaction.annotation.Transactional
class BookService {
#Transactional(readOnly = true)
def listBooks() {
Book.list()
}
#Transactional
def updateBook() {
// …
}
def deleteBook() {
// …
}
}
for more visit : I hope this will be a bit useful ,Buddy!
I am trying to run the sample Collab-Todo application as in book Beginning Groovy and Grails,page no.123 (Author:Christopher M. Judd,Joseph Faisal Nusairat,and James Shingler Publication:Apress and Edition:2008). Here is my User.groovy file:
package collab.todo
class User {
String userName
String firstName
String lastName
static hasMany = [todos: Todo, categories: Category]
static constraints = {
userName(blank:false,unique:true)
firstName(blank:false)
lastName(blank:false)
}
String toString () {
"$lastName, $firstName"
}
}
The UserController.groovy is as Follows:
package collab.todo
class UserController {
def scaffold = User
def login = {
}
def handleLogin = {
def user = User.findByUserName(params.userName)
if (!user) {
flash.message = "User not found for userName: ${params.userName}"
redirect(action:'login')
}
session.user = user
redirect(controller:'todo')
}
def logout = {
if(session.user) {
session.user = null
redirect(action:'login')
}
}
}
I am able to create,read,update or delete the User table as usual.Here is a sample screenshot of my User view:
In the scaffolding view, I am trying to show the list of all the users in a drop-down(as per the book) using following snippet inside the user/login.gsp:
<g:select name='userName' from="${User?.list()}"
optionKey="userName" optionValue="userName"></g:select>
But what I am getting in the login page is a dropdown with no values populated:
Here is the screenshot of the login page:
In case I change
from="${User?.list()}"
to
from="${User.list()}"
I am getting a NullPointerException. So any clues what is going on?
It looks like the User class could not be found from your view.
Try one of the following:
Add the import statement to your view.
<%# page import="collab.todo.User" %>
Or use the fully qualified name within from attribute.
from="${collab.todo.User.list()}"
The best practice would be to pass the list of users from the controller:
def login = {
[users: User.list()]
}
And use the collection within your view
from="${users}"
I have integrated Spring security core plugin in my Grails application.
grails.plugins.springsecurity.successHandler.defaultTargetUrl = "/user/home"
This is what I have done to set default home page after successful login. But I would like to have different home page depending upon user roles
Currently I have 2 user roles
1)"ROLE_ADMIN"
2)"ROLE_USER"
How would I implement this?
One quick way would be to do the logic in the controller action. For example, the home action could render a different view based on role, e.g.:
import grails.plugin.springsecurity.annotation.Secured
class UserController {
def home() {
String view
if (SpringSecurityUtils.ifAllGranted('ROLE_ADMIN')) {
view = 'admin'
}
else if (SpringSecurityUtils.ifAllGranted('ROLE_USER')) {
view = 'user'
}
else {
// ???
}
render view: view, model: [...]
}
}
If you want to distribute the logic among different controllers, you could redirect based on role:
import org.codehaus.groovy.grails.plugins.springsecurity.SpringSecurityUtils
class UserController {
def home() {
if (SpringSecurityUtils.ifAllGranted('ROLE_ADMIN')) {
redirect controller: '...', action: '...'
return
}
if (SpringSecurityUtils.ifAllGranted('ROLE_USER')) {
redirect controller: '...', action: '...'
return
}
// ???
}
}
You can configure an authentication success handler too which will redirect users to specific controllers based on the roles.
class CustomAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
LinkGenerator linkGenerator
private static final ADMIN_ROLE = 'ROLE_Admin'
#Override
protected String determineTargetUrl(HttpServletRequest request, HttpServletResponse response) {
if(SpringSecurityUtils.ifAllGranted(ADMIN_ROLE)) {
return linkGenerator.link(controller: 'admin', action: "index")
}
return super.determineTargetUrl(request, response);
}
}
See Spring Security Core : Redirect users to different screen based on role