How to assert that image has been uploaded with Capybara? - ruby

Let's say I have a form where user can create a post with an image attached to it. I want to make sure the image attached is displayed on the next page:
visit url
fill_in the_form
click_on 'Create'
assert_selector '.post'
post = Post.first
img = page.find '.post .image'
assert_equal post.file.thumb.url, URI(img[:src]).path
But I'm told asserting against database objects in system tests is to be avoided. What do I do then?

So long as there's no "complex" file renaming happening on the backend, you already know the uploaded filename when populating the form:
fill_in the_form
Therefore, you could assert that the page contains an image with this name (perhaps using an xpath).
If there is trivial file renaming (e.g. replacing spaces with hyphens), then you could either (ideally) just choose a filename that does not change, or reproduce the renaming in your test.

Related

How to find specific text field with same id?

I am testing this one angularjs application that has
text_field(:date, :id => 'transfer_date')
Problem is that there are two text fields with this id the reason being that the first one has ng-show = externalaccount and the second has ng-show = !externalaccount.
So when I test something that is not externalaccount, I will receive an error saying that the text_field is not visible. However when I test externalaccount, the test will pass fine. I looked around and found
Page-object gem: Identifying object with same properties based on their visibility
I tried this but it there are other text_fields on the page. I need to find the first visible text_field that has id='transfer_date'. How do I set this up with pageobject or step definitions?
You can find the first visible text field with specific attributes using:
text_field(:date){ text_field_elements(:id => 'transfer_date').find(&:visible?) }
This says to find all text fields with the specific id and then from that list find the first that is visible.

Django editing a user generated object

I'm trying to figure out how to do an inline editing for a user-generated object, what the rough procedure (no code just steps), and whether or not there's some way to do this without AJAX - of course it wouldn't be "inline" anymore.
Say the user object is just 1 line of text and 1 image. Something like,
class UserObject(models.Model):
text = models.CharField()
image_path = models.CharField()
If I were to use AJAX, would this be how it'd go? (sorry this is vague, I can figure out the details just trying to see if I understand the concepts correctly)
Create a form, populate it with an instance of the object belonging to the current user
Next to the image, I'd have, say, a "remove" button, which triggers an AJAX call to a URL that's something like project/remove/ab12345 that's connected to a view that handles it.
Wait for the AJAX call to be done
Then somehow remove the image and buttons, maybe by just deleting the div that contains it all
Is that right??
Also, what if I don't want to use AJAX? Would it go something like this?
Create a form, populate it with an instance of the object belonging to the current user
Next to the image, I'd have, say, a "remove" button, which directly links to the URL that's something like project/remove/ab12345 that's connected to a view that handles it
After the view deletes the image, it goes back to the editing page, which just refreshes and the image is no longer there.
Any pointers would be greatly appreciated!! I can figure out the details of the coding, just wondering if I am getting the concepts right.
An object created by a user is really no different from one you create yourself (except you have to be suspicious of potentially malicious input!). The simplest way to be able to edit objects outside the admin interface is to use the built-in UpdateView. Similarly you can delete them with a DeleteView. If you want to restrict you can edit objects you can user the PermissionRequiredMixin from django braces.
OK since I posted this ultra-vague question I'm going to answer it.
AJAX-free:
The AJAX free version is pretty much as I described, create a view and a URL that deletes the image and goes right back to the referring page. Next going to try the AJAX version, which basically requires a view that returns some kind of a signal of failure or success.
urls.py
url('^project/remove_image/(?P<image_id_string>[0-9A-Fa-f]+)/$', pbrand.views.ProjectRemoveImageView.as_view(), name='project_remove_image'),
views.py
class ProjectRemoveImageView(View):
redirect_url = '/project/edit' # the editing url
def get(self, request, image_id_string, *args, **kwargs):
# ... some checks on permissions
image.delete()
return HttpResponseRedirect(self.redirect_url + "/" + project.id_string)
inside the template
<a class = "btn btn-default btn-sm" href="{% url 'project_remove_image' i.id_string %}" role="button">remove</a>

How do clear words/images upon form validation? (AngularJS)

Just to be clear, in the subject line, it says "words/images". The "words" part is not about clearing the words in the form, but rather the word "required". As for images, these are validation images, like the green checkmark.
I just realized that I had no clue how to clear words/images upon form completion in AngularJS. Basically, the checkmark stays, and the word "required" appears after submitting the validated form. It does so, because now the form is empty. I tried using the $setPristine in the script page,
$scope.imgHolderComments.$setPristine(true);
knowing that it would probably not work. I looked for a workaround, so I checked out this impressive thread (but realized this was more for the validation itself and not added words/images).
Reset form to pristine state (AngularJS 1.0.x)
Then I tried using this code which I had used prior to today for the pre-validation. Or if you will, for the live validation, before submitting the form. I thought I could recycle this, but no.
$scope.checkUrl = 'images/validation/y_square_trans.png';
$scope.loadUrl = 'images/validation/loading.gif';
$scope.crossUrl = 'images/validation/loading_wrong.gif';
$scope.imgHolderComments = '';
$scope.comments = "";
$scope.timeout = null;
$scope.imgHolderComments = ($scope.reviewForm.comments.$invalid) ? $scope.crossUrl : $scope.crossUrl;
That looked so dirty, but thought it could work after pushing the data in. But the image stayed there (as you can see, I tried replacing the checkmark with a cross instead, as I did not want to create a blank pic).
I also googled it, but everything I could find had to do with two things: clearing the form AND removing the coloured frame (red/green). of course, my form already does that. So, I'm still trying to find a way to remove the word "required" and the gif image in my form upon successful validation.
In any case, I created a plunker. You'll have to click on the word "Reviews" to show the form.
http://embed.plnkr.co/QZT1Jzgg9elMMRHwhYel/preview
Have a look at the directives ng-show and ng-hide.
You can set $scope.allGood = true; somewhere in your controller (after validation) and use this directive: <span ng-hide="allGood">Required</span>
Edit:
You might also want to look at the ng-class directive. Example:
<span ng-class="{green: allGood == 1}">Name:</span> and obviously style the class green to your needs.

Verify for the text to be present in an overlay using watir-webdriver

I have an overlay form where i create an user for our application. After giving the details in the text fields i click on save and try to capture the Saved Successfully Text which appears for about a second on the overlay. But i am unable to do so as i get an error saying "Element is no longer attached to the DOM (Selenium::WebDriver::Error::StaleElementReferenceError)".I have used the below code:
if($browser.div(:class=>"validation-summary-valid").exists?)
message=$browser.div(:class=>"validation-summary-valid").li.text
if(message=="Saved Sucessfully")
puts("Save action complete")
else
fail("fail")
end
end
in capybara i would scope the code using ( within ) to the message element in the Dom then use have_content
within('#Browser div')do
page.should have_content('Saved Successfully')
end
hope this will help to try something similar in watir
What I understood from the situation is, the moment you click on save a transient message appears on the UI and a check needs to be performed.
The below approach should work fine in this case,
# the browser waits for 20 s until the element is present(exists+visible) on the UI
$browser.div(:class=>"validation-summary-valid").wait_until_present(20).li.text

Rails form, load new record page rather than redirecting to it

I'm creating my first app in rails.
Basically, I have a new customer form, normally when you enter a new customer you are redirected to the record you created.
However as I am loading all my pages via ajax I want to load the new record in rather than re-direct to it.
I already have the form firing via ajax, I just need to know how I can access the new record URL to load it into my container.
Hope that makes sense. Thanks in advance!
You can add an option :remote => true to your form_for helper method, so that instead of page redirect the form gets posted via ajax.
For Ex :
<%= form_for(#post, :remote => true) do |f| %>
...
<% end %>
Then create a new template named create.js.erb which will get rendered after create method has been executed.
In this template, you can display the details of the new record you created.
For Ex :
$('some_element').replaceWith('<%=#posts.name %>');
Edit: You mentioned using load in your javascript. I would generally avoid this as you're making two round trips to the server. 1) Create 2) Get html to load into a div. Additionally, if there is an error that happens, it will be more difficult to catch and do the "good thing", whatever that might be.
Once you've added the remote: true option to your form, you need to deal with what happens on the server side of things.
Assuming your using REST, you'll have a controller with a create action in it. Normally, this method is creating the item and then subsequently returning the HTML for the create page. Instead of this, we need to create a javascript view for the action. This will tell the calling page what to when this action is hit.
Let's assume your form has a div called "new_record_form" and that you have another div called "new_records". You'll want to blank out the form elements in the form, effectively resetting it. You'll also want to add the new record to the "new_records" div.
To add the record to the new records div, you might do something like this.
$("#new_records").append("#{#new_record.name}");
When you submit the form, you should see this added. One great way to debug issues is to use the inspector. If you're in chrome, right click anywhere, inspect element and select network. Do this prior to form submission. You'll be able to see the ajax call and the response. Your logs will also be helpful.
Don't forget to blank out the form as well.
Note: You mentioned all your pages are ajax, but I highly suggest you evaluate if this makes 100% sense due to the various issues that result. Not saying this is the best article on the subject but might be worth a read.

Resources