I've checked all the SO questions I can find related to this issue.
The rest of the form works and the test passes if I remove the validation that a PlanTemplate must have a Category.
I have created a Category with a name of 'Planets' in the Test script elsewhere.
I've just got Capybara screenshot working and have now updated it to include the text - it doesn't seem to be appearing in Capybara, but is appearing in local - I can't tell from my code why it would be hidden?
Full error:
1) PlanTemplate can be created by admin admin can create new plan
Failure/Error: expect(page).to have_content('Planets')
expected to find text "Planets" in "window.addEventListener(\"load\", function(){ window.cookieconsent.initialise({ \"palette\": { \"popup\": { \"background\": \"#252e39\" }, \"button\": { \"background\": \"#428bca\" } }, \"theme\": \"classic\", \"content\": { \"href\": \"Toggle navigation Communities Communities People Mates Weekly Activity view Recent Activity list view All Mate Goals Manage Mates Plans Goals All Goals Manage My Goals New Goal Admin Admin Account Profile Email Settings Manage Mates My Interests Log out New Plan Template Plan name: Plan description: What additional information is required? (optional) What is the delivery medium & requirements of plan? (optional) What should you do once the plan is complete? (optional) How many hours per week does this goal take to complete? How many days in total does this take to complete? /7 for weeks. This will be used to calculate start times What level of user is this plan for? beginner novice intermediate experienced expert What category is this goal in? Feed My Goals My Mates All goals Home Sign Up Log in Browse Users Feedback © Browse Goals Privacy TOS Cookies window.setTimeout(function() { $(\".alert\").fadeTo(500, 0).slideUp(500, function() { $(this).remove(); }); }, 6000);"
Things I have tried to date are commented out below (have also tried others and removed them):
let(:category) { create :category, name: 'Planets', id: 99 }
scenario 'admin can create new plan' do
login_as(create(:admin))
visit("/plans/new")
find('#plan-name').set('A test plan name')
find('#plan-desc').set('A test plan description, I think these need to be longish')
find('#plan-weekly-hours').set(1)
# page.check('Planets')
# find("label[for='Planets']").click
# find_field(['plan_template_category_ids_1']).check
# find('.checkbox').check
# check('Planets')
# find(:label, 'Planets').click
# check('.checkbox', allow_label_click: true)
# find(:label, 'plan_template_category_ids_1').click
# find('#plan_template_category_ids_1', visible: false).trigger('click')
find('#plan-days-to-complete').set(35)
find('#submit-new-plan').click
expect(page).to have_content('Plan template was successfully created.')
expect(page).to have_content('A test plan description, I think these need to be longish')
end
I have also added to rails_helper:
Capybara.automatic_label_click = true
Capybara.ignore_hidden_elements = false
The form partial code in question:
<%= form.label "What category is this goal in?" %><br>
<%= form.collection_check_boxes :category_ids, Category.all, :id, :name do |cb| %>
<% cb.label(class: "checkbox-inline input_checkbox") {cb.check_box(class: "checkbox") + cb.text} %>
<% end %>
The html snapshot from Capybara screenshot - have to paste it as an image as SO doesn't like it in code.
How it displays in the DEV browser *not test:
<div class="col-md-12 goalform">
<label>What category is this goal in?</label><br>
<input type="hidden" name="plan_template[category_ids][]" value=""><label class="checkbox-inline input_checkbox" for="plan_template_category_ids_2">
<input class="checkbox" type="checkbox" value="2" name="plan_template[category_ids][]" id="plan_template_category_ids_2">Dream Chasing</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_1"><input class="checkbox" type="checkbox" value="1" name="plan_template[category_ids][]" id="plan_template_category_ids_1">Weightloss</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_4"><input class="checkbox" type="checkbox" value="4" name="plan_template[category_ids][]" id="plan_template_category_ids_4">Productivity</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_11"><input class="checkbox" type="checkbox" value="11" name="plan_template[category_ids][]" id="plan_template_category_ids_11">Popular</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_3"><input class="checkbox" type="checkbox" value="3" name="plan_template[category_ids][]" id="plan_template_category_ids_3">Fitness</label>
<label class="checkbox-inline input_checkbox" for="plan_template_category_ids_12"><input class="checkbox" type="checkbox" value="12" name="plan_template[category_ids][]" id="plan_template_category_ids_12">Health</label>
</div>
This could've been diagnosed by using a debugging tool (e.g. using the pry gem and adding 'binding.pry' inside of the test (and before the failing expect line) which would have shown me that the Categories were not being generated correctly due to the lazy loading of the 'let'
First, nowhere in the HTML you've shown is 'Planets' mentioned, so it's not surprising that Capybara can't find the checkbox. If you're trying to select the checkbox with a value of 1 then its label contents is 'Weightloss' according to the HTML.
Second, assuming the fields you're using set on are input elements, stop doing find(...).set(...) and just use fill_in.
Those changes would make your test code
scenario 'admin can create new plan' do
login_as(create(:admin))
visit("/plans/new")
fill_in('plan-name', with: 'A test plan name')
fill_in('plan-desc', with: 'A test plan description, I think these need to be longish')
find('#plan-weekly-hours').set(1) # I don't know what type of element this is? If a select use `choose`
# check accepts the id, unique name, or associated label text as a locator
check('Weightloss')
find('#plan-days-to-complete').set(35) # same as above
click('submit-new-plan')
expect(page).to have_content('Plan template was successfully created.')
expect(page).to have_content('A test plan description, I think these need to be longish')
end
If that doesn't work for you, please copy the full error you get and add it to your question.
Additionally, never set Capybara.ignore_hidden_elements = false when writing tests. Doing so makes your tests invalid, from a users perspective, and basically pointless. The only time that setting may make sense is when web scraping.
The error I was having was because I was using let(:category) { create :category, name: 'Planets', id: 99 } & let(:category) { create :category } as a way to create Categories, but they weren't usable in the test.
I created them without using a factory inside the test and it's working fine.
Category.create(name:" Category 1")
I also thought my PlanTemplate factory would create a category that could be used as I have inside of the factory categories { [create(:category)] }, but that isn't usable the way I expected.
Related
I am passing an array of category names to my template and iterating through this to populate the value of checkbox elements.
<input id={{"category"|add:escaped_cat_name}} type="checkbox" name="category" value={{category_name}}>
Some of my category names contain spaces and ampersands but Django ignores these so "Fun & Gaming" becomes "Fun":
category_name: Fun & Gaming
<input id="categoryFun" type="checkbox name="category" value="Fun">
category_name: Business Expenses
<input id="categoryBusiness" type="checkbox name="category" value="Business">
In these examples, I would like the interpreted value to read the 'Fun & Gamingand 'Business Expenses
If I add a safe tag to the value it renders the value name as "Fun" & gaming, with the second part of the string still outside the value name.
I have tried writing a custom tag to deal with this behaviour but it seems as though this is Django's default and I can't figure out how to disable it.
Any help with this would be much appreciated.
This is my answer, and I know this answer will not be nice for you: you should to learn a bit more about django, and about django forms:
Handling and processing inputs by hand is an anti-pattern. The right way is to create a Form object and render the form in template.
You can learn about forms at Working with forms djag's documentation.
Let me copy here a sample for checkboxes:
from django import forms
BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
FAVORITE_COLORS_CHOICES = (
('blue', 'Blue'),
('green', 'Green'),
('black', 'Black'),
)
class SimpleForm(forms.Form):
birth_year = forms.DateField(
widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICES))
favorite_colors = forms.MultipleChoiceField(
required=False,
widget=forms.CheckboxSelectMultiple,
choices=FAVORITE_COLORS_CHOICES,
)
So then, we do not have to re-invent the wheel and just use Forms.
For your data:
>>> FAVORITE_COLORS_CHOICES = (
... ('categoryFun', 'Fun & Gaming'),
... ('categoryBusiness', 'Business Expenses'),
... )
>>>
>>> class SimpleForm(forms.Form):
... favorite_colors = forms.MultipleChoiceField(
... required=False,
... widget=forms.CheckboxSelectMultiple,
... choices=FAVORITE_COLORS_CHOICES,
... )
...
>>> str( SimpleForm() )
'<tr><th><label>Favorite colors:</label></th>
<td>
<ul id="id_favorite_colors">
<li>
<label for="id_favorite_colors_0">
<input type="checkbox" name="favorite_colors"
value="categoryFun" id="id_favorite_colors_0" />
Fun & Gaming
</label>
</li>...'
This is a project for school, and so far I only know Ruby, Sinatra, HTML & CSS. I've looked online but only came across PHP and Javascript insturctions (which I'm not supposed to use yet).
I have a form for checkboxes, and although I can check off more than one, only one of the checkbox values gets posted. How do I get it so all of the checked off ones are passed?
Also, 'abbreviation' comes out nil for each state. Am I doing this wrong in my form? And if anything it just uses ONE of the abbreviations and adds it to every state. Finally, how do I make it so the checkboxes remain checked for the states users have already added in the past?
Here's some code:
<form method="POST" action="/states">
<h3>Northeast:</h3> <br>
<input type="checkbox" name="state_name" value="Connecticut" name="abbreviation" value="CT"> Connecticut<br>
<input type="checkbox" name="state_name" value="Deleware" name="abbreviation" value="DE"> Deleware <br>
<input type="checkbox" name="state_name" value="Maine" name="abbreviation" value="ME"> Maine<br>
<input type="checkbox" name="state_name" value="Maryland" name="abbreviation" value="MD"> Maryland<br>
require 'pry'
class StatesController < ApplicationController
get '/states' do
if logged_in?
#user = current_user
#states = current_user.states
erb :'states/states'
else
redirect to '/login'
end
end
get '/states/new' do
if logged_in?
erb :'/states/create'
else
redirect to '/login'
end
end
post '/states' do
if logged_in? && params[:state_name] != ""
#state = State.create(:state_name => params[:state_name], :abbreviation => params[:abbreviation])
# binding.pry
#state.users << current_user
if #state.errors.any?
"Error, try again"
else
redirect "/states"
end
else
redirect to "/states/new"
end
end
get '/states/:id' do
if logged_in?
#state = State.find_by_id(params[:id])
if #state != nil
erb :'/states/show'
else
redirect to '/states'
end
else
redirect to '/login'
end
end
get '/states/:id/edit' do
if logged_in?
#state = State.find_by_id(params[:id])
if #state && current_user
erb :'states/edit'
else
redirect to '/states'
end
else
redirect to '/login'
end
end
patch '/states/:id' do
#state = State.find_by_id(params[:id])
if params[:state_name] != "" && #state.update(:state_name => params[:state][:state_name])
#state.update(:state_name => params[:state][:state_name])
redirect to "/states/#{#state.id}"
else
redirect to "/states/#{#state.id}/edit"
end
end
delete '/states/:id/delete' do
if logged_in?
#state = State.find_by_id(params[:id])
if #state && current_user
#state.destroy
end
redirect to '/states'
else
redirect to '/login'
end
end
end
You need to append [] to your name attribute to support array value i.e. more than one checked values:
<input type="checkbox" name="state_name[]" value="Connecticut"> Connecticut<br>
<input type="checkbox" name="state_name[]" value="Deleware"> Deleware<br>
<input type="checkbox" name="state_name[]" value="Maine"> Maine<br>
<input type="checkbox" name="state_name[]" value="Maryland"> Maryland<br>
Now on your server, you will see the checked values as state_name: ['Connecticut', 'Maine', ...].
Note: You have specified the name and value attributes twice for each checkbox. I am using the first one.
I have always known that in html's forms the ids and names must be unique, unless you are working with radio buttons or in this case checkboxes. Your code seems to be a bit overmade, meaning, you have 2 names and 2 values on each checkbox, and just the last one of both of them will work, so instead of this:
<input type="checkbox" name="state_name" value="Connecticut" name="abbreviation" value="CT"> Connecticut<br>
Try just having one of those attributes:
<input type="checkbox" name="abbreviation" value="CT"> Connecticut<br />
Usually, in for php we would append a '[]' at the end of the name, and you will recieve an array of the values that were checked. But since you are using ruby, which I am a bit familiar to, I've seen the ruby developers like to be a bit more expressive in their code, and sometimes they do things like this:
<input type="checkbox" name="abbreviation[city_1]" value="CT">Connecticut<br />
<input type="checkbox" name="abbreviation[city_2]" value="DE">Delaware<br />
And so on.
You can see an example of it, here, even though I believe you can do it with the empty '[]', but just so you know I've been told that's the php-ish way to do it.
Cheers!
Use only one name and value per input, like:
<input type="checkbox" name="state_name" value="CT"> Connecticut<br><input type="checkbox" name="state_name" value="DE"> Deleware <br><input type="checkbox" name="state_name" value="ME"> Maine<br><input type="checkbox" name="state_name" value="MD"> Maryland
Check your POST array for state_name and their should be an array of all check-boxes selected.
I have a question on a form with 15 checkboxes. I would like to take all items that are "checked" and combine the labels into a string that is entered into my database. I would rather not make a separate DB entry for every checkbox and selecting individuals on this field is not important, I simply need to record the response.
Is there an easy way to combine these and make that the value entered into the DB?
The following displays right, but I'm not sure how to actually submit the correct information.
HTML
<div class="form-inline">
<div class="form-group col-sm-12" style="margin-bottom: 10px">
<div class="field">
<div class="input check_boxes optional remote_issues">
<label style="display:block; padding-bottom: 1em" class="check_boxes optional">Which of the following pevent you from reaching your desired weight?</label>
<% boxes = ["Lack of Knowledge", "Physical Limitations", "Lack of Social Support", "Hunger", "Cravings", "Frequent Travel", "Social Events", "No Time", "Erratic schedule", "Finances", "Family Habits", "Stress", "Hormonal Issues", "Medications", "Illness", "Poor Sleep", "Health Conditions", "Age", "Slow Metabolism", "Alcohol", "No Exercise", "Emotional Eating", "Food Preferences", "Other"] %>
<% boxes.each do |box| %>
<%= render "health_intake", name: box %>
<% end %>
</div>
</div>
</div>
</div>
_health_intake Partial
<span class="checkbox col-xs-3">
<label for="remote_issues_lack_of_knowledge">
<input type="checkbox" value=#{<%= name %>} name="remote[issues][]" class="check_boxes optional" style="margin-right:1em"><%= name %>
</label>
</span>
Just join the values in your controller for that parameter array:
values = params[:issues].join
Furthermore, add a comma or something to the join method so you can split them later:
values = params[:issues].join(',')
You could have an Issue model where you store "Lack of Knowledge", etc. Then a join model FooIssue (assuming Foo is the model in your form above).
FooIssue belongs_to :foo and belongs_to :issue. Foo has_many :foo_issues and has_many :issues, through: :foo_issues. Foo accepts_nested_attributes_for :foo_issues.
In your form you display all issues as checkboxes. Issues that are checked get passed back to Foo as nested foo_issues attributes.
With this approach, rather than a serialized approach - which I think is where you were going - you can get better statistics around issues. "What percentage of people say 'Frequent Travel' is an issue?" Things like that.
I'm currently developing an app with Sinatra, ActiveRecord and MySQL. I'm working on the sign up form, which looks like this:
app.rb:
post '/signup' do
password_salt = BCrypt::Engine.generate_salt
password_hash = BCrypt::Engine.hash_secret(params[:password], password_salt)
#usuarios = User.new(params[:nombre], params[:cedula], password_hash, "admin")
if #usuarios.save
redirect './signup', :notice => "Usuario creado exitosamente."
else
redirect './signup', :error => "Ha ocurrido un error, intente nuevamente."
end
end
And the view looks like this, signup.erb:
<form id="registro" action="/signup" method="POST">
<fieldset>
<legend>Ingrese sus datos</legend>
<label>Nombre
<input type="text" name="nombre">
</label>
<label>Cédula
<input type="text" maxlength="10" name="cedula">
</label>
<label>Contraseña
<input type="password" name="password">
</label>
<!-- TO-DO:
Dropdown list con los diferentes tipos de usuarios, i.e.: admin, secretario, etc.
-->
<input type="submit" id="registerButton" class="button small">Finalizar registro</a>
</fieldset>
</form>
Whenever I try to create a new user, I get the following error:
ArgumentError - wrong number of arguments (4 for 0..2)
Considering that the table I'm trying to insert the values has 4 columns, I don't understand why I'm getting this error.
Any insight to help me solve this inconvenience would be greatly appreciated!
Thanks in advance.
ActiveRecord::new method allows only 2 parameters as arguments, it should be a hash. fix:
User.new(params[:nombre], params[:cedula], password_hash, "admin")
to:
User.new(nombre: params[:nombre], cedula: params[:cedula], password: password_hash, role: "admin")
You should always check the documentation, in 99% cases you can find a problem:
New objects can be instantiated as either empty (pass no construction
parameter) or pre-set with attributes but not yet saved (pass a hash
with key names matching the associated table column names). In both
instances, valid attribute keys are determined by the column names of
the associated table – hence you can’t have attributes that aren’t
part of the table columns.
new(attributes = nil, options = {})
Examples:
# Instantiates a single new object
User.new(:first_name => 'Jamie')
# Instantiates a single new object using the :admin mass-assignment security role
User.new({ :first_name => 'Jamie', :is_admin => true }, :as => :admin)
# Instantiates a single new object bypassing mass-assignment security
User.new({ :first_name => 'Jamie', :is_admin => true }, :without_protection => true)
I am creating a site in which I utilize partial views to display various bits of data about a single Model. Here is a bit of the HTML. (Note, all of these are contained within a single form and the Index page that these partials are rendered in is strongly typed to the main model. The main model contains various lists of data.)
<div id="tab1"><% Html.RenderPartial("Tab1", Model); %></div>
<div id="tab2"><% Html.RenderPartial("Tab2", Model.AnItemList1.FirstOrDefault<AnItemList1>()); %></div>
<div id="tab3"><% Html.RenderPartial("Tab3", Model.AnItemList2.FirstOrDefault()); %></div>
Here is ONE of the partial views headers (for 'tab2'):
<%# Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AnItem1>" %>
The pages display correctly. The issue is that, when I enter data into the various parts of the partial pages and then submit the entire form (via POST), the data is not making it back to my data store (MSSQL) - but this only happens for any of the list items (that are contained within the Model). The first partial page does properly have its data set within the data store.
What am I doing wrong here? Should I only be passing the model to Html.RenderPartial and then get the specific model I need on the partial page? Should I pass the entire list and then get the first (right now, I only care about the first item in the list - that will EVENTUALLY change, but not any time soon).
Suggestions or thoughts appreciated.
Update: Here is how I accessing the properties on the partial views.
<div class="data-group">
<%: Html.CheckBoxFor(model => model.Property1) %>
<%: Html.LabelFor(model => model.Property1) %>
</div>
Update 2: Per request...
Controller Action (ScenarioController):
public ActionResult Index(int id = 0)
{
if (id == 0)
{
SavedScenario scenario = new SavedScenario();
scenario.AnItemList1.Add(new AnItem1());
scenario.AnItemList2.Add(new AnItem2());
return View("Index", scenario);
}
else
{
SavedScenario scenario = repository.GetScenario(id);
if (scenario == null)
return View("NotFound");
else
return View("Index", scenario);
}
}
[HttpPost]
public ActionResult Index(SavedScenario scenario)
{
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
Rendered HTML (I can only include parts of it - this is a small sample of what is in the form):
<form action="/Scenario" id="form0" method="post">
<!-- This is the one that works - the basic Scenario. Top level. -->
<fieldset>
<legend>Scenario Information</legend>
<div class="data-group">
<div class="editor-label">
<label for="ScenarioName">Scenario Name</label>
</div>
<div class="option1">
<input class="wide" id="ScenarioName" name="ScenarioName" type="text" value="" />
</div>
<div class="validation">
<div><span class="field-validation-valid" id="ScenarioName_validationMessage"></span></div>
</div>
</div>
</fieldset>
<!-- This does not work or get submitted (as far as I can tell). -->
<div id="tab2">
<fieldset>
<legend>Tab2</legend>
<div class="data-group">
<input id="Property1" name="Property1" type="checkbox" value="true" /><input name="Property1" type="hidden" value="false" />
<label for="Property1" />
</div>
</div>
</fieldset>
</form>
My apologies for having to keep this so generic.
Hard to guess from this much code. However you should make sure that all properties of your models have the same prefix when they are posted back to the server
Edit: form field names should match property names of your model to correctly bind all values. You have two fields with the same name that you can bind in following way
[HttpPost]
public ActionResult Index(SavedScenario scenario, List<bool> Property1)
{
// here you can do with values coming in property1
if (ModelState.IsValid && TryUpdateModel(scenario, "SaveScenario"))
{
repository.Add(scenario);
repository.Save();
}
return View(scenario);
}
It might be issue with naming the fields on your partial forms. Try naming the fields on your partial views by prefixing it with the name of the Model passed into it...like 'AnItemList1.name' instead of just 'name'..I am just guessing here though...but that's what I did sometimes to fix the problem when I was getting values as null..