The configuration of the environment is as follows.
java 1.8
htmlunit 2.43
_webClient = new WebClient(BrowserVersion.FIREFOX);
// ajax, javaScript controller
_webClient.setAjaxController(new NicelyResynchronizingAjaxController());
// set options
_webClient.getOptions().setJavaScriptEnabled(true);
_webClient.getOptions().setCssEnabled(false);
_webClient.getOptions().setUseInsecureSSL(true);
_webClient.getOptions().setSSLClientProtocols( _webClient.getOptions().getSSLClientProtocols() );
//_webClient.getOptions().setSSLClientProtocols(new String[] {"TLSv1.2", "TLSv1.1", "TLSv1" });
_webClient.getOptions().setRedirectEnabled(true);
_webClient.getOptions().setPopupBlockerEnabled(false);
_webClient.getOptions().setThrowExceptionOnScriptError(false);
_webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
_webClient.getOptions().setPrintContentOnFailingStatusCode(false);
_webClient.waitForBackgroundJavaScript(10000);
// set timeouts
_webClient.getOptions().setTimeout( timeout *1000 );
_webClient.setJavaScriptTimeout( timeout *1000 );
_webClient.waitForBackgroundJavaScript( timeout *500 );
//_webClient.getJavaScriptEngine().setJavaScriptTimeout( timeout *1000 );
_webClient.waitForBackgroundJavaScriptStartingBefore(timeout *1000);
_webClient.getCookieManager().setCookiesEnabled(true);
The above is my environment.
Below is the HTML Element I want to click on.
<div class="btnConfirmWrap">
<button data-v-ffa8b1e8=""
class="hasBgColor bgColorRed roundType sizeMedium alignLeft">
<span data-v-ffa8b1e8="" class="inner"> <!----> <span
data-v-ffa8b1e8="" class=""> Login </span>
<!----></span>
</button>
</div>
This is the code for clicking on the html element mentioned above.
The content of the XPath is the fullXpath of the web page obtained by accessing Chrome.
I checked the XPath path myself. XPath wasn't wrong.
hElement = getByXPathFirstElement( _htmlPage,"/html/body/div[1]/main/div/div[1]/div/div[4]/button");
Page page = hElement.click();
_htmlPage = (HtmlPage) page;
System.out.println(_htmlPage.asText());
I checked that _htmlPage.asText() had ID and PW values before clicking the login button.
However, if you click the button, the login button function is not executed and the ID and PW values are lost. ID and password are correct values.
Can't log in, help me
It works very well if you directly access the site and log in. But htmlunit doesn't work. What's wrong with my code?
Without a way to reproduce i can only provide some hints:
As always check the logs, mabey there is a hint....
These two functions are not options
_webClient.waitForBackgroundJavaScript( timeout *500 );
_webClient.waitForBackgroundJavaScriptStartingBefore(timeout *1000);
You have to call them after you have started an action (e.g. after the click)
Page page = hElement.click();
_htmlPage = (HtmlPage) page;
Your code gets thy sync resulting page form the click. But maybe you click forces some js actions and maybe the action is async and maybe the action forces a page replace. You can try to do something like this:
force the click
wait for background js
get the current window content by asking the page for the current window and then asking the window for the enclosed page
Maybe this helps.
If not try to build a reproducible sample and open a issue at github.
Related
Sorry if this is a dumb question, its my first time doing web dev. Also, first time asking a question here. I am trying to get my read more / read less button to work. It worked once and upon refreshing the page it stopped working. I am running a script in a separate js file to toggle my button. And i run the script using the script tag directly in the page. Any help will be greatly appreciated. Also, this is a react App. Upon clicking the button the user should be able to see the text within the span tag.
Code in main.js
document.addEventListener('DOMContentLoaded', () => {
const readMoreBtn = document.querySelector('.read-more-btn');
const text = document.querySelector('.text');
readMoreBtn?.addEventListener('click', (e)=>{
text.classList.toggle('show-more');
....
})
})
code in onlineClasses.js
<div className='class-container'>
<div className='class-list'>
<h1>VIRTUAL CLASSES</h1>
<div className="grid-class-container">
<div className="grid-item" >
<h4>SUNNAH PRAYERS - <i>CLASS TITLE</i></h4>
<p className="text" id="txt"> Class info.
<span className="more-text">More info about classes.</span></p>
<button className="read-more-btn" id="btn">READ MORE</button>
<Helmet><script src = './main.js'type="text/jsx"></script </Helmet></div>
I tried putting the script in different parts of my project. I tried using quearySelector() and getElementById(), i know react uses states and effect, im not sure if i would have to use that. I had some errors in my console, but i resolved those.
Request:
Either with Cypress (preferably) or using extending Cypress with Puppeteer via cy.task(), being able to perform a test on a system-generated popup menu?
This popup menu element cannot be inspected by Chrome Devtools and is thereby not navigatable by either CSS selectors or XPath.
Details: I am using Cypress for UX testing and extend in limited cases with custom cy.task() with Puppeteer.
The web page under test has a dropdown selection that generates a popup menu, source is this npm module:
npm react-select-async-paginate
Under inspections, the popup menu is generated from an and it has these two attributes:
aria-haspopup="true"
role="combobox"
Appreciate assistance, thank you
22 Feb, Updated
This is the generated react-select-async-paginate:
<div>
<div class="***-singleValue">Offer Letter w/ Signature</div>
<div class=" css-ackcql" data-value="">
<input
id="react-select-2-input"
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
aria-controls="react-select-2-listbox"
aria-owns="react-select-2-listbox"
role="combobox">
</div>
</div>
This is the attempt for selecting an option, but it does not loads selected option into <div class="***-singleValue">.
This Cypress script selects an option:
cy.get(`${selectorReactSelectPaginate}`)
.find('input[role="combobox"]')
.focus()
.type($valueSelect, { force: true })
.then(() => {
cy.wrap(true);
});
As would able to validated using this Cypress script:
cy.get(selectorReactSelectPaginate)
.find('div[class*="-singleValue"]')
.contains($valueSelectContains)
.then(() => {
cy.wrap(true);
});
Yes, the option list is tricky to get at in devtools because of the blur event (which comes from the library itself). One approach is open devtools and watch carefully as you click open the select.
Using react-select-async-paginate - Simple Example, when I click open the select I can see this div appearing and disappearing
<div id="react-select-2-listbox">
So I can use Cypress to get a look inside that
cy.get('[role="combobox"]').click()
cy.get('#react-select-2-listbox')
.then($listbox => {
console.log($listbox)
})
Now in devtools console, check out children property (has one child), then the child's children - these look like the options to be tested.
The common selector is an id starting with react-select-2-option, so I can test like so
cy.get('[role="combobox"]').click()
cy.get('#react-select-2-listbox')
.find('[id^="react-select-2-option"]') // all div's with id starting react-select-2-option
.should('have.length', 10)
.eq(3) // check out the 4th option
.invoke('text')
.should('eq', 'Option 4') // passes
Selecting an option by typing into the box
Two things affect this method of selecting
the dropdown list changes (reloads) as you type characters
the element 'div[class*="-singleValue"]' only appears after you confirm the typed value (either enter key or blur(), I'm not sure which).
This worked for me
// Type in the option
cy.get(selectorReactSelectPaginate)
.find('[role="combobox"]')
.type(valueToSelect)
// Wait for the listbox to respond
cy.get('#react-select-2-listbox')
.should('contain', valueToSelect)
// Blur or enter will set the value
cy.get(selectorReactSelectPaginate)
.find('[role="combobox"]')
.type('{enter}')
.blur()
// Check the value
cy.get(selectorReactSelectPaginate)
.find('div[class*="-singleValue"]')
.should('contain', valueToSelect)
A neat trick with test runner is to add a .wait() after the dropdown selection appears. A snapshot of the DOM will be created and you can click on the .wait() to view the snapshot and use dev tools to inspect the dropdown selection.
I'm designing a particular page where wherever I click I want to go back to the homepage.
All of the page in enclosed in a section:
<section id="test-page-1" ui-sref="project.home">
</section>
The problem is that I have 3 particular buttons in this page and are not working as they should, instead they are also redirecting me to the Home page.
Z-index didn't solve the problem as from what I read it only works on a visual perspective rather than functionality. I'd really like it if I can still use the ui-sref="project.home" in the whole section as it is. Any ideas please ?
In the functions associated with you button clicks, stop the event propagation.
$scope.buttonFunctioanlity = function (e) {
e.stopPropagation();
};
<button ng-click="buttonFunctioanlity($event)">Click Me</button>
You know what ui-sref is right?
Changing your application state and redirecting to different url (Home in your case)
not really understood your problem, but remember you can add ng-click together with ui-sref to do some function before redirecting (might help your logic)
like
<section id="test-page-1"
ng-click="doSomething(someParams)"
ui-sref="project.home"></section>
and controller
$scope.doSomething = function(someParams) {
// bla-bla-blaaa
}
Let's say I'm on /page?id=1
Then I navigate to /page?id=2
And I make a change on that page, which implements a post and then redirects back to /page?id=2
In Firefox, I can hit the back button once and return to /page?id=1, but in Chrome and Safari on iPhone, I have to hit the back button twice because /page?id=2 is in the browser history twice. (And if I made multiple posts from id=2, I'd have to hit the back button that many times to finally return to id=1.)
In some ways, this seems like normal browser behavior, as each GET is simply pushed into the history, but since the URL is identical to the previous entry, this results in a poor user experience, which typically seems to be avoided by other web applications... and is naturally avoided in Firefox. Is this an unavoidable bug in Webkit browsers, or can I implement the PRG in a different way to avoid this?
btw- the behavior appears to be the same redirecting with 302 or 303.
UPDATE: I've mocked up some sample code... don't know if there's a platform like jsfiddle where I could upload this for you to see in action:
form.php:
id=<?=$_REQUEST['id']?>
<form action="submit.php" method="post">
<input type="hidden" name="id" value="<?=$_REQUEST['id']?>">
<input type="submit" value="submit">
</form>
submit.php:
<?php
header("Location: form.php?id=" . $_REQUEST['id']);
die($_REQUEST['id']);
?>
If I start on form.php?id=4 (just to put it in the browser history) and then go to form.php?id=5 and then hit submit (as though to execute a database change), in Firefox I get one entry in the history for each; in Chrome I get one entry for id=4 and then two entries for id=5. Why the difference in behavior? I think Firefox's behavior is better, since hitting back twice to get away from id=5 is counter-intuitive to the user.
Though it's not an explanation on what happens, I have a way around it that I use in all of my applications. First some code:
form.php would look like this:
id=<?=$_REQUEST['id']?>
<form action="submit.php" target="iframe" method="post">
<input type="hidden" name="id" value="<?=$_REQUEST['id']?>">
<input type="submit" value="submit">
</form>
submit.php like this:
<?php
header("Location: form.php?id=" . $_REQUEST['id']);
die($_REQUEST['id']);
?
<script>
window.parent.location.reload();
</script>
It's your documents with some extra stuff in the <form> tag and a brand new <script> tag.
I would also have an iframe somewhere in the document like this:
<iframe name="iframe"></iframe>
So to explain. Instead of navigating to a new site and back again every time you need to make a change, you simply load the submit.php in an iframe in your existing document. Hence the target="iframe" part.
Then when the iframe has loaded, meaning the changes have been made, you reload the original page to reflect these changes, hence the window.parent.location.reload(); part. Since the page is just reloading, it won't put a second entry in your history.
I hope this helped you :)
We also experienced the same problem and even after researching for days I found no "easy" solution. The closest thing I found was this Webkit Bugzilla ticket, which by the looks of it doesn't seem to be very high priority. As you mentioned, IE and Firefox behave just fine.
Since we have our own back button in the application, we were able to solve the problem by using the session storage and checking it when the page is loaded. The TypeScript code is the following:
class DoubleHistoryWorkaround {
// key of the attribute we store the URL we where on when clicking on the back button
private static comingFromLabel = "comingFromURL";
// key of the attribute of the flag denoting whether we set a valid comingFromURL
private static comingFromFlag = "comingFromSet";
constructor() {
this.checkLocation();
}
/**
* Checks the URL we saved in the session and goes a further step back
* in the history if the first back button brought us to the same page again.
*/
private checkLocation() {
let doubleEntry : boolean;
// have we set a comingFromURL?
let comingFromSet = window.sessionStorage.getItem(DoubleHistoryWorkaround.comingFromFlag);
if (comingFromSet) {
// is the set comingFromURL the same as our current page?
let currentURL = window.location.href;
let comingFromURL = window.sessionStorage.getItem(DoubleHistoryWorkaround.comingFromLabel);
if (currentURL === comingFromURL) {
// double history entry detected
doubleEntry = true;
// before we skip we save our location ourselves, since we might still navigate
// to the same page again (in case of triple identical history entries)
DoubleHistoryWorkaround.saveLocation();
// skip this page
history.back();
}
}
// reset the location entry unless we just set it ourselves
if (!doubleEntry) {
this.resetLocation();
}
}
/**
* Saves the current location in the session storage.
*/
public static saveLocation() {
window.sessionStorage.setItem(DoubleHistoryWorkaround.comingFromFlag, "true");
window.sessionStorage.setItem(DoubleHistoryWorkaround.comingFromLabel, window.location.href);
}
/**
* Removes the set location from the session storage.
*/
private resetLocation() {
window.sessionStorage.setItem(DoubleHistoryWorkaround.comingFromFlag, "false");
window.sessionStorage.setItem(DoubleHistoryWorkaround.comingFromLabel, "");
}
}
We call DoubleHistoryWorkaround.saveLocation() when we click our application's back button, setting the session entries that are checked by checkLocation().
I am getting a problem to reflect the value in the view, I don't want to load the complete page because its very costly to load the page,
I have two controllers(controller1 and controller2), one service(service1) and two views(modalwindow.html and product.html).
The scenario is:
1.User is on product.html(contains multiple accordions) and user explicitly close all the accordions.
2.User clicked on icon which opened modal window, since it's opened the modal window it's not going to change the URL on the address bar.
3.Modal window(Modalwindow.html ) has the link of show product, since the product page is the active page(show product is the accordion which closed by user explicitly) on the browser.
on the click of link, appropriate accordion should be open on the product.html
I am communicating between modal window controller (controller2.js) and product page(controller1.js) through service (service.js), I am calling controller2
how to fix this issue without loading a complete page
Assuming the modal closes when a product is selected, it can return the value to the calling controller. Then it opens the specified accordion.
I fiddled around in your fiddle. You are mixing two ways of showing your categories in the fiddle: an accordion value, and two boolean values (categoryAccordion, productAccordion). I moved to using one way and it seems to work with the eventCallback. Also, you checked wrongly for your 'args' in the eventCallback. You're passing it back as an array, so get the value out of the array first.
Also, you checked wrongly for your 'args' in the eventCallback.`
if (args[0] == 'Product') {
$scope.productAccordion = true;
$scope.categoryAccordion = false;
} else {
$scope.productAccordion = false;
$scope.categoryAccordion = true;
}
See fiddle.
Should it not be working in your real code, it might have something to do with the following SO question.