I created the following command in cypress commands.js: -
Cypress.Commands.add("form_request", (method, urlPathParam, formData) => {
return cy
.server()
.route(method, "https://admin.teamapp.myhelpling.com" + urlPathParam)
.as("formRequest")
.window()
.then(win => {
const xhr = new XMLHttpRequest();
xhr.open(method, "https://admin.teamapp.myhelpling.com" + urlPathParam);
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("access-token", accesstoken);
xhr.setRequestHeader("client", client);
xhr.setRequestHeader("expiry", expiry);
xhr.setRequestHeader("token-type", tokentype);
xhr.setRequestHeader("uid", uid);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(formData);
})
.wait("@formRequest");
});
Kindly note that I'm reading all header values from previous login request.
In the test spec file I'm doing the following inside a Test case: -
cy.AppLogin();
let dataname = "Test FORMDATA API";
let formData = new FormData();
formData.append("client[name]", dataname);
formData.append(
"client[client_logo_attributes][content]",
cy.fixture("/images/clients/Golden JPEG.jpeg")
);
cy.form_request("POST", "/admin/clients", formData).then(response => {
cy.log(response.status);
});
On cypress electron app I see the following error: -
CypressError: Timed out retrying: cy.wait() timed out waiting 5000ms for the 1st request to the route: 'formRequest'. No request ever occurred.
Commenting .wait("@formRequest"); out from commands.js code above I get the following error: -
POST https://admin.teamapp.myhelpling.com/admin/clients 405 (Method Not Allowed)
In the sources tab on dev tool I see a X(cross) mark on xhr.send(formData) line provided below: -
xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=----WebKitFormBoundaryAGOGnFVPveAa8gfv");
xhr.send(formData);
}); //.wait("@formRequest");
});
I've no idea what to do here. I'm also new to cypress. Kindly suggest.
All information is already provided above. Please do let me know what else I need to provide.
@Vinod-Gulia Yeah, our new bot added the needs information label - we're still working out a few things with it. @bahmutov I don't think a stage label should be added to newly opened issues.
As for your issue, have you verified that this same request outside of Cypress does not respond with a 405? Please double check that it's the right url.
I'm not exactly sure what you're trying to test overall, but have you tried using cy.request() to test what you want to test?
@jennifer-shehane - Yes, this URL works perfectly fine outside Cypress. Also, it works fine when cypress interacts the GUI of the application.
I have already testing the component from GUI. So I was thinking to create a command which mimics the same GUI functions but without interacting with the GUI elements. To achieve so I've to do a POST request and later play around with its response.
I started with using cy.request in the below mentioned manner: -
Add the following command in commands.js
Cypress.Commands.add("Client", () => {
cy.request({
method: "POST",
url: "https://admin.teamapp.myhelpling.com/admin/clients",
headers: {
accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded",
"access-token": accesstoken,
client: client,
expiry: expiry,
"token-type": tokentype,
uid: uid
},
body: {
client: {
name: "Cypress TER Client",
client_logo_attributes: {
content: "fx:../images/clients/Bull Client.jpg"
}
}
}
}).its("body");
});
Then called this command into my test cases as following: -
it("Test", () => {
cy.AppLogin();
cy.Client();
});
Kindly note that cy.AppLogin() will also set the headers for cy.Client() headers.
When I run this test i got the following error: -
CypressError: cy.request() failed trying to load:
https://admin.teamapp.myhelpling.com/admin/clients
We attempted to make an http request to this URL but the request failed without a response.
We received this error at the network level:
> str.replace is not a function
-----------------------------------------------------------
The request we sent was:
Method: POST
URL: https://admin.teamapp.myhelpling.com/admin/clients
-----------------------------------------------------------
Common situations why this would fail:
- you don't have internet access
- you forgot to run / boot your web server
- your web server isn't accessible
- you have weird network configuration settings on your computer
The stack trace for this error is:
TypeError: str.replace is not a function
at Querystring.rfc3986 (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\request\lib\querystring.js:43:14)
at Request.json (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\request\request.js:1293:30)
at Request.init (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\request\request.js:406:10)
at Request.RP$initInterceptor [as init] (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\request-promise-core\configure\request2.js:45:29)
at new Request (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\request\request.js:127:8)
at request (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\request\index.js:53:10)
at C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\request\index.js:100:12
at Object.create (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\lib\request.js:170:18)
at C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\lib\request.js:364:26
at tryCatcher (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\bluebird\js\release\util.js:16:23)
at Promise._settlePromiseFromHandler (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\bluebird\js\release\promise.js:510:31)
at Promise._settlePromise (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\bluebird\js\release\promise.js:567:18)
at Promise._settlePromise0 (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\bluebird\js\release\promise.js:612:10)
at Promise._settlePromises (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\bluebird\js\release\promise.js:691:18)
at Async._drainQueue (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\bluebird\js\release\async.js:133:16)
at Async._drainQueues (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\bluebird\js\release\async.js:143:10)
at Immediate.Async.drainQueues (C:\Users\gulivi\AppData\Local\Cypress\Cache\3.1.5\Cypress\resources\app\packages\server\node_modules\bluebird\js\release\async.js:17:14)
at runCallback (timers.js:781:20)
at tryOnImmediate (timers.js:743:5)
at processImmediate [as _immediateCallback] (timers.js:714:5)
The str.replace error is a bug documented here: https://github.com/cypress-io/cypress/issues/2923
We will have to look into the other POST formdata issue. Any information you can provide to help us reproduce it locally on our end would be most helpful.
@jennifer-shehane - This application under test accept form data in the following format : -
client[name] = "Cypress TER Client"
client[client_logo_attributes][content] = Some_Blob
With cy.request() I assume I had to convert that into JSON as body of the request. This is the following transformation of FormData into JSON: -
body: {
client: {
name: "Cypress TER Client",
client_logo_attributes: {
content: "fx:../images/clients/Bull Client.jpg"
}
}
I believe I'm not doing that correctly. Please suggest.
I checked how POSTMAN sends this request out and tried that too but no luck. Here's the code I tried: -
body: qs.stringify({
mode: "formdata",
formdata: [
{
key: "client[name]",
value: "Cypress TER Client",
type: "text"
},
{
key: "client[client_logo_attributes][content]",
type: "file",
src: cy.readFile("cypress//fixtures//images/clients//Bull Client.jpg")
}
]
})
You may want to try to run JSON.stringify on any strings, but I'm not really sure beyond that what your implementation details are here.
hello @jennifer-shehane , i think my below issue smiler , need your help plz , for how to load body from fixture file that defined below
it('Validate the header', () => {
cy.server();
cy.fixture('fixture/flightsEmails/fc_return-1adult-1child.json').then((bodyjson) => {
//cy.route('GET', 'fixture/flightsEmails/fc_return-1adult-1child.jsonn').as('bodyjson')
cy.request({
method: 'Post',
url: 'http://email-dev-external-endpoint.com/api/send-as-email',
body: this.bodyjson
})
})
})
, thanks for your continues support
@Vinod-Gulia @lorennorman
Appreciate your feedback for above request plz
@Elshaikh, I think you should be able to update the this.bodyjson to just be bodyjson to match the arguments being received to the .then() callback function. Does this work?
thanks @jennifer-shehane , working fine now after follow your update
Nothing works for me. If I use cy.request(), I'm unable to send formdata with it which contains a text and an image. So I've to go via XHR route. So, in my command.js I've used the following code to create a command: -
Cypress.Commands.add("formrequest", (method, url, formData, done) => {
cy.window().then(win => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method, url, false);
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("access-token", accesstoken);
xhr.setRequestHeader("client", client);
xhr.setRequestHeader("expiry", expiry);
xhr.setRequestHeader("token-type", tokentype);
xhr.setRequestHeader("uid", uid);
xhr.setRequestHeader("Accept-Encoding", null);
xhr.onload = function() {
done(xhr);
};
xhr.onerror = function() {
done(xhr);
};
xhr.send(formData);
});
});
});
Now, when I'm calling it I will first construct a BLOB and then use it in my formdata to later send the XHR request. Like this: -
it("Test XHR", () => {
cy.AppLogin();
cy.fixture("/images/clients/Golden JPEG.jpeg", "binary").then(imageBin => {
// File in binary format gets converted to blob so it can be sent as Form data
Cypress.Blob.binaryStringToBlob(imageBin, "image/jpeg").then(blob => {
// Build up the form
const formData = new FormData();
formData.set("client[name]", "Test TER"); //adding a plain input to the form
formData.set(
"client[client_logo_attributes][content]",
blob
//"Bull Client.jpg"
); //adding a file to the form
// Perform the request
cy.formrequest(method, url, formData, function(response) {
expect(response.status).to.eq(201);
});
});
});
});
Please note that cy.AppLogin() sets up the request headers like accesstoken, client, expiry, tokentype and uid.
Kindly refer to the attached file for checking the XHR request. Also attached a file (fromCypressUI) for showing XHR request being made when I did run my test from application UI.
I'm constantly getting 405, Method not allowed error. Please help. This is critical.
Thanks Jennifer. It works for me following the instructions mentioned at github.com/javieraviles/cypress-upload-file-post-form
Cypress.Commands.add(
"Post_Clients",
(imagePath, imageType, attr1, attr2, attr1Val, done) => {
cy.fixture(imagePath, "binary").then(imageBin => {
Cypress.Blob.binaryStringToBlob(imageBin, imageType).then(blob => {
const xhr = new XMLHttpRequest();
xhr.withCredentials = true;
const data = new FormData();
data.set(attr1, attr1Val);
data.set(attr2, blob);
xhr.open("POST", "https://api.teamapp.myhelpling.com/admin/clients");
xhr.setRequestHeader("accept", "application/json");
xhr.setRequestHeader("access-token", accesstoken);
xhr.setRequestHeader("client", client);
xhr.setRequestHeader("expiry", expiry);
xhr.setRequestHeader("token-type", tokentype);
xhr.setRequestHeader("uid", uid);
xhr.onload = function() {
done(xhr);
};
xhr.onerror = function() {
done(xhr);
};
xhr.send(data);
});
});
}
);
it.only("API POSTing TEST", () => {
cy.Post_Clients(
"/images/clients/Golden JPEG.jpeg",
"image/jpeg",
"client[name]",
"client[client_logo_attributes][content]",
"Test Attr 1 Value is Hi!!!",
response => {
cy.writeFile(
"cypress/fixtures/POST API OUTPUT DATA/Client.json",
response.
);
expect(response.status).to.eq(201);
}
);
});
Thanks Jennifer. It works for me following the instructions mentioned at github.com/javieraviles/cypress-upload-file-post-form
Cypress.Commands.add( "Post_Clients", (imagePath, imageType, attr1, attr2, attr1Val, done) => { cy.fixture(imagePath, "binary").then(imageBin => { Cypress.Blob.binaryStringToBlob(imageBin, imageType).then(blob => { const xhr = new XMLHttpRequest(); xhr.withCredentials = true; const data = new FormData(); data.set(attr1, attr1Val); data.set(attr2, blob); xhr.open("POST", "https://api.teamapp.myhelpling.com/admin/clients"); xhr.setRequestHeader("accept", "application/json"); xhr.setRequestHeader("access-token", accesstoken); xhr.setRequestHeader("client", client); xhr.setRequestHeader("expiry", expiry); xhr.setRequestHeader("token-type", tokentype); xhr.setRequestHeader("uid", uid); xhr.onload = function() { done(xhr); }; xhr.onerror = function() { done(xhr); }; xhr.send(data); }); }); } ); it.only("API POSTing TEST", () => { cy.Post_Clients( "/images/clients/Golden JPEG.jpeg", "image/jpeg", "client[name]", "client[client_logo_attributes][content]", "Test Attr 1 Value is Hi!!!", response => { cy.writeFile( "cypress/fixtures/POST API OUTPUT DATA/Client.json", response. ); expect(response.status).to.eq(201); } ); });
Is the above method working in headless browser(Electron)?
@arthsiddh
Are you lazy or is it too difficult to test it by yourself?
@Uzlopak Please thoroughly read through of our Code of Conduct for our repository. This kind of language against another user seeking help is not welcome here. https://github.com/cypress-io/cypress/blob/develop/CODE_OF_CONDUCT.md
Hello @jennifer-shehane
you are right. I will try to be more inclusive.
Did you investigate your Software to give @arthsiddh the proper answer for his/her/xir question?
Hi, @jennifer-shehane ! I need help with POST request. I created request for send form-data to server:
const options = {
'method': 'POST',
'url': 'http://localhost:3000/api',
'headers': {
'Content-Type': 'multipart/form-data; Boundary=XXX',
'Accept': 'application/json'
},
form:true
body: {
login: 'some',
email: '[email protected]',
password: 'some12345',
CSR: 'some'
}
}
cy.request(options).then((response) => {
if (error) throw new Error(error);
console.log(response.body);
});
but always get an error,
Error: Invalid content type (application/x-www-form-urlencoded). These are valid: multipart/form-data<br>  
because form:true converted the body values to url encoded content and set the x-www-form-urlencoded header.
How can I send form data with header multipart/form-data without convert to x-www-form-urlencoded?
How can I send form data with
header multipart/form-datawithout convert to x-www-form-urlencoded?
const querystring = require('querystring');
const options = {
'method': 'POST',
'url': 'http://localhost:3000/api',
'headers': {
'Content-Type': 'multipart/form-data; Boundary=XXX',
'Accept': 'application/json'
},
body: querystring.stringify({
login: 'some',
email: '[email protected]',
password: 'some12345',
CSR: 'some'
})
}
cy.request(options).then((response) => {
if (error) throw new Error(error);
console.log(response.body);
});
@linababenko try maybe sending body data as querystring.stringify({...}) with no form: true.
How can I send form data with
header multipart/form-datawithout convert to x-www-form-urlencoded?const querystring = require('querystring'); const options = { 'method': 'POST', 'url': 'http://localhost:3000/api', 'headers': { 'Content-Type': 'multipart/form-data; Boundary=XXX', 'Accept': 'application/json' }, body: querystring.stringify({ login: 'some', email: '[email protected]', password: 'some12345', CSR: 'some' }) } cy.request(options).then((response) => { if (error) throw new Error(error); console.log(response.body); });@linababenko try maybe sending body data as
querystring.stringify({...})with noform: true.
Did you get a solution on this? I am facing a similar issue when uploading test images through Jira API
Hi, @jennifer-shehane ! I need help with POST request. I created request for send form-data to server:
const options = { 'method': 'POST', 'url': 'http://localhost:3000/api', 'headers': { 'Content-Type': 'multipart/form-data; Boundary=XXX', 'Accept': 'application/json' }, form:true body: { login: 'some', email: '[email protected]', password: 'some12345', CSR: 'some' } } cy.request(options).then((response) => { if (error) throw new Error(error); console.log(response.body); });but always get an error,
Error: Invalid content type (application/x-www-form-urlencoded). These are valid: multipart/form-data<br>  because
form:trueconverted the body values to url encoded content and set the x-www-form-urlencoded header.How can I send form data with
header multipart/form-datawithout convert to x-www-form-urlencoded?
Any resolution?????
We have two types of e2e tests. Api tests, which don't need to run in browser and UI tests which need to run in the browser. The UI tests work properly with cy.request and usually there you don't need to stub your own requests generated by cy.request but just your UI requests which works properly.
In the API tests you usually don't need to stub your requests. We gave up to handle those by cy.request. we have anyway a client for our api and first I wanted to implement a cypress adapter, next to our axios adapter, but it did not work as expected because cy.request is not really awaitable (yeah yeah, we know the reasons). So instead of using cy.request we use axios and use Cypress.log with its rich options to have the same information output as cy.request in cypress. Or use request-promise. stop wasting your time when trying to do requests which should not run in the browser by doing cy.request.
@gitsaquib
The solution for me was to use "fetch".
//First, you need to read file, which you wont to upload
cy.readFile(`cypress/fixtures/${fileName}`).then((str) => {
// Create blob, set type of data:
let blob = new Blob([str], {type: 'text/plain'})
// Create headers:
const myHeaders = new Headers({
'Authorization': `Bearer ${Cypress.env('token')}`
})
// Create FordData
let formData = new FormData()
formData.append('hash', hashFile)
formData.append('file', blob)
// You can specify URL globally in cypress.json:
fetch(`${Cypress.env('backendURL')}/file`, {
method: 'PUT',
headers: myHeaders,
body: formData,
}).then((resp) => {
// And wait for response:
Cypress.env('respStatus', resp.status)
return Promise.resolve(resp)
})
.then((resp) => {
return resp.json()
})
.then((data) => {
// some expect
})
// You set alias and, wait alias in the following tests:
}).as('Update txt file').wait(100)
Most helpful comment
@Elshaikh, I think you should be able to update the
this.bodyjsonto just bebodyjsonto match the arguments being received to the.then()callback function. Does this work?