I'm building up a test in Geb (WebDriver) and need to submit a form which will create a file in response.
I am able to download the file (the Browser save it automatically to the disk), but I wand to check it in GEB.
I've tried withNewWindow(), but it only works on URIs??
I've tried downloadXXX(), but no luck either...
How can I download a file into a variable?
class CSVTest extends GebReportingTest
#Test
void csvCreation() {
to CSVExport
// select entries / fill values
selectAllEntries.value(true)
//// this will do a post
//// the server will render a file and deliver it back as a result of the submit
// CORRECTLY downloads the file
submitButton.click()
// NOT WORKING
withNewWindow (submitButton.click()) {
...
}
// NOT WORKING
def csv = download(submitButton.click())
}
}
You won't be able to intercept the file downloaded by the browser after clicking a button that does a post in any way unfortunately.
You will have to synthesize a post request with the right content which is sent when using the form. While it is possible to do so using Geb's DownloadSupport class it will be complicated and clunky. You're better off using a library for which performing such requests is the main functionality, like for example REST-Assured.
Related
Tech stack - Node.js, MongoDB for the database, Strapi CMS for editing and API, React - my application.
I have a database with a long list of entries and a ready-to-use application that allows users to read data from the database. I need to be able to generate a simple website with a single entity from my database as a source to fill the template.
Mockup
Here is a mock-up. Hopefully, it will make things a bit clearer.
Clarification
After a day of thinking about the task, I believe I need something like a simplest static website generator - an application that will allow me to select a single bit of data from the list and generate a small website filled with it. The end goal is to get a website in some subfolder of my application where I can get it and use it however I need.
A bit more about specifics:
It will be used locally
Security can be neglected
Running always in development is not a problem (just in case, thinking about additional question #2)
Few additional questions:
Is it possible to run NPM scripts from the application (like npm build)
Is there any way to show one component in development mode, but replace it with another during building for production?
App.js
//...
function App() {
if() {
return <AdminUI /> // This one is to be shown in development mode
} else {
return <Website /> // This one is to be used instead of AdminUI in the build
}
UPDATE
Well, I'm digging a path to create a site generator and so far I come up with the following basic plan:
Get my template ready
Create a new directory for my website
Copy a template to the new folder
Get an HTML file, parse it to a string to modify
Swap some bits with my data
Save to a file from the modified string
repeat if needed for other files.
If that works as expected, the whole process probably might be improved by moving from a fixed template to a component, that will be prepared with a JavaScript bundler and started with the help of something like node-cmd (to run shell commands from my application)...
What you want could be achievable, but if it's just a string and little else, I'd say it's much simpler to fetch the data at startup from a given file, and populate from there. You can put a JSON file under the public folder (together with other static data, like images) and have the file being your configuration.
In the App.js file, write an async componentDidMount() and you can do an await axios.get("") with your configuration.
So App.js would look like (code written on the fly, didn't check in an IDE):
export class App extends React.App {
constructor(props) {
super(props);
this.state = { loading: true, };
}
async componentDidMount() {
const response = await axios.get("your/data.json");
this.setState({ loading: false, ... whatever})
}
render = () => (
<>
(this.state.loading && <div>Still loading...</div>)
(this.state.adminData && <AdminUI data={this.state.admingData} />)
(this.state.devData && <Website data={this.state.devData} />)
</>
)
}
If you don't care about security, wouldn't be much simpler like this? And if you use TypeScript you'll have a much much simpler life in handling the data too.
Maybe it's worth doing an AdminUI to generate the JSON, and the another UI which reads the JSON, so you end up doing two UIs. The template-generated UI could even ask for a JSON file to bootstrap directly to the user, if it simplifies... In general, an approach based on simple JSON sounds a lost simpler than going for a CI/CD pipeline.
I want to use a workflow similar to the Page Object Pattern that exists in frameworks like Selenium. I want to use my login.spec.js in my editSettings.spec.js, because it requires a user to be logged in.
How do I achieve this in Cypress? Can I export a function from one test file to use in another?
Yes, Cypress supports the ability to create and reuse actions in your UI, such as logging in as a user would.
However, Cypress also allows you to control the state of the browser more powerfully than a user would.
For example: I create a test that a "user can log in with valid username and password"- Cypress navigates to the login page, types in the user field, types in the password field and clicks the "Log in" button. The Page Object Pattern would have you reuse this action on every test that requires a user to be logged in (most of the tests)
Cypress supports this; however, this is slower than it has to be. It takes a considerable amount of time to navigate to a login page, type in the information, handle the response, and navigate to the page under test.
Instead, Cypress's API allows the following:
use cy.request() to directly hit your server with the login credentials. This requires no state of your app, no typing in fields, no clicking buttons, or page directs
Any cookies your site uses are automatically set, or you can set localStorage using the response
Make this a custom command, call it before every test, and boom- you've generated your user's state almost instantly and most importantly flake-free
I actually came up with these two examples, one using JavaScript and another one with Typescript.
https://github.com/antonyfuentes/cypress-typescript-page-objects
https://github.com/antonyfuentes/cypress-javascript-page-objects
Hopefully, this helps someone else.
Create a SearchProduct.js file in fixtures folder (You can create it anywhere).Then create a class in it and define your all the methods in it something like this:
class ProductPage {
getSearchClick() {
return cy.get('.noo-search');
}
getSearchTextBox(){
return cy.get('.form-control');
}
getProductsName() {
return cy.get('.noo-product-inner h3');
}
getSelectSize() {
return cy.get('#pa_size');
}
getSelectColor() {
return cy.get('#pa_color');
}
getAddtoCartButton() {
return cy.get('.single_add_to_cart_button');
}
}
export default ProductPage
After creating the class, let's import it in the command.js file. After that, let's create a new object of it to access all the methods mentioned above in commands.js.
import ProductPage from '../support/PageObjects/ProductPage';
Cypress.Commands.add("selectProduct", (productName, size , color) => {
// Creating Object for ProductPage
const productPage=new ProductPage();
// Doing the search part for Shirts.
productPage.getSearchClick().click()
productPage.getSearchTextBox().type('Shirt');
productPage.getSearchTextBox().type('{enter}')
productPage.getProductsName().each(($el , index , $list) => {
//cy.log($el.text());
if($el.text().includes(productName)) {
cy.get($el).click();
}
})
// Selecting the size and color and then adding to cart button.
productPage.getSelectColor().select(color);
productPage.getSelectSize().select(size);
productPage.getAddtoCartButton().click();
})
So, here actually the custom command's class is importing and using the Page class. Additionally, the test script will use the same command.js to perform the needed action.
So, the test script will still be the same and will look as below:
// type definitions for Cypress object "cy"
// <reference types="cypress" />
describe('Cypress Page Objects and Custom Commands', function() {
//Mostly used for Setup Part
before(function(){
cy.fixture('example').then(function(data)
{
this.data=data ;
})
})
it('Cypress Test Case', function() {
//Registration on the site
cy.visit('https://shop.demoqa.com/my-account/');
cy.get('#reg_username').type(this.data.Username);
cy.get('#reg_email').type(this.data.Email);
cy.get('#reg_password').type(this.data.NewPassword);
cy.get('.woocommerce-Button').click();
//Checking whether the Registration is successful and whether UserName is populated under login section
cy.get('#username').should('have.value',this.data.Username);
})
// For Loop for Accessing productName array from Features File and Using the custom command
this.data.productName.forEach(function(element){
// Invoke the Custom command selectProduct
cy.selectProduct(element[0],element[1],element[2]);
})
})
You can also directly import the class in Test File by skipping Command.js file.
For that go to the following link:
Courtesy: https://softans.com/cypress-page-object-model/
I have a Java API endpoint that returns an excel (I am using content-disposition=Content-Disposition","attachment; filename=Audit.Report.xlsx).
I have another Angular-Node JS application that needs to consume this API and when the user clicks on a link, it should pull the excel and display a pop-up asking them the location to save the document. I am at a loss as to how I can do this. I tried doing the following on the server side though,
Server Code:
getAuditReport = function ( req, resp ) {
var numberOfMonths = req.query.numberOfMonths;
console.log('In first method ' + numberOfMonths);
var auditReportPromise = this.getAuditReportXlPromise ( numberOfMonths );
auditReportPromise.then ( function ( data ) {
resp.headers('Content-Disposition: attachment; filename="audit.report_'+ new Date() + '".xls"');
resp.ContentType = "application/vnd.ms-excel";
resp.status ( 200 ).send ( data );
} ).catch ( function ( err ) {
resp.status ( 500 ).send ( err );
} );
}
The getAuditReportXlPromise method returns a promise to invoke the get method of the Java API. On invoking this API via a browser, I get the excel content on the browser rather than a prompt requesting me to save the document somewhere.
Can someone suggest what's wrong here, and what I need to do on the client side for the click functionality to work.
Update 1:
Following is the code from the HTML
<a id='10051' href="{{url}}" target=_blank class="ok-white-text">
Download Report
</a>
Based on the duration the user selects, I'm building the URL - this is getting built correctly.
If your API provides the file using GET then you should be able to download the file in a simple way by just setting a link to that API (i.e. an anchor containing the link like Download).
In this scenario the browser will take over the download process (i.e. locating the folder to download to, if configured to do so).
If you want to process the file on client side, then you need to transfer it in a binary mode as suggested by multiple answer for this question.
To answer how I did manage to get around this,
Client Side:
In the component, I did the following - wrote a method on clicking the button that contains a window.open to the url - something like,
window.open("<path to the java api>", '_blank');
The link like I mentioned above was a java api that was already generating the excel file.
So I'm creating a script that uses nightmareJS and requests. I'm making the requests grab data from a webpage and then have nightmareJS navigate to a page as well. I'm then injecting a javascript file into the nightmare session using
.inject('js', 'injectFile.js')
This all works perfectly, however im trying to achieve something else. After grabbing data from the other page using requests, i would like to pass that data into the injectFile.js file. For example, I would get a url with the request. and then use that url in the injectFile.js file when it is called. Is there anyway / module to achieve this? Thanks in advance
The best way to do this is to define a function in injectFile.js, so that whatever you're doing doesn't run immediately when you inject the file, but only when you call the function:
function doStuff(params) {
// do stuff with params
// (this probably contains your entire injectFile.js script)
}
Then use nightmare.evaluate to call that function after you have injected it into the browser context:
nightmare.evaluate(function(params) {
doStuff(params);
}, yourFavoriteParamValues)
I've started a haxe js project in FlashDevelop, I need to load a local file, is this possible? how to to so?
The simple answer is use "resources". You add a path and an identifier to your hxml:
-resource hello_message.txt#welcome
And you use it in your code like this:
var welcome = haxe.Resource.getString("welcome");
Note that the operation is performed at compile time so there is no runtime overhead. It is essentially equivalent to embed the file content in a quoted string.
The complex answer is to use a macro. With them you can load, parse, process and do all the manipulation you might need. Pretty commonly, you can see macros to load a config file (say JSON or YAML) and use it as part of your application (again at compile time and not at runtime).
You could grab files with an XMLHttpRequest as long as you keep them somewhere public (if you're putting it online) and accessible to the script.
Here's a quick example of grabbing a text file from the location assets/test.txt
This is the sort of thing I usually do in the JS games I make, I find it a bit more flexible than just embedding them with -resource.
If it's not exactly what you're looking for then Franco's answer should see you through.
package ;
import js.html.XMLHttpRequest;
import js.html.Event;
class Start {
static function main() {
var request = new XMLHttpRequest();
// using the GET method, get the file at this location, asynchronously
request.open("GET", "assets/test.txt", true);
// when loaded, get the response and trace it out
request.onload = function(e:Event){
trace(request.response);
};
// if there's an error, handle it
request.onerror = function(e:Event) {
trace("error :(");
};
// send the actual request to the server
request.send();
}
}