Building Mortimer, day 2

This is rapidly turning into a series of posts on building Mortimer (see what happens the day after) – learn about the beginning up until this moment, and Vol 4 reboot, day 1.

Day 2

I was cheating – day 1 was not nearly finished and I felt having at least one more thing ...

Punches

Being anxious getting to the core of it all – the punching in and out. So what better way to finish a rather long and bumpy day than solving yet another use case

as a user I will punch in and out

Not the greatest use case of the century but anyways:

rails g scaffold Punch user:references...

Immediately I made an important change to the punches#create method:


  # POST /punches or /punches.json
  def create
    @punch = Punch.new(punch_params)
    @punch.user = current_user
    @punch.punched_at = DateTime.now
    ...

No punch should ever be "punched" by anybody else – at least that's what I know "right now" 😉

TDD'ing from there presented me with day 1's last bump:

Rails 7 errors on unsafe redirects

Not sure why this error is raised but the remedy is surmountable:

 def destroy
    @punch.destroy!

    respond_to do |format|
      format.html { 
        redirect_to punches_url, 
          allow_other_host: true, 
          notice: "Punch was successfully destroyed." 
        }
      format.json { head :no_content }
    end
  end

(and likewise for #create and #update)

Teams

-- meanwhile far away in a distant galaxy...duuut, duuut!! I almost jumped out of bed, full of excitement for what this day will bring.

rails g scaffold Team name, email

With that out of the way I could focus on the next use case being

as a user I will be assigned to a team

Needless to say that not all businesses will operate with the notion of teams – some may just consider the company, the team! In those situations what are we to do? I went with the null: true meaning that I will not have SQLite raise hell if I do not provide a team for a new user. Here is a little post about this 'not null' thing.

rails g migration add_team_id_to_user team:references

Make the user belong_to a team with

class User < ApplicationRecord
  belongs_to :team, optional: true
end

and make sure the team knows about the users

class Team < ApplicationRecord
  has_many :users, dependent: :destroy
end

Having 'said' that I instantly realized that there was foul play somewhere in here! Deleting a team would rip us of all signed up users assigned to this team! Making a mental note about that would not cut it – I had to, ferociously, attack this use case while still fresh off the bat!

Deleting record(s)

Coming from vol 1-3 I knew how to handle this one! Add a modal_control, start hacking and by no time I'd be right back in the code soup that I knew so well!

But using the 'system prompt' felt somewhat cheap - but that would be a task for some other time to do pricey stuff I decided, the sentiment being that I had taken no theme, no CSS framework into consideration thus any worries about a sordid modal versus system prompt were idle.

Making sure that the delete button would only be available if a team had no assigned users felt like the right way to go! So the use case was

as a user I will only be able to delete teams with no assigned users

Now the job was manageable!

My tests came out like this

require "test_helper"

class TeamsControllerTest < ActionDispatch::IntegrationTest

  ...
  
  test "should destroy team with no attached users" do
    assert_difference("Team.count", -1) do
      delete team_url(@team)
    end

    assert_redirected_to teams_url
  end

  test "should not destroy team with attached users" do
    assert_difference("Team.count", 0) do
      @team2 = teams(:two)
      delete team_url(@team2)
    end

    assert_redirected_to teams_url
  end
end

Making them red was fixed in the users.yml with

one: 
  ...
  team: two

two: 
  ...
  team:

Making them green was just about as easy

class TeamsController < ApplicationController
  before_action :authenticate_user!
  ...
  
  # DELETE /teams/1 or /teams/1.json
  def destroy
    options = { allow_other_host: true }
    result = @team.destroy! if @team.users.empty?
    if result
      options[:notice] = "Team was successfully destroyed."
    else
      options[:alert] = "Team has users attached and cannot be destroyed."
    end

    respond_to do |format|
      format.html { redirect_to teams_url, options }
      format.json { head :no_content }
    end
  end
  
  ...
end

and with that, I reckoned teams to be done with – for the time being at least 😉

green tests are a shear joy!

Wait! What about the system test? Yeah - right - okay! But it's not pretty. I hit one more bump! Setting up a system test for teams was practically done for me.

require "application_system_test_case"

class TeamsTest < ApplicationSystemTestCase
  setup do
    sign_in users(:one)
    @team = teams(:one)
  end

  test "visiting the index" do
    visit teams_url
    assert_selector "h1", text: "Teams"
  end

  test "should create team" do
    visit teams_url
    click_on "New team"

    fill_in "Email", with: @team.email
    fill_in "Name", with: @team.name
    click_on "Create Team"

    assert_text "Team was successfully created"
    click_on "Back"
  end

  test "should update Team" do
    visit team_url(@team)
    click_on "Edit this team", match: :first

    fill_in "Email", with: @team.email
    fill_in "Name", with: @team.name
    click_on "Update Team"

    assert_text "Team was successfully updated"
    click_on "Back"
  end

  test "should destroy Team" do
    visit team_url(@team)
    click_on "Destroy this team", match: :first

    assert_text "Team was successfully destroyed"
  end
end

But creating a team yelled

at me the very second I hit [Enter] on rails test:system - and my reaction was WTF? 😧

Out of the box Rails comes with Selenium enabled as the 'driver' for a "headless" browser. Apparently browser testing in Rails has come a long way since Selinium had its hay-days. Here is a post on changing that to Cuprite and here is another on focused with Minitest (the Rails standard test framework).

None of them, however, dives straight into the reason why Capybara somehow "forgets" about the logged_in_user; 🤔

I posted in the discussions tab on the Capybara GitHub repo – but no word yet.

Mortimer was (close to) feature complete (if looking at it from the basic functionality side of things) and I was happy to commit, and tag it 0.2.0_teams – what a day!