From d1e6fb1e3959e0357d966bd50db84339246518d2 Mon Sep 17 00:00:00 2001 From: Nelson Jovel Date: Fri, 22 Dec 2023 20:43:12 -0800 Subject: [PATCH] set up example scaffold with rspec + factorybot tests --- .../dashboard/examples_controller.rb | 60 ++++++++ app/helpers/dashboard/examples_helper.rb | 4 + app/models/dashboard/example.rb | 4 + .../dashboard/examples/_example.html.erb | 12 ++ app/views/dashboard/examples/_form.html.erb | 27 ++++ app/views/dashboard/examples/edit.html.erb | 10 ++ app/views/dashboard/examples/index.html.erb | 14 ++ app/views/dashboard/examples/new.html.erb | 9 ++ app/views/dashboard/examples/show.html.erb | 10 ++ config/routes.rb | 1 + ...0231223040511_create_dashboard_examples.rb | 10 ++ lib/dashboard/engine.rb | 6 + spec/dummy/db/schema.rb | 24 +++ spec/factories/dashboard/examples.rb | 6 + .../helpers/dashboard/examples_helper_spec.rb | 17 +++ spec/models/dashboard/example_spec.rb | 10 ++ spec/rails_helper.rb | 25 +++- spec/requests/dashboard/examples_spec.rb | 138 ++++++++++++++++++ .../dashboard/examples_routing_spec.rb | 39 +++++ .../dashboard/examples/edit.html.erb_spec.rb | 26 ++++ .../dashboard/examples/index.html.erb_spec.rb | 26 ++++ .../dashboard/examples/new.html.erb_spec.rb | 22 +++ .../dashboard/examples/show.html.erb_spec.rb | 18 +++ 23 files changed, 512 insertions(+), 6 deletions(-) create mode 100644 app/controllers/dashboard/examples_controller.rb create mode 100644 app/helpers/dashboard/examples_helper.rb create mode 100644 app/models/dashboard/example.rb create mode 100644 app/views/dashboard/examples/_example.html.erb create mode 100644 app/views/dashboard/examples/_form.html.erb create mode 100644 app/views/dashboard/examples/edit.html.erb create mode 100644 app/views/dashboard/examples/index.html.erb create mode 100644 app/views/dashboard/examples/new.html.erb create mode 100644 app/views/dashboard/examples/show.html.erb create mode 100644 db/migrate/20231223040511_create_dashboard_examples.rb create mode 100644 spec/dummy/db/schema.rb create mode 100644 spec/factories/dashboard/examples.rb create mode 100644 spec/helpers/dashboard/examples_helper_spec.rb create mode 100644 spec/models/dashboard/example_spec.rb create mode 100644 spec/requests/dashboard/examples_spec.rb create mode 100644 spec/routing/dashboard/examples_routing_spec.rb create mode 100644 spec/views/dashboard/examples/edit.html.erb_spec.rb create mode 100644 spec/views/dashboard/examples/index.html.erb_spec.rb create mode 100644 spec/views/dashboard/examples/new.html.erb_spec.rb create mode 100644 spec/views/dashboard/examples/show.html.erb_spec.rb diff --git a/app/controllers/dashboard/examples_controller.rb b/app/controllers/dashboard/examples_controller.rb new file mode 100644 index 0000000..c2b96dc --- /dev/null +++ b/app/controllers/dashboard/examples_controller.rb @@ -0,0 +1,60 @@ +module Dashboard + class ExamplesController < ApplicationController + before_action :set_example, only: %i[ show edit update destroy ] + + # GET /examples + def index + @examples = Example.all + end + + # GET /examples/1 + def show + end + + # GET /examples/new + def new + @example = Example.new + end + + # GET /examples/1/edit + def edit + end + + # POST /examples + def create + @example = Example.new(example_params) + + if @example.save + redirect_to @example, notice: "Example was successfully created." + else + render :new, status: :unprocessable_entity + end + end + + # PATCH/PUT /examples/1 + def update + if @example.update(example_params) + redirect_to @example, notice: "Example was successfully updated.", status: :see_other + else + render :edit, status: :unprocessable_entity + end + end + + # DELETE /examples/1 + def destroy + @example.destroy! + redirect_to examples_url, notice: "Example was successfully destroyed.", status: :see_other + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_example + @example = Example.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def example_params + params.require(:example).permit(:text, :body) + end + end +end diff --git a/app/helpers/dashboard/examples_helper.rb b/app/helpers/dashboard/examples_helper.rb new file mode 100644 index 0000000..d7c4dbd --- /dev/null +++ b/app/helpers/dashboard/examples_helper.rb @@ -0,0 +1,4 @@ +module Dashboard + module ExamplesHelper + end +end diff --git a/app/models/dashboard/example.rb b/app/models/dashboard/example.rb new file mode 100644 index 0000000..9f42183 --- /dev/null +++ b/app/models/dashboard/example.rb @@ -0,0 +1,4 @@ +module Dashboard + class Example < ApplicationRecord + end +end diff --git a/app/views/dashboard/examples/_example.html.erb b/app/views/dashboard/examples/_example.html.erb new file mode 100644 index 0000000..17eac30 --- /dev/null +++ b/app/views/dashboard/examples/_example.html.erb @@ -0,0 +1,12 @@ +
+

+ Text: + <%= example.text %> +

+ +

+ Body: + <%= example.body %> +

+ +
diff --git a/app/views/dashboard/examples/_form.html.erb b/app/views/dashboard/examples/_form.html.erb new file mode 100644 index 0000000..7a3ee22 --- /dev/null +++ b/app/views/dashboard/examples/_form.html.erb @@ -0,0 +1,27 @@ +<%= form_with(model: example) do |form| %> + <% if example.errors.any? %> +
+

<%= pluralize(example.errors.count, "error") %> prohibited this example from being saved:

+ + +
+ <% end %> + +
+ <%= form.label :text, style: "display: block" %> + <%= form.text_field :text %> +
+ +
+ <%= form.label :body, style: "display: block" %> + <%= form.text_area :body %> +
+ +
+ <%= form.submit %> +
+<% end %> diff --git a/app/views/dashboard/examples/edit.html.erb b/app/views/dashboard/examples/edit.html.erb new file mode 100644 index 0000000..92b1731 --- /dev/null +++ b/app/views/dashboard/examples/edit.html.erb @@ -0,0 +1,10 @@ +

Editing example

+ +<%= render "form", example: @example %> + +
+ +
+ <%= link_to "Show this example", @example %> | + <%= link_to "Back to examples", examples_path %> +
diff --git a/app/views/dashboard/examples/index.html.erb b/app/views/dashboard/examples/index.html.erb new file mode 100644 index 0000000..6eed656 --- /dev/null +++ b/app/views/dashboard/examples/index.html.erb @@ -0,0 +1,14 @@ +

<%= notice %>

+ +

Examples

+ +
+ <% @examples.each do |example| %> + <%= render example %> +

+ <%= link_to "Show this example", example %> +

+ <% end %> +
+ +<%= link_to "New example", new_example_path %> diff --git a/app/views/dashboard/examples/new.html.erb b/app/views/dashboard/examples/new.html.erb new file mode 100644 index 0000000..3986849 --- /dev/null +++ b/app/views/dashboard/examples/new.html.erb @@ -0,0 +1,9 @@ +

New example

+ +<%= render "form", example: @example %> + +
+ +
+ <%= link_to "Back to examples", examples_path %> +
diff --git a/app/views/dashboard/examples/show.html.erb b/app/views/dashboard/examples/show.html.erb new file mode 100644 index 0000000..e58d608 --- /dev/null +++ b/app/views/dashboard/examples/show.html.erb @@ -0,0 +1,10 @@ +

<%= notice %>

+ +<%= render @example %> + +
+ <%= link_to "Edit this example", edit_example_path(@example) %> | + <%= link_to "Back to examples", examples_path %> + + <%= button_to "Destroy this example", @example, method: :delete %> +
diff --git a/config/routes.rb b/config/routes.rb index a63ec31..3fb7434 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,2 +1,3 @@ Dashboard::Engine.routes.draw do + resources :examples end diff --git a/db/migrate/20231223040511_create_dashboard_examples.rb b/db/migrate/20231223040511_create_dashboard_examples.rb new file mode 100644 index 0000000..0108ae7 --- /dev/null +++ b/db/migrate/20231223040511_create_dashboard_examples.rb @@ -0,0 +1,10 @@ +class CreateDashboardExamples < ActiveRecord::Migration[7.1] + def change + create_table :dashboard_examples do |t| + t.string :text + t.text :body + + t.timestamps + end + end +end diff --git a/lib/dashboard/engine.rb b/lib/dashboard/engine.rb index 88d7a16..d676b9d 100644 --- a/lib/dashboard/engine.rb +++ b/lib/dashboard/engine.rb @@ -1,5 +1,11 @@ module Dashboard class Engine < ::Rails::Engine isolate_namespace Dashboard + + config.generators do |g| + g.test_framework :rspec + g.fixture_replacement :factory_bot + g.factory_bot dir: "spec/factories" + end end end diff --git a/spec/dummy/db/schema.rb b/spec/dummy/db/schema.rb new file mode 100644 index 0000000..1b07f5b --- /dev/null +++ b/spec/dummy/db/schema.rb @@ -0,0 +1,24 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.1].define(version: 2023_12_23_040511) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "dashboard_examples", force: :cascade do |t| + t.string "text" + t.text "body" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end diff --git a/spec/factories/dashboard/examples.rb b/spec/factories/dashboard/examples.rb new file mode 100644 index 0000000..09396d6 --- /dev/null +++ b/spec/factories/dashboard/examples.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :example, class: "Dashboard::Example" do + text { "MyString" } + body { "MyText" } + end +end diff --git a/spec/helpers/dashboard/examples_helper_spec.rb b/spec/helpers/dashboard/examples_helper_spec.rb new file mode 100644 index 0000000..0e6c57a --- /dev/null +++ b/spec/helpers/dashboard/examples_helper_spec.rb @@ -0,0 +1,17 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the ExamplesHelper. For example: +# +# describe ExamplesHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +module Dashboard + RSpec.describe ExamplesHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" + end +end diff --git a/spec/models/dashboard/example_spec.rb b/spec/models/dashboard/example_spec.rb new file mode 100644 index 0000000..ec767e1 --- /dev/null +++ b/spec/models/dashboard/example_spec.rb @@ -0,0 +1,10 @@ +require "rails_helper" + +module Dashboard + RSpec.describe Example, type: :model do + it "creates an example to test rspec and factorybot" do + create(:example) + expect(Example.count).to eq 1 + end + end +end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index a15455f..62024d6 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,10 +1,11 @@ # This file is copied to spec/ when you run 'rails generate rspec:install' -require 'spec_helper' -ENV['RAILS_ENV'] ||= 'test' -require_relative '../config/environment' +require "spec_helper" +ENV["RAILS_ENV"] ||= "test" +require_relative "../spec/dummy/config/environment" # Prevent database truncation if the environment is production abort("The Rails environment is running in production mode!") if Rails.env.production? -require 'rspec/rails' +require "rspec/rails" + # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in @@ -29,12 +30,19 @@ begin rescue ActiveRecord::PendingMigrationError => e abort e.to_s.strip end + +require "factory_bot_rails" + +FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), "factories") +FactoryBot.factories.clear +FactoryBot.find_definitions + RSpec.configure do |config| # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_paths = [ - Rails.root.join('spec/fixtures') + Rails.root.join("spec/fixtures") ] - + config.include FactoryBot::Syntax::Methods # If you're not using ActiveRecord, or you'd prefer not to run each of your # examples within a transaction, remove the following line or assign false # instead of true. @@ -62,4 +70,9 @@ RSpec.configure do |config| config.filter_rails_from_backtrace! # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") + config.before(:example, type: :view) do + view.class_eval do + include Dashboard::Engine.routes.url_helpers + end + end end diff --git a/spec/requests/dashboard/examples_spec.rb b/spec/requests/dashboard/examples_spec.rb new file mode 100644 index 0000000..5deb02c --- /dev/null +++ b/spec/requests/dashboard/examples_spec.rb @@ -0,0 +1,138 @@ +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to test the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. + +module Dashboard + RSpec.describe "/examples", type: :request do + include Engine.routes.url_helpers + + # This should return the minimal set of attributes required to create a valid + # Example. As you add validations to Example, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + describe "GET /index" do + it "renders a successful response" do + Example.create! valid_attributes + get examples_url + expect(response).to be_successful + end + end + + describe "GET /show" do + it "renders a successful response" do + example = Example.create! valid_attributes + get example_url(example) + expect(response).to be_successful + end + end + + describe "GET /new" do + it "renders a successful response" do + get new_example_url + expect(response).to be_successful + end + end + + describe "GET /edit" do + it "renders a successful response" do + example = Example.create! valid_attributes + get edit_example_url(example) + expect(response).to be_successful + end + end + + describe "POST /create" do + context "with valid parameters" do + it "creates a new Example" do + expect { + post examples_url, params: { example: valid_attributes } + }.to change(Example, :count).by(1) + end + + it "redirects to the created example" do + post examples_url, params: { example: valid_attributes } + expect(response).to redirect_to(example_url(Example.last)) + end + end + + context "with invalid parameters" do + it "does not create a new Example" do + expect { + post examples_url, params: { example: invalid_attributes } + }.to change(Example, :count).by(0) + end + + + it "renders a response with 422 status (i.e. to display the 'new' template)" do + post examples_url, params: { example: invalid_attributes } + expect(response).to have_http_status(:unprocessable_entity) + end + + end + end + + describe "PATCH /update" do + context "with valid parameters" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested example" do + example = Example.create! valid_attributes + patch example_url(example), params: { example: new_attributes } + example.reload + skip("Add assertions for updated state") + end + + it "redirects to the example" do + example = Example.create! valid_attributes + patch example_url(example), params: { example: new_attributes } + example.reload + expect(response).to redirect_to(example_url(example)) + end + end + + context "with invalid parameters" do + + it "renders a response with 422 status (i.e. to display the 'edit' template)" do + example = Example.create! valid_attributes + patch example_url(example), params: { example: invalid_attributes } + expect(response).to have_http_status(:unprocessable_entity) + end + + end + end + + describe "DELETE /destroy" do + it "destroys the requested example" do + example = Example.create! valid_attributes + expect { + delete example_url(example) + }.to change(Example, :count).by(-1) + end + + it "redirects to the examples list" do + example = Example.create! valid_attributes + delete example_url(example) + expect(response).to redirect_to(examples_url) + end + end + end +end diff --git a/spec/routing/dashboard/examples_routing_spec.rb b/spec/routing/dashboard/examples_routing_spec.rb new file mode 100644 index 0000000..3007913 --- /dev/null +++ b/spec/routing/dashboard/examples_routing_spec.rb @@ -0,0 +1,39 @@ +require "rails_helper" + +module Dashboard + RSpec.describe ExamplesController, type: :routing do + describe "routing" do + it "routes to #index" do + expect(get: "dashboard/examples").to route_to("dashboard/examples#index") + end + + it "routes to #new" do + expect(get: "dashboard/examples/new").to route_to("dashboard/examples#new") + end + + it "routes to #show" do + expect(get: "dashboard/examples/1").to route_to("dashboard/examples#show", id: "1") + end + + it "routes to #edit" do + expect(get: "/dashboard/examples/1/edit").to route_to("dashboard/examples#edit", id: "1") + end + + it "routes to #create" do + expect(post: "/dashboard/examples").to route_to("dashboard/examples#create") + end + + it "routes to #update via PUT" do + expect(put: "/dashboard/examples/1").to route_to("dashboard/examples#update", id: "1") + end + + it "routes to #update via PATCH" do + expect(patch: "/dashboard/examples/1").to route_to("dashboard/examples#update", id: "1") + end + + it "routes to #destroy" do + expect(delete: "/dashboard/examples/1").to route_to("dashboard/examples#destroy", id: "1") + end + end + end +end diff --git a/spec/views/dashboard/examples/edit.html.erb_spec.rb b/spec/views/dashboard/examples/edit.html.erb_spec.rb new file mode 100644 index 0000000..5162d25 --- /dev/null +++ b/spec/views/dashboard/examples/edit.html.erb_spec.rb @@ -0,0 +1,26 @@ +require "rails_helper" + +module Dashboard + RSpec.xdescribe "dashboard/examples/edit", type: :view do + let(:example) do + Example.create!( + text: "MyString", + body: "MyText" + ) + end + + before(:each) do + assign(:example, example) + end + + it "renders the edit example form" do + render + + assert_select "form[action=?][method=?]", example_path(example), "post" do + assert_select "input[name=?]", "example[text]" + + assert_select "textarea[name=?]", "example[body]" + end + end + end +end diff --git a/spec/views/dashboard/examples/index.html.erb_spec.rb b/spec/views/dashboard/examples/index.html.erb_spec.rb new file mode 100644 index 0000000..649774b --- /dev/null +++ b/spec/views/dashboard/examples/index.html.erb_spec.rb @@ -0,0 +1,26 @@ +require "rails_helper" +require "nokogiri" + +module Dashboard + RSpec.describe "/dashboard/examples/index", type: :view do + before(:each) do + assign(:examples, [ + Example.create!( + text: "Word", + body: "Sentence" + ), + Example.create!( + text: "Word", + body: "Sentence" + ) + ]) + end + + it "renders a list of examples" do + render + cell_selector = Rails::VERSION::STRING >= "7" ? "div>p" : "tr>td" + assert_select cell_selector, text: Regexp.new("Word".to_s), count: 2 + assert_select cell_selector, text: Regexp.new("Sentence".to_s), count: 2 + end + end +end diff --git a/spec/views/dashboard/examples/new.html.erb_spec.rb b/spec/views/dashboard/examples/new.html.erb_spec.rb new file mode 100644 index 0000000..6d97650 --- /dev/null +++ b/spec/views/dashboard/examples/new.html.erb_spec.rb @@ -0,0 +1,22 @@ +require "rails_helper" + +module Dashboard + RSpec.xdescribe "dashboard/examples/new", type: :view do + before(:each) do + assign(:example, Example.new( + text: "MyString", + body: "MyText" + )) + end + + it "renders new example form" do + render + + assert_select "form[action=?][method=?]", examples_path, "post" do + assert_select "input[name=?]", "example[text]" + + assert_select "textarea[name=?]", "example[body]" + end + end + end +end diff --git a/spec/views/dashboard/examples/show.html.erb_spec.rb b/spec/views/dashboard/examples/show.html.erb_spec.rb new file mode 100644 index 0000000..5049e5e --- /dev/null +++ b/spec/views/dashboard/examples/show.html.erb_spec.rb @@ -0,0 +1,18 @@ +require "rails_helper" + +module Dashboard + RSpec.describe "dashboard/examples/show", type: :view do + before(:each) do + assign(:example, Example.create!( + text: "Word", + body: "Sentence" + )) + end + + it "renders attributes in

" do + render + expect(rendered).to match(/Word/) + expect(rendered).to match(/Sentence/) + end + end +end