I am still new to Sinatra and I have built my app all based in json, with no views. Now I would like to have the same behaviour but rendering the results on a view. When I was just returning json, my tests all worked. Now I am trying to introduce the erb templates in the routes, and my tests crash, and the variables in the route method don't get passed to the view either.
What am I doing wrong?
Here is the code of the tests:
main_spec.rb:
describe "when a player joins the game" do
it "welcomes the player" do
post "/join", "data" => '{"name": "Jon"}'
response = {status: Messages::JOIN_SUCCESS}
expect_response_to_eq(response)
end
it "sends an error message if no name is sent" do
post "/join", "data" => '{}'
response = {status: Messages::JOIN_FAILURE}
expect_response_to_eq(response)
end
it "sends an error message if player could not join the game" do
fill_the_game
post "/join", "data" => '{"name": "Jon"}'
response = {status: Messages::JOIN_FAILURE}
expect_response_to_eq(response)
end
it "returns an empty response if no data is sent" do
post "/join"
expect_response_to_eq({})
end
def expect_response_to_eq(response)
expect(last_response).to be_ok
expect(JSON.parse(last_response.body, symbolize_names: true)).to eq(response)
end
def fill_the_game
server.join_game("Jane")
server.join_game("Joe")
server.join_game("Moe")
server.join_game("May")
end
end
where Messages
is a module that contains string messages for the game.
My controller initially looked like this, it just returned the response in json format:
main.rb
post "/join" do
response = helper.join_response(params)
@title = Messages::JOIN_TITLE
response.to_json
end
The helper is a class where I extracted all the business logic so that the controller only has to deal with HTTP requests. I use dependency injection to pass the helper to the main controller, so that it is easier to test.
So up to here, if I run the tests, they are green. But now I want to render the results of the response in the views through erb, while still returning the json. So I added a test like this:
main_spec.rb:
it "renders the join page" do
h = {'Content-Type' => 'text/html'}
post "/join", "data" => '{"name": "Jon"}', "headers" => h
expect(last_response).to be_ok
expect(last_response.body).to include(Messages::JOIN_TITLE)
end
And then modified the join router to make the test pass:
main.rb:
post "/join", :provides => ['html', 'json'] do
response = helper.join_response(params)
@title = Messages::JOIN_TITLE
@r_status = response[:status]
respond_to do |format|
format.html { erb :join }
format.json { response.to_json }
end
response.to_json
end
This broke all my tests. So I tried something else:
main.rb:
post "/join", :provides => ['html', 'json'] do
response = helper.join_response(params)
@title = Messages::JOIN_TITLE
@r_status = response[:status]
request.accept.each do |type|
case type.to_s
when 'text/html'
halt erb :join
when 'text/json'
halt response.to_json
end
end
end
Brokes everything as well.
If I add a line at the end, response.to_json
, just before closing the method, my tests pass except for the last line expect(last_response.body).to include(Messages::JOIN_TITLE)
. Indeed when I load the page in a browser, the @title
seems to be sent to the page but not the @r_status
for some reason. In the erb view, I have <p><%= @r_status %></p>
, so it should show up. The title is rendered in the layout erb, as <h1><%= @title %></h1>
.
I have printed the value of @r_status
and it is correct, but if I print stuff from inside the when
blocks, it's like it never hits those.
What is it that I am doing wrong? Why is the @r_status not rendered in the view and why aren't the when
blocks hit?