Submit a POST form in Cypress and navigate to the resulting page - request

I'm having issues with Cypress loading the response body when I utilize the cy.request() command.
In our application, when a form is filled out and submitted, it POSTs, and the response body is the new page.
When I'm trying to do in Cypress is programmatically fill out the form. So I set up a cy.request() command, with the body filled with the form fields, which is the same as what happens when you fill it out manually. When I run the command, I can view the console and see that the correct body is being returned, but the new document page doesn't load. So I'm left just sitting on the old empty form page.
cy.request({
url: "company-webpage-form-url.com",
method: "POST",
form: true,
body: {
first_name: "first_name",
last_name: "last_name",
company_name: "company_name",
address1: "address1",
address2: "address2",
city: "city",
state: "NY",
zip: "13903",
country: "US",
phone_number: "607-555-5555",
phone_ext: "555",
fax_number: "fax_number",
fax_ext: "fax_ext",
email: "developer#company.com",
email_2: "developer#company.com",
user_data: "Continue"
}
});
All of the data is correct, and I get the correct response body, but I can only see it in the console. I have no idea how to get it to load, like it would when I submit the form. All I get right now is a 200 response, and the test ends.
I've tried visiting the next URL right after, but I get an error that the page for that URL doesn't exist. I've tried clicking the submit button after the POST, but that just results in an empty form being submitted, which causes a validation error.
I'm at a loss for how to get cypress to load the response body, which is in the form of a document (the new page). Anyone have any tips?
Edit: I should add that - the reason I am looking to fill the form from a POST is because the form is necessary to fill out for me to test whether certain options work or not. I have a single test that ensures the form fields and submission work as required, but for the 30+ options that need to be checked on the other side of this form, I wanted to follow Cypress' best practice of not manually filling the form every single time (they show an example with login on the website).

If you'd like to simulate a form POST navigating to a new page, you can use cy.visit() to do this! Just change your request to visit and it should work:
cy.visit({
url: "company-webpage-form-url.com",
method: "POST",
body: {
first_name: "first_name",
last_name: "last_name",
company_name: "company_name",
address1: "address1",
address2: "address2",
city: "city",
state: "NY",
zip: "13903",
country: "US",
phone_number: "607-555-5555",
phone_ext: "555",
fax_number: "fax_number",
fax_ext: "fax_ext",
email: "developer#company.com",
email_2: "developer#company.com",
user_data: "Continue"
}
});

cy.request() is intended to be used for accessing APIs to set up your tests, it does not interact with the application under test or the DOM at all.
If you want to test form submission, use cy.get(), cy.type(), and cy.click() to interact with the form like a real user would:
// fill out a form field
cy.get('input[name="first_name"]')
.type('first name here')
.get('input[name="last_name"]')
.type('last name here')
/** fill out more form fields **/
// simulate clicking submit
cy.get('input[type=submit]')
.click()

Related

reactjs pre-select radio button

I have a section in Client`s panel to agree (or not) to receive promo emails.
I set this as a choice of two radio buttons, yes or no.
Technically everything is working just fine, I can store proper information in the DB but the initial state of radio buttons is not showing. The proper value is being sent ot its 'parent' but it seems it's not passed down to the actual form.
What I would like to achieve is the initial radio button being checked (true, false):
The info is being sent (in line 52, [PROMO_CONSENT]: promo_consent):
[PROMO_CONSENT_FORM]: {
label: _t(panelMessages.promos),
data: Promo,
form: {
formId: PROMO_CONSENT_FORM,
data: {
[PROMO_CONSENT]: promo_consent
}
}
}
but then it doesn't seems to show anywhere else. To manage the states of radiobuttons, component FormChoiceGroup is being called component: FormChoiceGroup,.
[PROMO_CONSENT_FORM]: {
formId: [PROMO_CONSENT_FORM],
endpoint: '/accounts/set_promo/',
fields: [
{
name: PROMO_CONSENT,
label: _t(panelMessages.promoLabel),
component: FormChoiceGroup,
type: 'radio',
isRequired: false
}
]
}
It's a lot of code, if anyone feels like going through it, I'd appreciate it.
Block:
Panel main block
Form:
Panel form, the part of consent starts at line 96
ChoiceGroup:
Component to manage radio buttons
I had to send NOT a boolean value, but turn it into a string, to send literal "true" or "false" and not true/false.
I set up a const that checked the status of the variable and then assigned it with literal values:
const val = this.state.formData[field.name] == false ? "false" : "true";
This simple thing did the trick.

Hide email in Stripe Checkout

How to disable or hide email in checkout.js in Stripe Payment
onStripeUpdate(e) {
this.stripehandler.open({
name: "",
description: "",
panelLabel: "Pay {{amount}}",
allowRememberMe: false,
email: "", //--->how to hide this email?
});
e.preventDefault();
}
It is not possible to disable the email field entirely. You can pass a value for the email configuration option, but it must be a valid email address. If it's a valid address, the field will be replaced by a static label with the value you provided.
If you don't provide the email option, or if you provide an invalid value (such as an empty string in your example code), then the email field will still be displayed.

capturing the form data inside the Angularjs controller

I am new to Angularjs. I am having the form data which I need to make a post request to the server passing the data. I have done the UI and controller part inside the angularjs but do not know how to capture the form data. Please find the link to plnkr - Plnkr link
By clicking the add button, a new li element gets added and the same gets deleted when the minus sign is clicked. I need to get all the key value items into the below format for sending for Post request.
{
"search_params": [
{
"key": "search string",
"predicate": "matches",
"value": "choosen text"
},
{
"key": "search string",
"predicate": "not-matches",
"value": " search value"
},
{
"key": "search string",
"predicate": "matches",
"value": " search value"
}
]
}
How to capture the form data and construct the param object inside my controller inside the searchParams function inside the controller. Please let me know as I am new to Angularjs.
Updated Question
Based on the inputs, I am able to get the user details in the controller. But there are few things:
By default there will be one li element and when the user submits, the current li elements data should be captured in the controller.
Only when I add the criteria using the plus button, the array is getting updated, but the last elements data is not being updated in the model when submitted.
The same is holding good for the deleting the criteria too.
Link to updated Plunker - Plnkr Link
Expanding on #Chris Hermut and assuming you want an array of map, according to json you posted. You can do that by
var arr = [];
var form = {
name: 'asd',
surname: 'aasdasdsd',
wharever: 'asd'
}
arr.push(form);
//prentending to be another (key,value)
form.surname = 'hfg';
arr.push(form);
here's a fiddle illustrating just that.
Directives like <select>, <input> require ng-model attribute to correctly bind your input to $scope properties.
In your HTML markup you'll have to update your form elements with required attributes (like ng-model).
I would recommend (in controller/link) to use only one object for form data with different properties like
var form = {
name: '',
surname: '',
wharever: ''
}
Corresponding <input> would be ex. <input ng-model="form.name" type="text">
After you have your 'form' object populated you can do JSON.stringify(form) before your request (if your using some other content-type then application/json).

Working with angular cached array object

So I create a cached json object within an array with the following method in Angular:
$scope.saveClaim = function() {
//always set isOffset to false - empty string does not work for non-string objects in web api when field is required
$scope.claimInfo.isOffset = false;
$scope.claimsSubmit.push($scope.claimInfo);
//clears scope so form is empty
$scope.claimInfo = {
id: "",
benefitId: "",
isSecIns: "",
isNoResId: "",
expenseTypeId: "",
fromDate: "",
toDate: "",
provider: "",
who: "",
depId: "",
age: "",
amount: "",
comments: "",
isOffset: ""
};
}
The idea is the user fills out a form, and at the end either selects to add another claim or submit a claim (the object). After each time the form is filled and user selects file or add another, the form clears and the user then enters more data. The results is an array of object(s) that look like:
[
{
"id": "",
"benefitId": "",
"isSecIns": "",
"isNoResId": "",
"expenseTypeId": "",
"fromDate": "",
"toDate": "",
"provider": "",
"who": "",
"depId": "",
"age": "",
"amount": "",
"comments": "",
"isOffset": false
}
]
If more than one claim is entered, then we get multiple objects with same properties.
Each claim is then displayed with limited data in info boxes that display only 3-4 of the properties.
So I am trying to figure best way to do 3 things. First, add a unique "id" to each object. Second, if the delete icon in the info box selected, then remove that object from the array and if the "edit" icon is selected in the info box, then all the relative properties that that object in the array is populated back to the form.
Googling best tries for this, but not sure how I can work with the json objects this for for now. Can some of you help me on this?
Thanks much.
Hard to give the best way. Probably comes down to your style and preferences. But here is one way to do it, to get you going.
Define your model. It will contain the claim that is bound to the form and an array of added claims.
$scope.viewModel = {
claim: {},
claims: []
};
Add a function that assigns a claim object with default values:
var resetClaim = function() {
$scope.viewModel.claim = {
name: '',
city: ''
};
};
resetClaim();
The form elements will use ng-model:
<input type="text" model="viewModel.claim.name">
We will use ng-repeat to show the added claims:
<tr ng-repeat="claim in viewModel.claims">
Our form will have two buttons:
<button type="submit" ng-click="saveClaim()">Save Claim</button>
<button type="button" ng-click="cancel()">Cancel</button>
The cancel button will just reset the form.
The saveClaim function will look like this:
$scope.saveClaim = function() {
if (!isValidClaim()) return;
$scope.viewModel.claim.id ? updateClaim() : saveNewClaim();
resetClaim();
};
The isValidClaim function just checks if we have entered the requied fields. You could use form validation for this instead.
In this solution when saving a claim it could either be a new claim or an existing one that we have edited, and what we will do in the two cases will differ, so we need a way to tell what we are doing. Here we just check if it has an id. If it hasn't - it's a new claim. If it has, it's an existing.
To save a new claim we will do the following:
var saveNewClaim = function() {
var newClaim = angular.copy($scope.viewModel.claim);
newClaim.id = id++;
$scope.viewModel.claims.push(newClaim);
};
Note that it's important that we use for example angular.copy to create a new copy of the claim that is bound to the view. Otherwise we would just push a reference to the same object to the claims array which is not good since we want to reset one of them.
In this example id is just a variable starting at 0 that we increment each time we create a new claim.
Each element in our ng-repeat will have an edit and a remove icon:
<tr ng-repeat="claim in viewModel.claims">
<th>{{claim.id}}</th>
<td>{{claim.name}}</td>
<td>{{claim.city}}</td>
<td><i class="glyphicon glyphicon-edit" ng-click="editClaim(claim)"></i></td>
<td><i class="glyphicon glyphicon-remove" ng-click="removeClaim(claim)"></i></td>
</tr>
The removeClaim function simply takes a claim and removes it from the array:
$scope.removeClaim = function(claim) {
var index = $scope.viewModel.claims.indexOf(claim);
$scope.viewModel.claims.splice(index, 1);
};
The editClaim function will make a copy of the claim to edit and put it in the variable that is bound to the form:
$scope.editClaim = function(claim) {
$scope.viewModel.claim = angular.copy(claim);
};
You can also do the following:
$scope.viewModel.claim = claim;
And when you edit the claim in the form it will update in the ng-repeat at the same time. But then you have no good way of canceling and the save button wouldn't be needed. So it depends on how you want it to work.
If you edit the claim in the form now and save, we will come back to the saveClaim function:
$scope.saveClaim = function() {
if (!isValidClaim()) return;
$scope.viewModel.claim.id ? updateClaim() : saveNewClaim();
resetClaim();
};
This time the claim will have an id, so the updateClaim function will execute:
var updateClaim = function() {
var claim = $scope.viewModel.claims.filter(function(c) {
return c.id === $scope.viewModel.claim.id;
})[0];
angular.extend(claim, $scope.viewModel.claim);
};
It will retrieve the claim that we are editing from the claims array based on the id. We need to do this since we used angular.copy earlier and have two difference objects.
We will then use angular.extend to move all the new edited values to the claim that we pressed edit on in the ng-repeat.
Demo: http://plnkr.co/edit/yuNcZo7nUyxVsOyPTBEd?p=preview

backbone model saving, validation fails

no amount of Googling is managing to solve my confusion so I thought I'd ask the question on here.
I'm trying to save a model and make use of success/error callbacks. On the backbone documentation it states you save your model like so: model.save([attributes], [options]).
I cannot find anywhere on the documentation that tells you how to save the entire model (i.e. without specifying the attributes), but have come across this question where the second answer says to save the entire model you can do model.save({}, [options]).
However I am trying this to no avail. My code is below:
Backbone Model:
class Student extends Backbone.Model
url: ->
'/students' + (if #isNew() then '' else '/' + #id)
validation:
first_name:
required: true
last_name:
required: true
email:
required: true
pattern: 'email'
schema: ->
first_name:
type: "Text"
title: "First Name"
last_name:
type: "Text"
title: "Last Name"
email:
type: "Text"
title: "Email"
In my view I have the following function:
class Students extends CPP.Views.Base
...
saveModel = ->
console.log "model before", #model.validate()
console.log "model attrs", #model.attributes
#model.save {},
wait: true
success: (model, response) ->
notify "success", "Updated Profile"
error: (model, response) =>
console.log "model after", #model.validate()
console.log "model after is valid", #model.isValid()
console.log "response", response
notify "error", "Couldn't Update"
In the first console.log before the save I am told that the model is valid, via the means of an undefined response. If indeed I look at the model I can see that all three fields are filled in.
Similarly in the next two console logs in the error #model.validate() and #model.isValid() both return undefined and true respectively.
However the response I get from trying to save the model is Object {first_name: "First name is required", last_name: "Last name is required", email: "Email is required"}
Finally in the console.log of the models attributes I get:
Object
created_at: "2012-12-29 23:14:54"
email: "email#email.com"
first_name: "John"
id: 2
last_name: "Doe"
type: "Student"
updated_at: "2012-12-30 09:25:01"
__proto__: Object
This leads me to believe that when I passed {} to my model it was actually trying to save the attributes as nil, else why else would it have errored?
Could someone kindly point out what I'm doing wrong? I'd rather not have to pass each attribute individually to the save!
Thanks in advance
From the suggested answer by Hui Zheng I modified my controller in my server to return the student in JSON format.
However to find the real source of the problem I read the backbone documentation on saving, and found out that when wait: true is given as an option it performs the following:
if (!done && options.wait) {
this.clear(silentOptions);
this.set(current, silentOptions);
}
On further investigation into clear I found
clear: function(options) {
var attrs = {};
for (var key in this.attributes) attrs[key] = void 0;
return this.set(attrs, _.extend({}, options, {unset: true}));
},
From this it looks as if every attribute is being cleared to then be reset. However on clearing my model the validations I wrote will fail (since first_name, last_name, email are required).
On the backbone.validation documentation we are told that we can use the parameter forceUpdate: true, so I have opted to use this when saving my model. I'm going to assume for now (although this might not be good practice) that the data from the server is correct since this has been validated too.
Therefore my final code is:
saveModel = ->
#model.save {},
wait: true
forceUpdate: true
success: (model, response) ->
notify "success", "Updated Profile"
error: (model, response) ->
notify "error", "Couldn't Update"
Are you sure that the model's attributes have been set correctly before save? Even none of its attributes has been set, it may still pass validate(depending on how validate function is defined). Please try to print the model in the console to verify that. BTW, it's better to pass null instead of {} in save, this way the model's set method won't be invoked.
Updated:
According to the Backbone's source code, if passing null as the first argument of save, the model's attributes will be kept intact until the model has been saved on the server successfully. So the other possibility is that your server has succeeded in saving the model but returned a corrupted object, resulting in failure in model's set method. If you still cannot fix the problem, tracing the model.set method might help。

Resources