Grails web flow problem with browser's back button - grails

I'm having problems to get browser's back button work properly on web flow.
Version of grails is 1.1.2.
Imagine example code:
def someFlow = {
...
fillGroup {
on("addMember"){
...
}.to "fillMember"
}
fillMember {
on("addMember") {
...
}.to "fillMember"
on("goToCart").to "showCart"
}
showCart {
...
}
}
Now, I add group, several (>1) members and go to cart. Problem is that during filling the members the URL remains the same. URL execution parameter changes only if the state (view) changes.
So Firefox remembers fillMember pages as one page because the URL doesn't change. Therefore back button doesn't work properly. If I am on showCart and push back, I get to fillMember page. Further push of back button returns fillGroup. I need it to go through all the fillMember pages.
Is there any way to force Grails web flow to change the execution parameter even though I redirected to the same state? Or can I put my own parameter into the URL?
I found one pretty ugly way how to do that: use two fillMember states - fillMember1 and fillMember2, both doing the same thing, one redirects to another. But I need one more action state to be able to distinguish the actual state when hitting back and forward buttons. This construct works but I'd prefer easier way.
Thanks for any answers
Tom

So far, the only solution I've found is the one I mentioned. Use two view states, both doing exactly the same thing, and one action state to hold some state information (it would be difficult to properly distinguish processed member without it). The code would be something like this:
def someFlow = {
...
fillGroup {
on("addMember"){
...
}.to "fillMemberLogic"
}
fillMemberLogic {
action {
...
flow.stateinf += 1
if(flow.stateinf%2 == 1)
return gotoFillMember1()
else
return gotoFillMember2()
}
on("gotoFillMember1").to "fillMember1"
on("gotoFillMember2").to "fillMember2"
}
fillMember1 {
on("addMember") {
...
}.to "fillMemberLogic"
on("goToCart").to "showCart"
}
fillMember2 {
on("addMember") {
...
}.to "fillMemberLogic"
on("goToCart").to "showCart"
}
showCart {
...
}
}
Since the view is being changed for every member, the execution parameter is also being changed and URL is distinct for every member. Firefox distinguishes viewed pages according to URL, so you can go back and forth through all the members using back and forward buttons.
Web flow is mapping URL with current state of flow object. Therefore it's easily possible to distinguish the current member you are processing after several back button pushes.

Related

Playwright - not working with zoomed out site

our team decided to zoom out the whole site. So they did this:
This is breaking my PW tests while clicking on the button.
I get this in the inspector:
selector resolved to visible <button id="add-to-cart-btn" data-partid="04-0001" data-…>ADD TO CART</button>
attempting click action
waiting for element to be visible, enabled and stable
element is visible, enabled and stable
scrolling into view if needed
done scrolling
element is outside of the viewport
I found this is an issue in PW https://github.com/microsoft/playwright/issues/2768
My question is: how can I bypass this in the most efficient way?
Is there a way to override a playwright function that sets the initial loading of the page and set my zoom there?
Since if I do it via JavaScript, then I have to do it every time my page reloads, and that can be really tedious and error-prone in the tests.
This is what I have now, but it is really a hack a like solution:
async removeZoomOutClassFromBodyElement() {
await this.#page.evaluate(() => {
const body = document.querySelector('body');
if (body) {
// Removes the class only if it exists on the body tag
body.classList.remove('zoom-out');
} else {
throw Error(ErrorMessage.BODY_NOT_FOUND);
}
});
}
Can you please advise what would be the best approach here?
Thanks!

How to go from one view state to another view state in grails webflow plugin using glink tag?

I have the following webflow representation with 2 view state
def createFlow = {
startState {
on("addItem"){
//do something
}.to "startState"
on("deleteItem"){
//do something
}.to "startState"
}
secondState {
on("addItem"){
//do something
}.to "startState"
on("deleteItem"){
//do something
}.to "startState"
}
}
In each view state i want to have a glink tag to call each other, I was trying this solution with no success
<g:link action="create" event="startState">Start</g:link>
<g:link action="create" event="secondState">Second</g:link>
I get
No transition found on occurence of event 'startState' in state 'startState' of flow 'purchaseOrder/create' -- valid transitional criteria are array[addItem, deleteItem] -- likely programmer error, check the set of TransitionCriteria for this state
My question is, it is possible from a glink tag to call self view state or another view state?
Thanks for your time
It is possible to use g:link to trigger an event.
The structure of your webflow should be something like this:
def createFlow = {
startState {
on("goToSecond"){
//do something
}.to "secondState"
}
secondState {
on("goToStart"){
//do something
}.to "startState"
}
}
In create/startState.gsp
<g:link action="create" event="goToSecond">Second</g:link>
In create/secondState.gsp
<g:link action="create" event="goToStart">Start</g:link>
The key things are:
You are always in a state and each state can handle one or more events.
You signal the event which causes the current state's event handler to fire and, if you set up this way, to move to another state.
Each state has a view that you use to signal the events using g:submit or g:link.
In your case you were trying to signal the state directly when you really wanted to signal the event name. Also, it looks like you were trying to do it from a single view. It is possible to bind a single view to multiple states (using a view override or using a template), but it doesn't really make much sense to do so.

How to handle user refresh of page

this sounds quite basic...
I have views that are built in controllers with some input argument (from previous page) and more stuff like getting data from TempData[] and more.
public ActionResult Submit(SubmitDetails submitDetails)
{
var x = (long)TempData["SomeMoreValue"];
// do stuff
return View("NextView", someInputModelThatIHaveBuilt);
}
Everything is working, but when I hit refresh on Next view, got Resource not found...
My Question - what is the practice to keep it up with the info you need in "NextView" to survive user refresh...
Have you tried using:
TempData.Keep();

In Geb, what is the difference between displayed and present?

I am writing functional tests and dealing with a modal window that fades in and out.
What is the difference between displayed and present?
For example I have:
settingsModule.container.displayed and settingsModule.container.present
where settingsModule represents my modal window.
When testing my modal window (the modal from Twitter's bootstrap), I usually do this:
def "should do ... "() {
setup:
topMenu.openSettingsModal()
expect:
settingsModule.timeZone.value() == "Asia/Hong_Kong"
cleanup:
settingsModule.closeSettingsModal()
}
def "should save the time zone"() {
setup:
topMenu.openSettingsModal()
settingsModule.timeZone = "Japan"
when:
settingsModule.saveSettings()
then:
settingsModule.alertSuccess.size() == 1
settingsModule.alertSuccess.text() == "Settings updated"
when:
settingsModule.saveSettings()
then:
settingsModule.alertSuccess.size() == 1
cleanup:
settingsModule.closeSettingsModal()
}
and on and on. In my modules, I have:
void openSettingsModal() {
username.click()
settingsLink.click()
}
void closeSettingsModal() {
form.cancel().click()
}
I always get a complain: "Element must be displayed to click".
In my openSettingsModal and closeSettingsModal, i tried many combination of waitFor with time interval and using present or not ... Can't figure it out.
Any pointers would be highly appreciated. Thanks!
I think the main difference is that present would check that there is an element in your DOM, whereas displayed checks on the visibility of this element.
Remember that webdriver simulates the actual experience of an user using and clicking the website using a mouse, so if the element is not visible to them, they will not be able to click on it.
I wonder if your issue has to do with settingsLink not being in the DOM when the page is first loaded. If you are waiting for a dialog to popup and a link to live in this dialog, then you probably want to set something like
content{
settingsLink( required: false ) { $( '...' }
settingsModal( required: false ) { $( '#modalDialog' ) }
}
your waitfor should look something like
username.click()
waitFor{ settingsModal.displayed }
settingsLink.click()
I would stick with the book of geb conventions and just use displayed all the time.
Geb Manual - Determining visibility
Thanks for your reply. I was actually able to resolve my issue.
The problem was that the modal window had an animation of 500ms. Opening and closing the window several times in my tests made them succeed/fail inconsistently.
What I ended up doing is hooking the the "shown" event provided by the plugin. I ended up adding a "shown" class to the modal and check for it every 100ms during 1s.
void openSettingsModal() {
username.click()
settingsLink.click()
waitFor (1, 0.1) { $("#settingsModal", class: "shown").size() == 1 }
}
void closeSettingsModal() {
form.cancel().click()
waitFor (1, 0.1) { $("#settingsModal", class: "shown").size() == 0 }
}
As a side note, the tests were failing in Chrome and Firefox BUT were passing in IE!! I am guessing that because IE 8 doesn't support animations that my tests were passing.
It is all good now.
I hope it will help someone someday!
Where we can use displayed?
If a particular element you are removing or deleting, if it is still there in DOM and not displayed in page, you can use assert thatelement.displayed == false which will make sure that element is not displayed in the page (but still it is present in DOM)
Where we can use present?
In the same example as above, after removing,if the element is not found at the DOM ,you should use present for verification
assert thatelement.present == false
Hope you understand....
Adding to the above, present takes more time in script execution

Editing a BrowserField's History

I have a BrowserField in my app, which works great. It intercept NavigationRequests to links on my website which go to external sites, and brings up a new windows to display those in the regular Browser, which also works great.
The problem I have is that if a user clicks a link to say "www.google.com", my app opens that up in a new browser, but also logs it into the BrowserHistory. So if they click back, away from google, they arrive back at my app, but then if they hit back again, the BrowserHistory would land them on the same page they were on (Because going back from Google doesn't move back in the history) I've tried to find a way to edit the BrowserField's BrowserHistory, but this doesn't seem possible. Short of creating my own class for logging the browsing history, is there anything I can do?
If I didn't do a good job explaining the problem, don't hesitate for clarification.
Thanks
One possible solution to this problem would be to keep track of the last inner URL visited before the current NavigationRequest URL. You could then check to see whether the link clicked is an outside link, as you already do, and if it is call this method:
updateHistory(String url, boolean isRedirect)
with the last URL before the outside link. Using your example this should overwrite "www.google.com" with the last inner URL before the outside link was clicked.
Here is some half pseudocode/half Java to illustrate my solution:
BrowserFieldHistory history = browserField.getHistory():
String lastInnerURL = "";
if navigationRequest is an outside link {
history.updateHistory(lastInnerURL, true);
// Handle loading of outer website
} else {
lastInnerURL = navigationRequest;
// Visit inner navigation request as normal
}
http://www.blackberry.com/developers/docs/5.0.0api/net/rim/device/api/browser/field2/BrowserFieldHistory.html#updateHistory(java.lang.String, boolean)
I had a similar but a little bit different issue. Special links in html content like device:smth are used to open barcode scanner, logout etc and I wanted them not to be saved in BrowserFieldHistory. I found in WebWork source code interesting workaround for that. All that you need is throw exception at the end like below:
public void handleNavigationRequest( BrowserFieldRequest request ) throws Exception {
if scheme equals to device {
// perform logout, open barcode scanner, etc
throw new Exception(); // this exception prevent saving history
} else {
// standard behavior
}
}

Resources