Cypress POST request, how to access 'fields' section of reponse body - javascript

I want to send a POST request in Cypress that triggers the validation to reject the request
According to Postman, the body of the response looke like this:
"code": "validation_error",
"message": "Validation error, please see fields property for details",
"fields":
{
"TariffData[rate_gp]": " Invalid rate_gp. Expected: 9.35. Imported: 19.35"
}
I am interested in the "fields" section, so I tried to assert with this code:
const api_key = require('../../fixtures/contracts/api_test_client1.json')
const body1 = require('../../fixtures/contracts/body_test_client1.json')
describe('test POST/client validation', () => {
it('send POST/client request', function () {
cy.request({
method: 'POST',
url: Cypress.env('staging_url') + '/service/clients',
headers: {
'API-KEY': api_key,
},
body: body1,
failOnStatusCode:false
})
.then(response => {
expect(response.status).to.eq(400)
expect(response.body.fields).to.contain('"TariffData[rate_gp]": " Invalid rate_gp. Expected: 9.35. Imported: 19.35"')
})
)}
)}
Yet this leads to an error:
AssertionError
object tested must be an array, a map, an object, a set, a string, or a weakset, but object given
Yes, the error message ends there. Any ideas how I can make an assertion that the response contains this message?

I think you just want to present the expected value as an object, not as a string
expect(response.body.fields)
.to.contain({
"TariffData[rate_gp]": " Invalid rate_gp. Expected: 9.35. Imported: 19.35"
})
If you looks at the docs chaijs API they show, for example
expect({a: 1, b: 2, c: 3}).to.include({a: 1, b: 2});
and contain is a synonym for include
You could also try to.deep.equal, as it seems you are specifying the total fields property
expect(response.body.fields)
.to.deep.eq({
"TariffData[rate_gp]": " Invalid rate_gp. Expected: 9.35. Imported: 19.35"
})

Related

Axios get call in Vue3 not working, although curl and javascript work as expected

I'm trying to make an API call from my Vue3 app. The prepared API has an endpoint like http://localhost:8888/api/dtconfigsearch, where one needs to pass a json payload like { "Modelname": "MyFancyModel"} to get the full dataset with the given modelname. Pure get functions without a payload / a body do work from my Vue3 project to the golang backend, but I'm having problems with passing a payload to the backend.
Test with curl -> ok
$ curl -XGET localhost:8888/api/dtconfigsearch -d '{"Modelname" : "MyFancyModel" }'
{"ID":4,"Modelname":"MyFancyModel","ModelId":"96ee6e80-8d4a-b59a-3524-ced3187ce7144000","OutputTopic":"json/fancyoutput"}
$
This is the expected output.
Test with javascript ok
Source file index.js:
const axios = require('axios');
function makeGetRequest() {
axios.get(
'http://localhost:8888/api/dtconfigsearch',
{
data: { Modelname : "MyFancyModel" },
headers: {
'Content-type' : 'application/json'
}
}
)
.then(resp => {
console.log(resp.data)
})
.catch(err => {
console.log(err)
})
}
makeGetRequest()
Output
$ node index.js
{
ID: 4,
Modelname: 'MyFancyModel',
ModelId: '96ee6e80-8d4a-b59a-3524-ced3187ce7144000',
OutputTopic: 'json/fancyoutput'
}
$
Here, I also get the desired output.
Test within Vue fails :-(
Source in the Vue one file component:
onSelection(event) {
let searchPattern = { Modelname : event.target.value }
console.log(event.target.value)
console.log("searchPattern = " + searchPattern)
axios.get("http://localhost:8888/api/dtconfigsearch",
{
data : { Modelname : "Windshield"},
headers: {
'Content-type' : 'application/json',
'Access-Control-Allow-Origin': '*'
}
})
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err)
alert("Model with name " + event.target.value + " not found in database")
})
},
Output in browser:
In the image you can see in the terminal log on the right side that the backend is not receiving the body of the API call. However, in the browser information of the call there is content in the config.data part of the object tree, which is the payload / the body. The only thing that bothers me that it is not a json object, but stringified json, although it was entered as json object. According to the documentation, the parameter name (data) in the call should be correct to hold the body content of the api call.
I've tried different header information, looked if it could be a CORS issue, what it isn't to my opinion, exchanged key data with body, used axios instead of axios.get and adapted parameter, all without success. The version of the axios library is 0.27, identical for Vue and vanilla javascript. After checking successfully in javascript, I was sure that it would work the same way in Vue, but it didn't.
Now I'm lost and have no further ideas how to make it work. Maybe someone of you had similar issues and could give me a hint? I'd be very grateful for some tipps!!

React Admin parseResponse doesn't trigger when query returns error

I'm using React Admin and ra-data-graphQl, when I update something in my UserEdit component all works perfect, BUT, when I need to handle the error message from the API, I don't know where catch it.
This is my Update query:
case 'UPDATE': {
const updateParams = { ...params };
return {
query: gql`mutation updateUser($id: ID!, $data: UpdateUser!) {
data: updateUser(id: $id,input:$data) {
${buildFieldsGraphQL(updateFields)}
}
}`,
variables: {
...updateParams,
id: updateParams.data.uuid,
data: {
...updateParams.data,
},
},
parseResponse: (response) => {
console.log('tr response: ', response);
},
};
}
When the API returns an error, it never reach the console.log.
I was searching a list with options here (https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql#options) searching something like "parseError", but I did not find nothing similar.
I need to catch the error and show a message in the UserEdit form.
Reading the link that I share in this post, it say this:
but must return an object matching the options of the ApolloClient query method with an additional parseResponse function.
I understand that I should go to the link in the word "query" and check if there is something like "parserError", but the link is broken:
https://www.apollographql.com/docs/react/reference/index.html#ApolloClient.query
Any help?
Ok, its easier. By adding the onFailure function I can handle the error.

How can we ignore the openHash value in cy.route() request send

In Cypress I am using cy.route() for sending the below request, but cypress is not identifying the below request send. In the route url there is a openHash value which will be different for every POST request. Is there any way to ignore the openHash value or accept what ever value displays there.
So far I have tried by giving the url in following ways in route.
url: '**/student/details.php?viewDetails=project&stdCount=1&sectionID=1&openHash=**',
url: '**/student/details.php?viewDetails=project&stdCount=1&sectionID=1&openHash=**&ajaxCall=true**',
I believe while using cy.route() the POST url need to match exactly. Could someone please advise
Cypress version: 5.4.0
Student.feature
Feature: Update student details
Background: User logged in to application
Given I should load all of the routes required for tests
Scenario: Update student details
When I am logged in as the student user
And I click on "Student" subtab
And I should see details displayed
Step definition:
import { Then, When, And } from "cypress-cucumber-preprocessor/steps";
before(() => {
Then('I should load all of the routes required for tests', () => {
cy.server();
cy.route({
method: 'POST',
url: '**student/details.php?viewDetails=project&stdCount=1&sectionID=1&openHash=5fc8329a76e73&ajaxCall=true**',
delay: 2000
}).as('getStudentTabDetails');
})
})
Then('I am logged in as the student user', () => {
cy.get('[name=loginUsername]').type("Student1");
cy.get('[name=loginPassword]').type("somePassword1", { sensitive: true });
cy.contains('Login').click();
})
Then('I click on {string} subtab', (student) => {
cy.get('#main a').contains(student).click({force:true});
});
Then('I should see details displayed', () => {
cy.wait('#getStudentTabDetails', { timeout: 5000 });
});
Error:
CypressError
Timed out retrying: cy.wait() timed out waiting 5000ms for the 1st request to the route: getStudentTabDetails. No request ever occurred.
Cypress.minimatch is a tool that can be used for checking the route matchers.
By default Cypress uses minimatch to test glob patterns against request URLs.
If you’re struggling with writing the correct pattern you can iterate much faster by testing directly in your Developer Tools console.
The two routes you show in the question actually pass the minimatch test.
const url = 'http://example/student/details.php?viewDetails=project&stdCount=1&sectionID=1&openHash=5fc8329a76e73&ajaxCall=true';
const pattern1 = '**/student/details.php?viewDetails=project&stdCount=1&sectionID=1&openHash=**';
console.log( Cypress.minimatch(url, pattern1) ); // true
const pattern2 = '**/student/details.php?viewDetails=project&stdCount=1&sectionID=1&openHash=**&ajaxCall=true**';
console.log( Cypress.minimatch(url, pattern2) ); // true
Here is a Cypress fiddle that shows how to use the new intercept method to handle query parameters.
/// <reference types="#cypress/fiddle" />
const test = {
html: `
<p class="text-lg"></p>
<script>
setTimeout(() => {
const url = 'http://example/student/details.php?viewDetails=project&stdCount=1&sectionID=1&openHash=5fc8329a76e73&ajaxCall=true';
window.fetch(url, { method: 'POST'});
}, 1000);
</script>
`,
test: `
cy.intercept({
method: 'POST',
url: '/student/details.php',
query: {
viewDetails: 'project', // whatever query parts you care about
stdCount: '1',
sectionID: '1'
}
}, {}) // Added an empty stub here, as my url does not actually exist
.as('getStudentTabDetails');
cy.wait('#getStudentTabDetails')
`
}
it('', () => {
cy.runExample(test)
});
The POST is made with native fetch(), which would not be captured in the old cy.route() method without using a polyfill.

How to modify request headers using Puppeteer & Chrome DevTools Protocol? (Possibly a JS syntax issue)

I have the following Typescript function that assumes a Chrome browser has already been launched using Puppeteer. The documentation for the Fetch functions used below can be found here.
async function modify(client: CDPSession) {
client.on('Fetch.requestPaused', async ({ requestId, request, frameId, resourceType, responseErrorReason, responseStatusCode, responseHeaders, networkId }) => {
// Correctly prints out the User-Agent header's value
console.log(request.headers["User-Agent"]);
// After this line is run, I can inspect the request.headers object and see that User-Agent was successfully edited
request.headers['User-Agent'] = 'trying to edit this header';
// Continuing the request gives an error
await client.send('Fetch.continueRequest', {
requestId: requestId,
headers: request.headers,
});
});
}
Here is the specific error I'm seeing:
Error: Protocol error (Fetch.continueRequest): Invalid parameters headers: array expected
How can I resolve this error and successfully modify the request.headers? Is this a silly Javascript/Typescript syntax issue that I just can't figure out?
Fetch.requestPaused returns the headers as an object. e.g.:
{
"Upgrade-Insecure-Requests":"1",
"Accept": "text/html,application/xhtml+xml"}
}
Fetch.continueRequest expects an Array<{name: string, value: string}>. e.g.
[
{"name": "Accept", value: "text/html,application/xhtml+xml"}
]
You can use the code that Puppeteer is using:
function headersArray(headers) {
const result = [];
for (const name in headers) {
if (!Object.is(headers[name], undefined))
result.push({name, value: headers[name] + ''});
}
return result;
}

Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body in react-admin

I am using react-adminframework, and I have written my own DataProvider. I am trying to accomplish that when an User is created, an instance of UserPossession is created as well. My code bellow accomplishes that, but react-admin Front-end just displays the warning message:
Failed to execute 'fetch' on 'Window': Request with GET/HEAD method cannot have body
I checked the Network tab in Developer Tools and every request to server is correct, there is no error. Which leaves me confused and stuck with this, because I have no idea what that warning means or why is it even occuring.
My code is a part of convertDataRequestToHTTP constant and looks like this:
if (resource === 'User') {
url = `${apiUrl}/${resource}`;
options.body = params.data;
httpClient(url, {
method: 'POST',
body: JSON.stringify(options.body),
})
.then(response => (
url = `${apiUrl}/Location`,
options.method = 'POST',
options.body = JSON.stringify({
"odata.type": "HardwareDatabase.UserPossession",
"Name": response.json.Login,
"UserId": response.json.Id
}),
httpClient(url, {
method: options.method,
body: options.body
})
));
}
If you have any questions regarding the code I can clarify.
Thank you for any ideas in advance.
Since you are stating that this code snippet is a part of convertDataRequestToHTTP I might see the issue. httpClient cannot be used in this constant since it creates duplicit calls to API or in your case, this Warning. Correct way would be to only state the options constant.
url = `${apiUrl}/${resource}`;
options.body = JSON.stringifiy(params.data);
options.method = 'POST';
Later in the constant that converts response from OData to mandatory React Admin format, state the httpClient.
params.data = {
"odata.type": "HardwareDatabase.UserPossession",
"Name": response.json.Login,
"UserId": response.json.Id
};
httpClient(`${apiUrl}/Location`, {
method: 'POST',
body: JSON.stringify(params.data),
})
Unfortunately, the GET method for XMLHttpRequest and fetch don't support request bodies.
A temporary work around I found was to use the axios library, which does let you send GET with request bodies.
const res = await axios.get("/api/devices", {
data: { deviceName: 'name' }
})

Categories

Resources