# How to use and test an AJAX request in Rails

### What's AJAX?

AJAX (Asynchronous Javascript and XML) is a kind of technique that can send HTTP requests and exchange data with a server by Javascript. It's an important and practical technique to enhance the user experience, especially when we want to achieve the effect that updating the partial content and wouldn't like to reload the whole page.

### How to send an AJAX request in Rails

Rails has built-in [Unobtrusive JavaScript](https://github.com/rails/jquery-ujs) that has required in the `application.js` file.

```javascript
//app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs
```

Thanks to Unobtrusive JavaScript! We can just put something like `data-*` on the HTML DOM and some simple dynamic things can be implemented. (Read more about [the list of data attributes](https://github.com/rails/jquery-ujs/wiki/Unobtrusive-scripting-support-for-jQuery-%28list-of-data-attributes%29))

The most common one is `data-confirm`. After adding `data-confirm` like the example below, when a user clicks the `Delete`, a pop-up dialog with 'Are you sure to delete this event' texts will show up.

```ruby
<%= link_to(
  'Delete', 
  event_path(event), 
  method: :delete, 
  data: { confirm: 'Are you sure to delete this event?' } 
 )
%>
```

We can implement an AJAX request by the data attributes that Unobtrusive JavaScript has provided, too. Just like the example above, we just need to add `data-remote` on the target HTML DOM. After that, Rails will help us use AJAX to send the HTTP request.

For instance,

```ruby
<%= link_to 'Show the article', article_path(article), remote: true %>
```

Rails helper will generate HTML tags like:

```html
<a href="/artciles/1" data-remote="true">an article</a>
```

### How to test an AJAX request with RSpec

For a general GET request, the test subject might look like this:

```ruby
RSpec.describe ArticlesController, type: :controller do
  describe 'GET #show' do
    subject { get :show, params: params }
    
    let(:params) { { id: article.id } }
    
    it { is_expected.to have_http_status(:ok) }
    it { is_expected.to render_template(:show) }
  end
end
```

To make an AJAX request to the show action using GET,

we have to add `xhr: true` to the subject (Rails version 5.0+):

```ruby
RSpec.describe ArticlesController, type: :controller do
  describe 'GET #show' do
    subject { get :show, params: params, xhr: true }
    
    #...
  end
end
```

According to Rails [ActionController::TestCase](https://github.com/rails/rails/blob/main/actionpack/lib/action_controller/test_case.rb#L569),

```ruby
# rails/actionpack/lib/action_controller/test_case.rb:569

if xhr
  @request.set_header "HTTP_X_REQUESTED_WITH", "XMLHttpRequest"
  @request.fetch_header("HTTP_ACCEPT") do |k|
  @request.set_header k, [Mime[:js], Mime[:html], Mime[:xml], "text/xml", "*/*"].join(", ")
  end
end
```

I'm curious if it is an AJAX request now, so I try to check it out by calling `request.headers["X-Requested-With"]` and `request.xhr?`.

```ruby
# rails console

  def show
=>  binding.pry
    #...
  end
  
 $ request.headers["X-Requested-With"]
 #=> "XMLHttpRequest"
 
 $ request.xhr?
 #=> 0
 # means the value of headers["X-Requested-With"] is totally match "XMLHttpRequest" string
```

The answer is yes! It's an AJAX request now.

The process of AJAX interacting with the server is by an `XMLHttpRequest` object. After setting `xhr: true` means the “X-Requested-With” header has contained “XMLHttpRequest” and tells the client to send the request by AJAX.

---

#### Reference

* [https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX/Getting\_Started](https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX/Getting_Started?ref=lynnbright.com)
    
* [https://guides.rubyonrails.org/working\_with\_javascript\_in\_rails.html](https://guides.rubyonrails.org/working_with_javascript_in_rails.html?ref=lynnbright.com)
    
* [https://github.com/rails/jquery-ujs/wiki/Unobtrusive-scripting-support-for-jQuery-%28list-of-data-attributes](https://github.com/rails/jquery-ujs/wiki/Unobtrusive-scripting-support-for-jQuery-%28list-of-data-attributes%29?ref=lynnbright.com)
    
* [https://ihower.tw/rails/ajax.html](https://ihower.tw/rails/ajax.html?ref=lynnbright.com)
    
* [https://newbedev.com/rails-testing-xhr-with-post-data](https://newbedev.com/rails-testing-xhr-with-post-data?ref=lynnbright.com)
    
* [https://github.com/rails/rails/blob/main/actionpack/lib/action\_controller/test\_case.rb#L569](https://github.com/rails/rails/blob/main/actionpack/lib/action_controller/test_case.rb?ref=lynnbright.com#L569)
