Cypress doesn't find input field (maybe because inside a form?) - input

Page source (only iFrame part which contains to form i need to fill)
<iframe title="Form 0" id="hs-form-iframe-0" >
#document
<html>
<body>
<form id="hsForm_405e4c3f-98da-4eb1-bd27-c1886a1f811e">
<div>
<label placeholder="Enter your Vorname">Vorname</span>
<div class="input">
<input name="firstname">
</input>
</div>
</div>
</form>
</body>
</html>
</iframe>
Code i tried:
cy.get('#hs-form-iframe-0').its('0.contentDocument').should('exist')
cy.get('input[name="firstname"]').type( 'Smith') //failes as never found. Is the iFrame the cause of it? Of the form?

TLDR The correct way would be to use .find() on the iframe contentWindow.
cy.get('#hs-form-iframe-0').its('0.contentWindow').should('exist')
.its('body').should('not.be.undefined')
.find('input[name="firstname"]').type( 'Smith')
Example from Working with iframes in Cypress
const getIframeWindow = () => {
return cy.get('iframe[data-cy="the-frame"]')
.its('0.contentWindow').should('exist')
.its('body').should('not.be.undefined')
}
cy.getIframeBody().find('#run-button').should('have.text', 'Try it').click()
There are other potential problems, such as delayed loading of the iframe source. The .should('exist') check on the iframe window does not cover all situations, nor does performing visibility checks on the input.
The cypress-iframe package has a lot more checks built in, so it's a safer way to handle iframes.

You have found the iframe and access its contents but then you search for the input at the root of your DOM instead of the iframe. You can continue the chain of commands by removing the second cy.
cy.get('#hs-form-iframe-0')
.its('0.contentDocument')
.should('exist')
.get('input[name="firstname"]')
.should('be.visible') // always good to check before action
.type( 'Smith')

Related

Handle connection errors in htmxjs

With the power and elegance of HTMXJS and its companion _hyperscriptJS is a matter of few lines to write a code that selects, uploads (POST) multiple files shows a progress bar and then display them:
<form hx-encoding="multipart/form-data"
_="on htmx:xhr:progress(loaded, total) set #bar.value to (loaded/total)*100">
<input type="file" name="fileToUpload[]" multiple
hx-post="upload.php"
hx-target="#image-src"
hx-swap="innerHTML">
<button type="button">Select</button>
<progress id="bar" value="0" max="100"></progress>
</form>
<div id="image-src"></div>
and upload.php:
$countfiles = count($_FILES['fileToUpload']['name']);
for($i=0;$i<$countfiles;$i++){
$filename = $_FILES['fileToUpload']['name'][$i];
move_uploaded_file($_FILES['fileToUpload']['tmp_name'][$i], $filename);
echo '
<div>
<img src="'.$filename.'">
</div>
';
}
but now I would like to add network error handling. I know that HTMX fires
htmx:sendError, but I don't understand how to add it into my code above so that if there's a network error it pops-up a Alert (or swaps/shows the error into a <div>)
You are on the right track, the way to handle this is to hook into the htmx:sendError event.
If you wanted to do this with hyperscript, you could add the following code to your body tag (or any enclosing element of the element issuing the request):
<body _="on htmx:sendError call alert('A network error occured')">
...
</body>

DebugElement.query does not work with elements added dynamically to the dom in a spec

I have an app that is using ngx-bootstrap to show a tooltip on mouseover. I want to test that the content, which is dynamically added, shows properly. In order to do this I have a test that looks like this:
it(shows the right tooltip', fakeAsync(() => {
fixture.debugElement.query(By.directive(TooltipDirective))
.triggerEventHandler('mouseover', null);
tick();
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('.tooltip-inner')).nativeElement)
.toBe('the tooltip text');
}
This results in an error that indicates that fixture.debugElement.query(By.css('.tooltip-inner')): "Cannot read property 'nativeElement' of null"
If I print out the content of fixture.debugElement.nativeElement I get this:
<div id="root1" ng-version="5.2.9">
<my-component>
<div ng-reflect-tooltip="the tooltip text">
<img src="images/test.png">
</div>
<bs-tooltip-container role="tooltip" class="tooltip in tooltip-right">
<div class="tooltip-arrow arrow"></div>
<div class="tooltip-inner">the tooltip text</div>
</bs-tooltip-container>
<my-component>
</div>
The important take away is that the html exists - it is just not accessible by the DebugElement.query.
My current solution to get the spec passing is to change the expect to:
expect(fixture.debugElement.nativeElement.textContent.trim())
.toBe('the tooltip text');
This works, but it is a hack that will fall to pieces if I run into a similar situation with multiple tooltips (for example). Has anyone been able to handle this in a better way? Am I not setting this spec up correctly?

Upload file to hidden input using protractor and selenium

I've got a hidden file input field like this:
<input type="file" id="fileToUpload-1827" multiple="" onchange="angular.element(this).scope().setFiles(this)" data-upload-id="1827" class="hidden-uploader">
I'd like to be able to upload files to this. The normal way to do this in protractor would be to do:
ptor.findElement(protractor.By.css('.file-upload-form input')).sendKeys('/path/to/file')
But because the input element isn't visible, I get an error.
I tried:
ptor.driver.executeScript("return $('.file-upload-form input')[0].removeClass('hidden-uploader');").then(function () {
ptor.findElement(protractor.By.css('.file-upload-form input')).sendKeys('hello');
})
But got the error
UnknownError: $(...)[0].removeClass is not a function
It seems ridiculous to have to use executeScript to make an element visible so that I can upload a file, is there a better way? If not, how do I unhide the element?
The full html for the input form is:
<form class="file-upload-form ng-scope ng-pristine ng-valid" ng-if="ajaxUploadSupported">
<strong>Drag files here to upload</strong> or
<label for="fileToUpload-1953">
<div class="btn btn-info select-file-btn">
Click to Select
</div>
</label>
<div>
<input type="file" id="fileToUpload-1953" multiple="" onchange="angular.element(this).scope().setFiles(this)" data-upload-id="1953" class="hidden-uploader">
</div>
</form>
The only way I could find to do this in the end was to use javascript to make the input element visible.
So I have a function unhideFileInputs:
var unhideFileInputs = function () {
var makeInputVisible = function () {
$('input[type="file"]').removeClass('hidden-uploader');
};
ptor.driver.executeScript(makeInputVisible);
}
This contains the function 'makeInputVisible' which is executed in the browser when I call ptor.driver.executeScript(makeInputVisible). Because I know my page contains jQuery I can use the jQuery removeClass method to unhide my file input element.
To see more on how to execute javascript in the browser using webdriver, see the answer to this question (although the answer uses executeAsyncScript rather than executeScript).
To add on user2355213s answer for the more current releases of protractor. ptor is obsolote and instead browser should be used. Also, executeScript() expects a string as parameter. So I ended up using
browser.executeScript('$(\'input[type="file"]\').attr("style", "");');
as my visibility setting was directly applied to the element. Of course, you can also use
browser.executeScript('$(\'input[type="file"]\').removeClass("hidden-uploader");');
depending on your HTML/CSS.

Cgi C program return value to main HTML and display result

I am programming a server side script on an Apache machine with cgi. I am using C for the cgi programming. I am a total noob and learning from online examples(I must say except the basics I didn't come across more web sources for detailed learning!).
I am having a simple HTML page where the username(input) is added to a list which is a file I have in my system and then the updated list should be displayed in the SAME PAGE.
I am not able to "print" the results of both the script and http link on the same page so therefore in the code below, you will only see buttons. Please help.
Here is what I have:
Html:
<html>
<head><title>Home</title></head>
<body>
<h1>REGISTER</h1>
<form action= "/cgi-bin/mycgi.cgi" name ="create user" method ="get">
Enter name:<input type="text" name="user">
<br>
<input type="submit" value="add">
</form>
<FORM action="http://localhost:8000/getusers/" method="get">
<P>
<input value="Display Users" type="submit">
</P>
</FORM>
</body>
Here is the cgi Code:
#include<stdio.h>
#include<string.h>
int main(){
char *tmpStr;
char *user;
printf("Content-Type:text/html\n\n");
printf("<html><head><title></title></head><body>");
tmpStr = getenv("QUERY_STRING");
while(tmpStr && *tmpStr != '='){
tmpStr++;
}
user = tmpStr+1,
printf("Adding %s to User Database",user);
//system("wget http://localhost:8000/newuser/");//call script to add user?
printf("</body></html>");
return 0;//return user?
}
Could you please tell me how I can realize these? How can I display the user list without opening a new html site? Also in the above C code, I have to call the link "http://localhost:8000/newuser/" which returns a success or failure value. How can I return it to the parent form?
Thanks.
You could add an iframe to your html:
<iframe id="theiframe" name="theiframe"></iframe>
And then setting the target of your form to the iframe:
<form action= "/cgi-bin/mycgi.cgi" name ="create user" method ="get" target="theiframe">
Anyway, it is not clear to me if the updated list should be displayed when you click on the first or second button.

JavaScript .innerHTMLworking only when called manually

I've got a very simple function, of replacing the innerHTML of a element. I've been trying to debug this for hours but simply can't, and it's infuriating.
When called from a button press the JavaScript (as follows) works well, but when called from another function it doesn't work. I am totally lost as to why this might be, and its a fairly core part of my app
// This loaded function in my actual code is a document listener
// checking for when Cordova is loaded which then calls the loaded function
loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
Button press and HTML to replace
<div id="main">
<input type='button' onclick='changeText()' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
It is also here in full on JSFiddle
You are already changed the innerHTML by calling the function loaded(); on onLoad.
Put this in an empty file and same as .html and open with browser and try. I have commented the function loaded();. Now it will be changed in onclick.
<div id="main">
<input type='button' onclick='changeText();' value='Change Text'/>
<p>Change this text >> <b id='boldStuff'> THIS TEXT</b> </p>
</div>
<script>
//loaded();
function loaded() {
alert("loaded");
changeText();
}
function changeText() {
alert("started");
document.getElementById('boldStuff').innerHTML = 'Fred Flinstone';
}
</script>
The problem here is, that the element you're trying to manipulate is not yet existing when you are calling the changeText() function.
To ensure that the code is only executed after the page has finished loading (and all elements are in place) you can use the onload handler on the body element like this:
<body onload="loaded();">
Additionally you should know, that it's very bad practice to manipulate values by using the innerHTML property. The correct way is to use DOM Manipulations, maybe this can help you.
You script loads before the element (boldStuff) is loaded,
Test Link - 1 - Put the js in a seperate file
Test Link - 2 - put the js at the very end, before closing the <body>