From cd0a6da28dc5dab59452c8af94a94005907bb8e3 Mon Sep 17 00:00:00 2001 From: Venus Lumanglas Date: Sat, 21 Dec 2024 16:51:38 +0800 Subject: [PATCH] Refactor test suites --- .gitignore | 2 + Gemfile | 14 +- Gemfile.lock | 19 +- README.md | 60 ++++- app/models/category.rb | 3 +- app/models/order.rb | 5 +- app/models/order_item.rb | 6 +- app/models/product.rb | 13 +- app/models/role.rb | 5 +- app/models/stock.rb | 11 +- app/models/user.rb | 16 +- config/environments/test.rb | 5 + config/locales/devise.en.yml | 2 +- spec/factories/categories.rb | 8 + spec/factories/order_items.rb | 14 + spec/factories/orders.rb | 17 ++ spec/factories/products.rb | 16 ++ spec/factories/roles.rb | 11 + spec/factories/stocks.rb | 9 + spec/factories/subscribers.rb | 7 + spec/factories/users.rb | 16 ++ .../turbo_stream_actions_helper_spec.rb | 2 +- spec/models/category_spec.rb | 39 ++- spec/models/order_spec.rb | 87 ++++--- spec/models/product_spec.rb | 60 ++++- spec/models/role_spec.rb | 50 +++- spec/models/stock_spec.rb | 43 +++- spec/models/subscriber_spec.rb | 44 +++- spec/models/user_spec.rb | 239 ++++++++++++++++-- spec/rails_helper.rb | 14 +- spec/requests/admin/categories_spec.rb | 52 ++-- spec/requests/admin/customers_spec.rb | 15 +- spec/requests/admin/orders_spec.rb | 39 +-- spec/requests/admin/products_spec.rb | 55 ++-- spec/requests/admin/stocks_spec.rb | 52 ++-- spec/requests/admin/subscribers_spec.rb | 27 +- spec/requests/site/cart_spec.rb | 2 +- spec/requests/site/categories_spec.rb | 8 +- spec/requests/site/products_spec.rb | 10 +- spec/routing/admin/categories_routing_spec.rb | 2 +- spec/routing/admin/orders_routing_spec.rb | 2 +- spec/routing/admin/products_routing_spec.rb | 2 +- spec/routing/admin/stocks_routing_spec.rb | 2 +- .../routing/admin/subscribers_routing_spec.rb | 2 +- spec/support/devise.rb | 3 + spec/support/factory_bot.rb | 3 + spec/support/pagy.rb | 3 + spec/support/shoulda.rb | 8 + .../admin/categories/edit.html.slim_spec.rb | 4 +- .../admin/categories/index.html.slim_spec.rb | 16 +- .../admin/categories/new.html.slim_spec.rb | 4 +- .../admin/categories/show.html.slim_spec.rb | 8 +- .../admin/orders/index.html.slim_spec.rb | 44 +--- .../views/admin/orders/show.html.slim_spec.rb | 37 +-- .../admin/products/edit.html.slim_spec.rb | 6 +- .../admin/products/index.html.slim_spec.rb | 18 +- .../admin/products/new.html.slim_spec.rb | 4 +- .../admin/products/show.html.slim_spec.rb | 11 +- .../views/admin/stocks/edit.html.slim_spec.rb | 7 +- .../admin/stocks/index.html.slim_spec.rb | 14 +- spec/views/admin/stocks/new.html.slim_spec.rb | 7 +- .../views/admin/stocks/show.html.slim_spec.rb | 11 +- .../admin/subscribers/index.html.slim_spec.rb | 8 +- spec/views/site/cart/show.html.slim_spec.rb | 2 +- .../site/categories/show.html.slim_spec.rb | 6 +- .../site/products/show.html.slim_spec.rb | 7 +- 66 files changed, 870 insertions(+), 468 deletions(-) create mode 100644 spec/factories/categories.rb create mode 100644 spec/factories/order_items.rb create mode 100644 spec/factories/orders.rb create mode 100644 spec/factories/products.rb create mode 100644 spec/factories/roles.rb create mode 100644 spec/factories/stocks.rb create mode 100644 spec/factories/subscribers.rb create mode 100644 spec/factories/users.rb create mode 100644 spec/support/devise.rb create mode 100644 spec/support/factory_bot.rb create mode 100644 spec/support/pagy.rb create mode 100644 spec/support/shoulda.rb diff --git a/.gitignore b/.gitignore index 45fcb38..32aa2d8 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,5 @@ yarn-debug.log* .ionide # End of https://www.toptal.com/developers/gitignore/api/visualstudiocode + +coverage diff --git a/Gemfile b/Gemfile index c323e58..99f47c9 100644 --- a/Gemfile +++ b/Gemfile @@ -86,9 +86,15 @@ group :development, :test do # A library for generating fake data such as names, addresses, and phone numbers gem "faker" + # A fixtures replacement with a straightforward definition syntax, support for multiple build strategies [https://github.com/thoughtbot/factory_bot_rails] + gem "factory_bot_rails" + # Static analysis for security vulnerabilities [https://brakemanscanner.org/] gem "brakeman", require: false + # Optimize queries [https://github.com/flyerhzm/bullet] + gem "bullet" + ## Code Formatting & Linting # Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/] gem "rubocop-rails-omakase", require: false @@ -119,13 +125,15 @@ group :development do # A normaliser/beautifier for HTML that also understands embedded Ruby. Ideal for tidying up Rails templates # [https://github.com/threedaymonk/htmlbeautifier] gem "htmlbeautifier", require: false - - # Optimize queries - gem "bullet" end group :test do # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] gem "capybara" gem "selenium-webdriver" + + gem "shoulda-matchers", "~> 6.0" + + # A code coverage analysis tool for Ruby [https://github.com/simplecov-ruby/simplecov] + gem "simplecov", require: false end diff --git a/Gemfile.lock b/Gemfile.lock index e637d1c..d94877c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -87,7 +87,7 @@ GEM bindex (0.8.1) bootsnap (1.18.4) msgpack (~> 1.2) - brakeman (6.2.1) + brakeman (6.2.2) racc builder (3.3.0) bullet (7.2.0) @@ -121,6 +121,7 @@ GEM responders warden (~> 1.2.3) diff-lcs (1.5.1) + docile (1.4.1) dotenv (3.1.4) dotenv-rails (3.1.4) dotenv (= 3.1.4) @@ -128,6 +129,11 @@ GEM drb (2.2.1) e2mmap (0.1.0) erubi (1.13.0) + factory_bot (6.5.0) + activesupport (>= 5.0.0) + factory_bot_rails (6.4.4) + factory_bot (~> 6.5) + railties (>= 5.0.0) faker (3.4.2) i18n (>= 1.8.11, < 2) faraday (2.12.0) @@ -349,6 +355,14 @@ GEM rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2, < 3.0) websocket (~> 1.0) + shoulda-matchers (6.4.0) + activesupport (>= 5.2.0) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.13.1) + simplecov_json_formatter (0.1.4) slim (5.2.1) temple (~> 0.10.0) tilt (>= 2.1.0) @@ -456,6 +470,7 @@ DEPENDENCIES debug devise dotenv-rails + factory_bot_rails faker friendly_id htmlbeautifier @@ -473,6 +488,8 @@ DEPENDENCIES rubocop-rspec rubocop-rspec_rails selenium-webdriver + shoulda-matchers (~> 6.0) + simplecov slim-rails slim_lint solargraph diff --git a/README.md b/README.md index a364d6e..3c87f73 100644 --- a/README.md +++ b/README.md @@ -55,6 +55,32 @@ To test Stripe payments, use the following test card details: 2. Set the endpoint URL to your production route (e.g., `https://yourdomain.com/stripe_webhooks`). 3. Select the events you want to listen for (e.g., `checkout.session.completed`, `customer.created`). +## GitHub Actions, Linting and Security Auditing + +GitHub actions are setup to lint and test the application on pushes to **main** and **feature** branches. It's also setup to deploy the application on pushes to **main** + +You can also run these actions locally before pushing to see if your run is likely to fail. See the following gems / commands for more info. + +- [Brakeman](https://brakemanscanner.org/) - Security audit application code + + ```bash + bin/brakeman -q -w2 + ``` + +- [Brakeman: Ignoring False Positives](https://brakemanscanner.org/docs/ignoring_false_positives) - Creating and Managing an Ignore File + + ```bash + bin/brakeman -I -q -w2 + ``` + +- [Rubocop Rails Omakase](https://github.com/rails/rubocop-rails-omakase) - Ruby Linter + + ```bash + bin/rubocop + ``` + + **Note:** Some linters like `ESLint`, `Prettier`, etc. will automatically run on `pre-commit` git hook. + ## Testing Setup test database @@ -69,6 +95,12 @@ Default: Run all spec files (i.e., those matching spec/\*\*/\*\_spec.rb) bin/rspec ``` +or with `--fail-fast` option to stop running the test suite on the first failed test. You may add a parameter to tell RSpec to stop running the test suite after N failed tests, for example: `--fail-fast=3` + +```bash +bin/rspec --fail-fast +``` + Run all spec files in a single directory (recursively) ```bash @@ -81,16 +113,20 @@ Run a single spec file bin/rspec spec/models/product_spec.rb ``` -Use the plain-English descriptions to generate a report of where the application conforms to (or fails to meet) the spec +Run a single example from a spec file (by line number) ```bash -bin/rspec --format documentation spec/models/product_spec.rb +bin/rspec spec/models/product_spec.rb:6 ``` -Run a single example from a spec file (by line number) +Use the plain-English descriptions to generate a report of where the application conforms to (or fails to meet) the spec + +```bash +bin/rspec --format documentation +``` ```bash -bin/rspec spec/models/product_spec.rb:8 +bin/rspec --format documentation spec/models/product_spec.rb ``` See all options for running specs @@ -98,3 +134,19 @@ See all options for running specs ```bash bin/rspec --help ``` + +After running your tests, open `coverage/index.html` in the browser of your choice. For example, in a Mac Terminal, +run the following command from your application's root directory: + +```bash +open coverage/index.html +``` + +in a debian/ubuntu Terminal, + +```bash +xdg-open coverage/index.html +``` + +**Note:** [This guide](https://dwheeler.com/essays/open-files-urls.html) can help if you're unsure which command your particular +operating system requires. diff --git a/app/models/category.rb b/app/models/category.rb index d65b6e1..78984c4 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -10,7 +10,8 @@ class Category < ApplicationRecord has_one_attached :image do |attachable| attachable.variant :thumb, resize_to_limit: [50, 50] end - has_many :products, dependent: :destroy + + has_many :products, inverse_of: :category, dependent: :destroy has_rich_text :description diff --git a/app/models/order.rb b/app/models/order.rb index 3eeec3c..8649403 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -3,12 +3,13 @@ class Order < ApplicationRecord include Filterable - belongs_to :user + belongs_to :user, inverse_of: :orders - has_many :order_items, -> { order(:id) }, dependent: :destroy, inverse_of: :order + has_many :order_items, -> { order(:id) }, inverse_of: :order, dependent: :destroy broadcasts_refreshes + validates :order_code, presence: true, uniqueness: { case_sensitive: false } validates :customer_email, :customer_full_name, :customer_address, presence: true validate :validate_has_one_item diff --git a/app/models/order_item.rb b/app/models/order_item.rb index c90681f..606f0c9 100644 --- a/app/models/order_item.rb +++ b/app/models/order_item.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true class OrderItem < ApplicationRecord - belongs_to :order - belongs_to :product - belongs_to :stock + belongs_to :order, inverse_of: :order_items + belongs_to :product, inverse_of: :order_items + belongs_to :stock, inverse_of: :order_items validates :order_code, :product_name, :size, presence: true validates :quantity, presence: true, numericality: { greater_than: 0 } diff --git a/app/models/product.rb b/app/models/product.rb index bca8362..999f010 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -7,14 +7,14 @@ class Product < ApplicationRecord # Set the attribute from which the slug would be generated slugify :name - belongs_to :category + belongs_to :category, inverse_of: :products has_many_attached :images do |attachable| attachable.variant :thumb, resize_to_limit: [50, 50] attachable.variant :medium, resize_to_limit: [250, 250] end - has_many :stocks, dependent: :destroy - has_many :order_items, dependent: :restrict_with_exception + has_many :stocks, inverse_of: :product, dependent: :destroy + has_many :order_items, inverse_of: :product, dependent: :restrict_with_exception has_rich_text :description @@ -22,12 +22,7 @@ class Product < ApplicationRecord broadcasts_refreshes validates :name, presence: true, uniqueness: { case_sensitive: false } - validates :price, - presence: true, - numericality: { - greater_than_or_equal_to: 0, - less_than_or_equal_to: 999_999_999 - } + validates :price, presence: true, numericality: { in: 0..999_999_999 } validates :images, content_type: %i[jpeg jpg png webp], size: { less_than_or_equal_to: 3.megabytes } scope :available, -> { joins(:stocks).where("quantity > 0").distinct } diff --git a/app/models/role.rb b/app/models/role.rb index 0501f26..61946c6 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -1,14 +1,15 @@ # frozen_string_literal: true class Role < ApplicationRecord + has_many :users, inverse_of: :role, dependent: :restrict_with_exception + validates :name, presence: true, uniqueness: { case_sensitive: false }, length: { - minimum: 2, - maximum: 40 + in: 2..40 }, format: /\A([^\d\W]|-|\s)*\z/ end diff --git a/app/models/stock.rb b/app/models/stock.rb index e146615..cdeaef9 100644 --- a/app/models/stock.rb +++ b/app/models/stock.rb @@ -1,16 +1,13 @@ # frozen_string_literal: true class Stock < ApplicationRecord - belongs_to :product + belongs_to :product, inverse_of: :stocks + + has_many :order_items, inverse_of: :stock, dependent: :restrict_with_exception broadcasts_refreshes_to :product broadcasts_refreshes validates :size, presence: true, uniqueness: { scope: :product_id, case_sensitive: false } - validates :quantity, - presence: true, - numericality: { - greater_than_or_equal_to: 0, - less_than_or_equal_to: 999_999_999 - } + validates :quantity, presence: true, numericality: { only_integer: true, in: 0..999_999_999 } end diff --git a/app/models/user.rb b/app/models/user.rb index 0d767e6..30039cd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -11,11 +11,11 @@ class User < ApplicationRecord # The column `gender` is supposed to be of type string in the database. literal_enum :gender, %w[male female] - belongs_to :role + belongs_to :role, inverse_of: :users - has_many :orders, dependent: :restrict_with_exception + has_many :orders, inverse_of: :user, dependent: :restrict_with_exception - validates :gender, presence: true, inclusion: { in: genders } + validates :gender, inclusion: { in: genders.keys } validates :email, length: { maximum: 255 } validates :password, presence: true, @@ -52,6 +52,14 @@ class User < ApplicationRecord end scope :filter_by_name, ->(name) { where("CONCAT(users.first_name, ' ', users.last_name) ILIKE ?", "%#{name}%") } + # Overwrite the setter to rely on validations instead of [ArgumentError] + # https://github.com/rails/rails/issues/13971#issuecomment-721821257 + def gender=(value) + self[:gender] = value + rescue ArgumentError + self[:gender] = nil + end + # instead of deleting, indicate the user requested a delete & timestamp it def soft_delete! update!(active: false, deleted_at: Time.current) @@ -98,7 +106,7 @@ def new_and_old_password_must_be_different password_is_same = Devise::Encryptor.compare(User, encrypted_password_was, password) return unless password_is_same - errors.add(:base, I18n.t("errors.messages.old_password_not_allowed")) + errors.add(:password, I18n.t("devise.passwords.old_password_not_allowed")) end def set_role diff --git a/config/environments/test.rb b/config/environments/test.rb index b8b261a..168010e 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -9,6 +9,11 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. + config.after_initialize do + Bullet.enable = false # enable Bullet gem, otherwise do nothing + Bullet.bullet_logger = true # log to the Bullet log file (Rails.root/log/bullet.log) + Bullet.raise = true # raise an error if n+1 query occurs + end # While tests run files are not watched, reloading is not necessary. config.enable_reloading = false diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index fa2e3a0..d16cb78 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -37,6 +37,7 @@ en: send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." updated: "Your password has been changed successfully. You are now signed in." updated_not_active: "Your password has been changed successfully." + old_password_not_allowed: "Old password not allowed" registrations: destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." signed_up: "Welcome! You have signed up successfully." @@ -64,4 +65,3 @@ en: not_saved: one: "1 error prohibited this %{resource} from being saved:" other: "%{count} errors prohibited this %{resource} from being saved:" - old_password_not_allowed: "Old password not allowed" diff --git a/spec/factories/categories.rb b/spec/factories/categories.rb new file mode 100644 index 0000000..f552b8c --- /dev/null +++ b/spec/factories/categories.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :category do + sequence(:name) { |n| "Category #{n}" } + description { "Description" } + end +end diff --git a/spec/factories/order_items.rb b/spec/factories/order_items.rb new file mode 100644 index 0000000..eabdf07 --- /dev/null +++ b/spec/factories/order_items.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :order_item do + order + product + stock { association(:stock, product:) } + order_code { order.order_code } + product_name { product.name } + product_price { product.price } + size { stock.size } + quantity { 2 } + end +end diff --git a/spec/factories/orders.rb b/spec/factories/orders.rb new file mode 100644 index 0000000..2791b08 --- /dev/null +++ b/spec/factories/orders.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :order do + user + customer_email { user.email } + customer_full_name { user.full_name } + customer_address { "my address 1" } + + transient { items_count { 2 } } + + after(:build) do |order, evaluator| + product = create(:product, :with_stocks) + build_list(:order_item, evaluator.items_count, order:, product:) + end + end +end diff --git a/spec/factories/products.rb b/spec/factories/products.rb new file mode 100644 index 0000000..a6bfd56 --- /dev/null +++ b/spec/factories/products.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :product do + category + sequence(:name) { |n| "Product #{n}" } + description { "Description" } + price { 100 } + + trait :with_stocks do + transient { stocks_count { 2 } } + + after(:build) { |product, evaluator| build_list(:stock, evaluator.stocks_count, product:) } + end + end +end diff --git a/spec/factories/roles.rb b/spec/factories/roles.rb new file mode 100644 index 0000000..8877ed9 --- /dev/null +++ b/spec/factories/roles.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :role do + name { "Customer" } + + trait :as_admin do + name { "Administrator" } + end + end +end diff --git a/spec/factories/stocks.rb b/spec/factories/stocks.rb new file mode 100644 index 0000000..f6363bd --- /dev/null +++ b/spec/factories/stocks.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :stock do + product + sequence(:size) { |n| "SZ #{n}" } + quantity { 50 } + end +end diff --git a/spec/factories/subscribers.rb b/spec/factories/subscribers.rb new file mode 100644 index 0000000..6351fd2 --- /dev/null +++ b/spec/factories/subscribers.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :subscriber do + sequence(:email) { |n| "user-#{n}@example.com" } + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb new file mode 100644 index 0000000..176c0c4 --- /dev/null +++ b/spec/factories/users.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :user do + role + first_name { "John" } + last_name { "Doe" } + sequence(:email) { |n| "user-#{n}@example.com" } + password { "password" } + gender { "male" } + + trait :as_admin do + role { association(:role, :as_admin) } + end + end +end diff --git a/spec/helpers/turbo_stream_actions_helper_spec.rb b/spec/helpers/turbo_stream_actions_helper_spec.rb index 268a4a7..208751e 100644 --- a/spec/helpers/turbo_stream_actions_helper_spec.rb +++ b/spec/helpers/turbo_stream_actions_helper_spec.rb @@ -3,7 +3,7 @@ require "rails_helper" # Specs in this file have access to a helper object that includes the TurboStreamActionsHelper. -RSpec.describe TurboStreamActionsHelper do +RSpec.describe TurboStreamActionsHelper, type: :helper do describe "#redirect" do it "returns a turbo-stream tag" do expect(helper.redirect("/login")).to eq( diff --git a/spec/models/category_spec.rb b/spec/models/category_spec.rb index 59b6b20..dc78ff0 100644 --- a/spec/models/category_spec.rb +++ b/spec/models/category_spec.rb @@ -2,14 +2,39 @@ require "rails_helper" -RSpec.describe Category do - it "does not save without name" do - category = described_class.new - expect(category.save).to be false +RSpec.describe Category, type: :model do + describe "db_columns" do + it { should have_db_column(:name).of_type(:string).with_options(null: false) } + it { should have_db_column(:active).of_type(:boolean).with_options(null: false, default: true) } + it { should have_db_column(:slug).of_type(:string).with_options(null: false) } end - it "saves" do - category = described_class.new(name: "Category") - expect(category.save).to be true + describe "db_indexes" do + it { should have_db_index(:active) } + it { should have_db_index(:name).unique } + it { should have_db_index(:slug).unique } + end + + describe "associations" do + describe "has_one" do + it { should have_one_attached(:image) } + it { should have_rich_text(:description) } + end + + describe "has_many" do + it { should have_many(:products).inverse_of(:category).dependent(:destroy) } + end + end + + describe "validations" do + describe "presence" do + it { should validate_presence_of(:name) } + end + + describe "uniqueness" do + subject { build :category } + + it { should validate_uniqueness_of(:name).case_insensitive } + end end end diff --git a/spec/models/order_spec.rb b/spec/models/order_spec.rb index 86651de..30f043b 100644 --- a/spec/models/order_spec.rb +++ b/spec/models/order_spec.rb @@ -2,49 +2,54 @@ require "rails_helper" -RSpec.describe Order do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass1234", - first_name: "John", - last_name: "Doe" - ) +RSpec.describe Order, type: :model do + describe "db_columns" do + it { should have_db_column(:user_id).of_type(:integer).with_options(null: false) } + it { should have_db_column(:customer_email).of_type(:string).with_options(null: false) } + it { should have_db_column(:customer_full_name).of_type(:string).with_options(null: false) } + it { should have_db_column(:customer_address).of_type(:string).with_options(null: false) } + it { should have_db_column(:fulfilled).of_type(:boolean).with_options(null: false, default: false) } + it { should have_db_column(:order_code).of_type(:string).with_options(null: false) } + it do + should have_db_column(:total).of_type(:decimal).with_options( + null: false, + default: 0.0, + precision: 12, + scale: 2 + ) + end end - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "Product", category:) } - let!(:stock) { product.stocks.create!(size: "L", quantity: 3) } - - it "does not save without item" do - order = - described_class.new( - user:, - customer_email: user.email, - customer_full_name: user.full_name, - customer_address: "my address 1" - ) - expect(order.save).to be false + + describe "db_indexes" do + it { should have_db_index(:customer_email) } + it { should have_db_index(:customer_full_name) } + it { should have_db_index(:fulfilled) } + it { should have_db_index(:order_code).unique } + it { should have_db_index(:user_id) } + end + + describe "associations" do + describe "belongs_to" do + it { should belong_to(:user).inverse_of(:orders) } + end + + describe "has_many" do + it { should have_many(:order_items).inverse_of(:order).dependent(:destroy) } + end end - it "saves" do # rubocop:disable RSpec/ExampleLength - order = - described_class.new( - user:, - customer_email: user.email, - customer_full_name: user.full_name, - customer_address: "my address 1" - ) - order.order_items.build( - product:, - stock:, - order_code: order.order_code, - product_name: product.name, - product_price: product.price, - size: stock.size, - quantity: 2 - ) - expect(order.save).to be true + describe "validations" do + describe "presence" do + it { should validate_presence_of(:order_code) } + it { should validate_presence_of(:customer_email) } + it { should validate_presence_of(:customer_full_name) } + it { should validate_presence_of(:customer_address) } + end + + describe "uniqueness" do + subject { build :order } + + it { should validate_uniqueness_of(:order_code).case_insensitive } + end end end diff --git a/spec/models/product_spec.rb b/spec/models/product_spec.rb index 75c1c0a..03e9648 100644 --- a/spec/models/product_spec.rb +++ b/spec/models/product_spec.rb @@ -2,21 +2,59 @@ require "rails_helper" -RSpec.describe Product do - let!(:category) { Category.create!(name: "Category") } +RSpec.describe Product, type: :model do + describe "db_columns" do + it { should have_db_column(:category_id).of_type(:integer).with_options(null: false) } + it { should have_db_column(:name).of_type(:string).with_options(null: false) } + it do + should have_db_column(:price).of_type(:decimal).with_options( + null: false, + default: 0.0, + precision: 12, + scale: 2 + ) + end + it { should have_db_column(:active).of_type(:boolean).with_options(null: false, default: true) } + it { should have_db_column(:slug).of_type(:string).with_options(null: false) } + end - it "does not save without name" do - product = described_class.new(category:) - expect(product.save).to be false + describe "db_indexes" do + it { should have_db_index(:active) } + it { should have_db_index(:category_id) } + it { should have_db_index(:name).unique } + it { should have_db_index(:slug).unique } end - it "does not save without category" do - product = described_class.new(name: "Product") - expect(product.save).to be false + describe "associations" do + describe "belongs_to" do + it { should belong_to(:category).inverse_of(:products) } + end + + describe "has_one" do + it { should have_rich_text(:description) } + end + + describe "has_many" do + it { should have_many_attached(:images) } + it { should have_many(:stocks).inverse_of(:product).dependent(:destroy) } + it { should have_many(:order_items).inverse_of(:product).dependent(:restrict_with_exception) } + end end - it "saves" do - product = described_class.new(name: "Product", category:) - expect(product.save).to be true + describe "validations" do + describe "presence" do + it { should validate_presence_of(:name) } + it { should validate_presence_of(:price) } + end + + describe "numericality" do + it { should validate_numericality_of(:price).is_in(0..999_999_999) } + end + + describe "uniqueness" do + subject { build :product } + + it { should validate_uniqueness_of(:name).case_insensitive } + end end end diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb index feb533d..464a8ff 100644 --- a/spec/models/role_spec.rb +++ b/spec/models/role_spec.rb @@ -2,14 +2,50 @@ require "rails_helper" -RSpec.describe Role do - it "does not save without name" do - role = described_class.new - expect(role.save).to be false +RSpec.describe Role, type: :model do + describe "db_columns" do + it { should have_db_column(:name).of_type(:string).with_options(null: false) } + it { should have_db_column(:active).of_type(:boolean).with_options(null: false, default: true) } end - it "saves" do - role = described_class.new(name: "Administrator") - expect(role.save).to be true + describe "db_indexes" do + it { should have_db_index(:active) } + it { should have_db_index(:name).unique } + end + + describe "associations" do + describe "has_many" do + it { should have_many(:users).inverse_of(:role).dependent(:restrict_with_exception) } + end + end + + describe "validations" do + describe "presence" do + it { should validate_presence_of(:name) } + end + + describe "length" do + it { should validate_length_of(:name).is_at_least(2).is_at_most(40) } + end + + describe "uniqueness" do + subject { build :role } + + it { should validate_uniqueness_of(:name).case_insensitive } + end + + describe "format" do + subject { build :role } + + it "name accepts a valid value" do + subject.name = "role" + expect(subject).to be_valid + end + + it "name does not accept an invalid format" do + subject.name = "role-1" + expect(subject).to be_invalid + end + end end end diff --git a/spec/models/stock_spec.rb b/spec/models/stock_spec.rb index 5b0019f..365b6b3 100644 --- a/spec/models/stock_spec.rb +++ b/spec/models/stock_spec.rb @@ -2,17 +2,42 @@ require "rails_helper" -RSpec.describe Stock do - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "Product", category:) } +RSpec.describe Stock, type: :model do + describe "db_columns" do + it { should have_db_column(:product_id).of_type(:integer).with_options(null: false) } + it { should have_db_column(:size).of_type(:string).with_options(null: false) } + it { should have_db_column(:quantity).of_type(:integer).with_options(null: false, default: 0) } + end + + describe "db_indexes" do + it { should have_db_index(%i[product_id size]).unique } + it { should have_db_index(:product_id) } + end + + describe "associations" do + describe "belongs_to" do + it { should belong_to(:product).inverse_of(:stocks) } + end - it "does not save without product" do - stock = described_class.new(size: 10, quantity: 5) - expect(stock.save).to be false + describe "has_many" do + it { should have_many(:order_items).inverse_of(:stock).dependent(:restrict_with_exception) } + end end - it "saves" do - stock = described_class.new(size: 10, quantity: 5, product:) - expect(stock.save).to be true + describe "validations" do + describe "presence" do + it { should validate_presence_of(:size) } + it { should validate_presence_of(:quantity) } + end + + describe "numericality" do + it { should validate_numericality_of(:quantity).only_integer.is_in(0..999_999_999) } + end + + describe "uniqueness" do + subject { build :stock } + + it { should validate_uniqueness_of(:size).scoped_to(:product_id).case_insensitive } + end end end diff --git a/spec/models/subscriber_spec.rb b/spec/models/subscriber_spec.rb index 0038b5b..44778da 100644 --- a/spec/models/subscriber_spec.rb +++ b/spec/models/subscriber_spec.rb @@ -2,14 +2,44 @@ require "rails_helper" -RSpec.describe Subscriber do - it "does not save without email" do - subscriber = described_class.new - expect(subscriber.save).to be false +RSpec.describe Subscriber, type: :model do + describe "db_columns" do + it { should have_db_column(:email).of_type(:string).with_options(null: false) } + it { should have_db_column(:ip_address).of_type(:string).with_options(null: false, default: "") } + it { should have_db_column(:user_agent).of_type(:string).with_options(null: false, default: "") } end - it "saves" do - subscriber = described_class.new(email: "jd@gmail.com") - expect(subscriber.save).to be true + describe "db_indexes" do + it { should have_db_index(:email).unique } + end + + describe "validations" do + describe "presence" do + it { should validate_presence_of(:email) } + end + + describe "length" do + it { should validate_length_of(:email).is_at_most(255) } + end + + describe "uniqueness" do + subject { build :subscriber } + + it { should validate_uniqueness_of(:email).case_insensitive } + end + + describe "format" do + subject { build :subscriber } + + it "email accepts a valid value" do + subject.email = "subscriber@email.com" + expect(subject).to be_valid + end + + it "email does not accept an invalid format" do + subject.email = "subscriber@" + expect(subject).to be_invalid + end + end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 2f50dd7..536b5f1 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -2,41 +2,228 @@ require "rails_helper" -RSpec.describe User do - let!(:user) do - described_class.new( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass1234", - first_name: "John", - last_name: "Doe" - ) +RSpec.describe User, type: :model do + describe "db_columns" do + it { is_expected.to have_db_column(:email).of_type(:string).with_options(null: false, default: "") } + it do + is_expected.to have_db_column(:encrypted_password).of_type(:string).with_options(null: false, default: "") + end + it { is_expected.to have_db_column(:reset_password_token).of_type(:string) } + it { is_expected.to have_db_column(:reset_password_sent_at).of_type(:datetime) } + it { is_expected.to have_db_column(:remember_created_at).of_type(:datetime) } + it { is_expected.to have_db_column(:sign_in_count).of_type(:integer).with_options(null: false, default: 0) } + it { is_expected.to have_db_column(:current_sign_in_at).of_type(:datetime) } + it { is_expected.to have_db_column(:last_sign_in_at).of_type(:datetime) } + it { is_expected.to have_db_column(:current_sign_in_ip).of_type(:string) } + it { is_expected.to have_db_column(:last_sign_in_ip).of_type(:string) } + it { is_expected.to have_db_column(:first_name).of_type(:string).with_options(null: false) } + it { is_expected.to have_db_column(:last_name).of_type(:string).with_options(null: false) } + it { is_expected.to have_db_column(:phone_number).of_type(:string) } + it { is_expected.to have_db_column(:active).of_type(:boolean).with_options(null: false, default: true) } + it { is_expected.to have_db_column(:role_id).of_type(:integer).with_options(null: false) } + it { is_expected.to have_db_column(:gender).of_type(:string).with_options(null: false) } + it { is_expected.to have_db_column(:deleted_at).of_type(:datetime) } + it { is_expected.to have_db_column(:stripe_customer_id).of_type(:string) } end - it "does not save without email and password" do - user = described_class.new(first_name: "John", last_name: "Doe") - expect(user.save).to be false + describe "db_indexes" do + it { is_expected.to have_db_index(:active) } + it { is_expected.to have_db_index(:email).unique } + it { is_expected.to have_db_index(:first_name) } + it { is_expected.to have_db_index(:last_name) } + it { is_expected.to have_db_index(:gender) } + it { is_expected.to have_db_index(:role_id) } + it { is_expected.to have_db_index(:reset_password_token).unique } + it { is_expected.to have_db_index(:stripe_customer_id).unique } end - it "does not save without first name and last name" do - user = described_class.new(email: "jd@gmail.com", password: "pass123") - expect(user.save).to be false + describe "associations" do + describe "belongs_to" do + # Use `without_validating_presence` with `belong_to` to prevent the + # matcher from checking whether the association disallows nil (Rails 5+ only). + # This can be helpful if you have a custom hook that always sets + # the association to a meaningful value: + it { is_expected.to belong_to(:role).inverse_of(:users).without_validating_presence } + end + + describe "has_many" do + it { is_expected.to have_many(:orders).inverse_of(:user).dependent(:restrict_with_exception) } + end + end + + describe "validations" do + describe "presence" do + it { should validate_presence_of(:first_name) } + it { should validate_presence_of(:last_name) } + + subject! { create :user, password: "password" } + + context "if encrypted_password_changed? on update" do + before { allow(subject).to receive(:encrypted_password_changed?).and_return(true) } + + it { should validate_presence_of(:password).on(:update) } + end + + context "if not encrypted_password_changed? on update" do + before { allow(subject).to receive(:encrypted_password_changed?).and_return(false) } + + it { should_not validate_presence_of(:password).on(:update) } + end + end + + describe "inclusion" do + it { is_expected.to validate_inclusion_of(:gender).in_array(User.genders.keys) } + end + + describe "length" do + it { should validate_length_of(:email).is_at_most(255) } + it { should validate_length_of(:first_name).is_at_least(2).is_at_most(100) } + it { should validate_length_of(:last_name).is_at_least(2).is_at_most(100) } + + subject! { create :user, password: "password" } + + context "if encrypted_password_changed? on update" do + before { allow(subject).to receive(:encrypted_password_changed?).and_return(true) } + + it { should validate_length_of(:password).on(:update).is_at_least(8).is_at_most(20) } + end + + context "if not encrypted_password_changed? on update" do + before { allow(subject).to receive(:encrypted_password_changed?).and_return(false) } + + it { should_not validate_length_of(:password).on(:update).is_at_least(8).is_at_most(20) } + end + end + + describe "format" do + subject { build :user } + + it "first_name accepts a valid value" do + subject.first_name = "John" + expect(subject).to be_valid + end + + it "first_name does not accept an invalid format" do + subject.first_name = "John-1" + expect(subject).to be_invalid + end + + it "last_name accepts a valid value" do + subject.last_name = "Doe" + expect(subject).to be_valid + end + + it "last_name does not accept an invalid format" do + subject.last_name = "Doe-1" + expect(subject).to be_invalid + end + end + + describe "before_validation .set_role" do + subject { build :user, role: } + + let!(:role) { create :role, :as_admin } + + it "sets the role to Administrator" do + subject.save! + expect(subject.role.name).to eq("Administrator") + end + + context "when the role_id is nil" do + before { subject.role_id = nil } + + it "sets the role to Customer" do + subject.save! + expect(subject.role.name).to eq("Customer") + end + end + end + + describe "new_and_old_password_must_be_different" do + subject! { create :user, password: "password" } + + it "is valid" do + expect(subject.valid?).to be true + end + + context "when the password is similar to old" do + before { subject.password = "password" } + + it "is has an error on password" do + subject.validate + expect(subject.errors[:password].first).to eq("Old password not allowed") + end + end + end + end + + describe "status" do + subject { build :user } + + it "sets active to true" do + expect(subject.active).to be true + end + + describe ".deactivate!" do + it "changes the active from true to false" do + expect { subject.deactivate! }.to(change(subject, :active).from(true).to(false)) + end + end + + describe ".activate!" do + subject { build :user, active: false } + + it "changes the active from false to true" do + expect { subject.activate! }.to(change(subject, :active).from(false).to(true)) + end + end end - it "saves" do - expect(user.save).to be true + describe ".active_for_authentication?" do + subject { build :user } + + it "returns true" do + expect(subject.active_for_authentication?).to be true + end + + context "when active is false" do + subject { build :user, active: false } + + it "returns false" do + expect(subject.active_for_authentication?).to be false + end + end end - it "updates password" do - user.save - user.update(password: "admin123") - user.reload - expect(user.password).to eq("admin123") + describe ".admin?" do + subject { build :user, :as_admin } + + it "returns true" do + expect(subject.admin?).to be true + end + + context "when the role is Customer" do + subject { build :user } + + it "returns false" do + expect(subject.admin?).to be false + end + end end - it "does not update new password similar to old" do - user.save - expect(user.update(password: "pass1234")).to be false + describe ".customer?" do + subject { build :user } + + it "returns true" do + expect(subject.customer?).to be true + end + + context "when the role is Administrator" do + subject { build :user, :as_admin } + + it "returns false" do + expect(subject.customer?).to be false + end + end end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 8be8af6..40d9c36 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -1,5 +1,8 @@ # frozen_string_literal: true +require "simplecov" +SimpleCov.start { add_filter("spec/support") } + # This file is copied to spec/ when you run 'rails generate rspec:install' require "spec_helper" ENV["RAILS_ENV"] ||= "test" @@ -70,6 +73,13 @@ # arbitrary gems may also be filtered via: # config.filter_gems_from_backtrace("gem name") - config.include Devise::Test::IntegrationHelpers, type: :request - config.include Pagy::Backend, type: :view + # Wrap each test in Bullet api. + if Bullet.enable? + config.before { Bullet.start_request } + + config.after do + Bullet.perform_out_of_channel_notifications if Bullet.notification? + Bullet.end_request + end + end end diff --git a/spec/requests/admin/categories_spec.rb b/spec/requests/admin/categories_spec.rb index 3b2fc9f..904433a 100644 --- a/spec/requests/admin/categories_spec.rb +++ b/spec/requests/admin/categories_spec.rb @@ -14,17 +14,8 @@ # 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. -RSpec.describe "/admin/categories" do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass123", - first_name: "John", - last_name: "Doe" - ) - end +RSpec.describe "/admin/categories", type: :request do + let!(:admin) { create :user, :as_admin } # This should return the minimal set of attributes required to create a valid # Category. As you add validations to Category, be sure to @@ -33,20 +24,25 @@ let(:invalid_attributes) { { name: "" } } - before { sign_in(user) } + before { sign_in(admin) } describe "GET /index" do - it "renders a successful response" do - Category.create! valid_attributes + before do + create_list(:category, 2) get admin_categories_url + end + + it "renders a successful response" do expect(response).to be_successful end end describe "GET /show" do + let!(:category) { create :category } + + before { get admin_category_url(category) } + it "renders a successful response" do - category = Category.create! valid_attributes - get admin_category_url(category) expect(response).to be_successful end end @@ -59,9 +55,11 @@ end describe "GET /edit" do + let!(:category) { create :category } + + before { get edit_admin_category_url(category) } + it "renders a successful response" do - category = Category.create! valid_attributes - get edit_admin_category_url(category) expect(response).to be_successful end end @@ -97,18 +95,17 @@ end describe "PATCH /update" do - context "with valid parameters" do - let(:new_attributes) { { name: "Category2" } } + let!(:category) { create :category } + let(:new_attributes) { { name: "Category2" } } + context "with valid parameters" do it "updates the requested category" do - category = Category.create! valid_attributes - patch admin_category_url(category), params: { category: new_attributes } - category.reload - expect(category.name).to eq("Category2") + expect { patch admin_category_url(category), params: { category: new_attributes } }.to( + change { category.reload.name }.to("Category2") + ) end it "redirects to the category" do - category = Category.create! valid_attributes patch admin_category_url(category), params: { category: new_attributes } category.reload expect(response).to redirect_to(admin_category_url(category)) @@ -117,7 +114,6 @@ context "with invalid parameters" do it "renders a response with 422 status (i.e. to display the 'edit' template)" do - category = Category.create! valid_attributes patch admin_category_url(category), params: { category: invalid_attributes } expect(response).to have_http_status(:unprocessable_entity) end @@ -125,13 +121,13 @@ end describe "DELETE /destroy" do + let!(:category) { create :category } + it "destroys the requested category" do - category = Category.create! valid_attributes expect { delete admin_category_url(category) }.to change(Category, :count).by(-1) end it "redirects to the categories list" do - category = Category.create! valid_attributes delete admin_category_url(category) expect(response).to redirect_to(admin_categories_url) end diff --git a/spec/requests/admin/customers_spec.rb b/spec/requests/admin/customers_spec.rb index a1d60c8..f942bfd 100644 --- a/spec/requests/admin/customers_spec.rb +++ b/spec/requests/admin/customers_spec.rb @@ -2,19 +2,10 @@ require "rails_helper" -RSpec.describe "/admin/customers" do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass123", - first_name: "John", - last_name: "Doe" - ) - end +RSpec.describe "/admin/customers", type: :request do + let!(:admin) { create :user, :as_admin } - before { sign_in(user) } + before { sign_in(admin) } describe "GET /index" do it "returns http success" do diff --git a/spec/requests/admin/orders_spec.rb b/spec/requests/admin/orders_spec.rb index c231087..5e46562 100644 --- a/spec/requests/admin/orders_spec.rb +++ b/spec/requests/admin/orders_spec.rb @@ -14,42 +14,11 @@ # 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. -RSpec.describe "/admin/orders" do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass123", - first_name: "John", - last_name: "Doe" - ) - end - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "Product", category:) } - let!(:stock) { product.stocks.create!(size: "L", quantity: 3) } - let!(:order) do - order = - Order.new( - user:, - customer_email: user.email, - customer_full_name: user.full_name, - customer_address: "my address 1" - ) - order.order_items.build( - product:, - stock:, - order_code: order.order_code, - product_name: product.name, - product_price: product.price, - size: stock.size, - quantity: 2 - ) - order.save! - order - end +RSpec.describe "/admin/orders", type: :request do + let!(:admin) { create :user, :as_admin } + let!(:order) { create :order } - before { sign_in(user) } + before { sign_in(admin) } describe "GET /index" do it "renders a successful response" do diff --git a/spec/requests/admin/products_spec.rb b/spec/requests/admin/products_spec.rb index ce85b5b..9c5fb8a 100644 --- a/spec/requests/admin/products_spec.rb +++ b/spec/requests/admin/products_spec.rb @@ -14,19 +14,9 @@ # 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. -RSpec.describe "/admin/products" do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass123", - first_name: "John", - last_name: "Doe" - ) - end - - let!(:category) { Category.create!(name: "Category") } +RSpec.describe "/admin/products", type: :request do + let!(:admin) { create :user, :as_admin } + let!(:category) { create :category } # This should return the minimal set of attributes required to create a valid # Product. As you add validations to Product, be sure to @@ -35,20 +25,25 @@ let(:invalid_attributes) { { name: "" } } - before { sign_in(user) } + before { sign_in(admin) } describe "GET /index" do - it "renders a successful response" do - Product.create! valid_attributes + before do + create_list(:product, 2) get admin_products_url + end + + it "renders a successful response" do expect(response).to be_successful end end describe "GET /show" do + let!(:product) { create :product } + + before { get admin_product_url(product) } + it "renders a successful response" do - product = Product.create! valid_attributes - get admin_product_url(product) expect(response).to be_successful end end @@ -61,9 +56,11 @@ end describe "GET /edit" do + let!(:product) { create :product } + + before { get edit_admin_product_url(product) } + it "renders a successful response" do - product = Product.create! valid_attributes - get edit_admin_product_url(product) expect(response).to be_successful end end @@ -98,18 +95,17 @@ end describe "PATCH /update" do - context "with valid parameters" do - let(:new_attributes) { { name: "Product2" } } + let!(:product) { create :product } + let(:new_attributes) { { name: "Product2" } } + context "with valid parameters" do it "updates the requested product" do - product = Product.create! valid_attributes - patch admin_product_url(product), params: { product: new_attributes } - product.reload - expect(product.name).to eq("Product2") + expect { patch admin_product_url(product), params: { product: new_attributes } }.to( + change { product.reload.name }.to("Product2") + ) end it "redirects to the product" do - product = Product.create! valid_attributes patch admin_product_url(product), params: { product: new_attributes } product.reload expect(response).to redirect_to(admin_product_url(product)) @@ -118,7 +114,6 @@ context "with invalid parameters" do it "renders a response with 422 status (i.e. to display the 'edit' template)" do - product = Product.create! valid_attributes patch admin_product_url(product), params: { product: invalid_attributes } expect(response).to have_http_status(:unprocessable_entity) end @@ -126,13 +121,13 @@ end describe "DELETE /destroy" do + let!(:product) { create :product } + it "destroys the requested product" do - product = Product.create! valid_attributes expect { delete admin_product_url(product) }.to change(Product, :count).by(-1) end it "redirects to the products list" do - product = Product.create! valid_attributes delete admin_product_url(product) expect(response).to redirect_to(admin_products_url) end diff --git a/spec/requests/admin/stocks_spec.rb b/spec/requests/admin/stocks_spec.rb index af2cce6..ca22c3d 100644 --- a/spec/requests/admin/stocks_spec.rb +++ b/spec/requests/admin/stocks_spec.rb @@ -14,19 +14,9 @@ # 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. -RSpec.describe "/admin/products/1/stocks" do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass123", - first_name: "John", - last_name: "Doe" - ) - end - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "Product", category:) } +RSpec.describe "/admin/products/1/stocks", type: :request do + let!(:admin) { create :user, :as_admin } + let!(:product) { create :product, :with_stocks } # This should return the minimal set of attributes required to create a valid # Stock. As you add validations to Stock, be sure to @@ -35,20 +25,22 @@ let(:invalid_attributes) { { size: "" } } - before { sign_in(user) } + before { sign_in(admin) } describe "GET /index" do + before { get admin_product_stocks_url(product) } + it "renders a successful response" do - Stock.create! valid_attributes - get admin_product_stocks_url(product) expect(response).to be_successful end end describe "GET /show" do + let(:stock) { product.stocks.first } + + before { get admin_product_stock_url(product, stock) } + it "renders a successful response" do - stock = Stock.create! valid_attributes - get admin_product_stock_url(product, stock) expect(response).to be_successful end end @@ -61,9 +53,11 @@ end describe "GET /edit" do + let(:stock) { product.stocks.first } + + before { get edit_admin_product_stock_url(product, stock) } + it "renders a successful response" do - stock = Stock.create! valid_attributes - get edit_admin_product_stock_url(product, stock) expect(response).to be_successful end end @@ -99,18 +93,17 @@ end describe "PATCH /update" do - context "with valid parameters" do # rubocop:disable RSpec/MultipleMemoizedHelpers - let(:new_attributes) { { quantity: 8 } } + let(:stock) { product.stocks.first } + let(:new_attributes) { { quantity: 8 } } + context "with valid parameters" do # rubocop:disable RSpec/MultipleMemoizedHelpers it "updates the requested stock" do - stock = Stock.create! valid_attributes - patch admin_product_stock_url(product, stock), params: { stock: new_attributes } - stock.reload - expect(stock.quantity).to eq(8) + expect { patch admin_product_stock_url(product, stock), params: { stock: new_attributes } }.to( + change { stock.reload.quantity }.to(8) + ) end it "redirects to the stock" do - stock = Stock.create! valid_attributes patch admin_product_stock_url(product, stock), params: { stock: new_attributes } stock.reload expect(response).to redirect_to(admin_product_stock_url(product, stock)) @@ -119,7 +112,6 @@ context "with invalid parameters" do it "renders a response with 422 status (i.e. to display the 'edit' template)" do - stock = Stock.create! valid_attributes patch admin_product_stock_url(product, stock), params: { stock: invalid_attributes } expect(response).to have_http_status(:unprocessable_entity) end @@ -127,13 +119,13 @@ end describe "DELETE /destroy" do + let(:stock) { product.stocks.first } + it "destroys the requested stock" do - stock = Stock.create! valid_attributes expect { delete admin_product_stock_url(product, stock) }.to change(Stock, :count).by(-1) end it "redirects to the stocks list" do - stock = Stock.create! valid_attributes delete admin_product_stock_url(product, stock) expect(response).to redirect_to(admin_product_stocks_url) end diff --git a/spec/requests/admin/subscribers_spec.rb b/spec/requests/admin/subscribers_spec.rb index 88d61ac..4f09de5 100644 --- a/spec/requests/admin/subscribers_spec.rb +++ b/spec/requests/admin/subscribers_spec.rb @@ -14,29 +14,18 @@ # 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. -RSpec.describe "/admin/subscribers" do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass123", - first_name: "John", - last_name: "Doe" - ) - end - - # This should return the minimal set of attributes required to create a valid - # Subscriber. As you add validations to Subscriber, be sure to - # adjust the attributes here as well. - let(:valid_attributes) { { email: "jd@gmail.com" } } +RSpec.describe "/admin/subscribers", type: :request do + let!(:admin) { create :user, :as_admin } - before { sign_in(user) } + before { sign_in(admin) } describe "GET /index" do - it "renders a successful response" do - Subscriber.create! valid_attributes + before do + create_list(:subscriber, 2) get admin_subscribers_url + end + + it "renders a successful response" do expect(response).to be_successful end end diff --git a/spec/requests/site/cart_spec.rb b/spec/requests/site/cart_spec.rb index 74aa046..5a968ac 100644 --- a/spec/requests/site/cart_spec.rb +++ b/spec/requests/site/cart_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe "Site::Cart" do +RSpec.describe "Site::Cart", type: :request do describe "GET /show" do it "returns http success" do get "/cart" diff --git a/spec/requests/site/categories_spec.rb b/spec/requests/site/categories_spec.rb index 97f25e2..ac6cd8e 100644 --- a/spec/requests/site/categories_spec.rb +++ b/spec/requests/site/categories_spec.rb @@ -2,11 +2,13 @@ require "rails_helper" -RSpec.describe "/categories" do +RSpec.describe "/categories", type: :request do describe "GET /show" do + let!(:category) { create :category } + + before { get category_url(category) } + it "renders a successful response" do - category = Category.create!(name: "Category") - get category_url(category) expect(response).to be_successful end end diff --git a/spec/requests/site/products_spec.rb b/spec/requests/site/products_spec.rb index c4b201f..57445e3 100644 --- a/spec/requests/site/products_spec.rb +++ b/spec/requests/site/products_spec.rb @@ -2,13 +2,13 @@ require "rails_helper" -RSpec.describe "/products" do +RSpec.describe "/products", type: :request do describe "GET /show" do + let!(:product) { create :product, :with_stocks } + + before { get product_url(product) } + it "renders a successful response (i.e. active product with stocks quantity > 0)" do - category = Category.create!(name: "Category") - product = category.products.create!(name: "Product") - product.stocks.create!(size: "L", quantity: 3) - get product_url(product) expect(response).to be_successful end end diff --git a/spec/routing/admin/categories_routing_spec.rb b/spec/routing/admin/categories_routing_spec.rb index d4d53bd..d346987 100644 --- a/spec/routing/admin/categories_routing_spec.rb +++ b/spec/routing/admin/categories_routing_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe Admin::CategoriesController do +RSpec.describe Admin::CategoriesController, type: :routing do describe "routing" do it "routes to #index" do expect(get: "/admin/categories").to route_to("admin/categories#index") diff --git a/spec/routing/admin/orders_routing_spec.rb b/spec/routing/admin/orders_routing_spec.rb index 198664a..ef6c7d8 100644 --- a/spec/routing/admin/orders_routing_spec.rb +++ b/spec/routing/admin/orders_routing_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe Admin::OrdersController do +RSpec.describe Admin::OrdersController, type: :routing do describe "routing" do it "routes to #index" do expect(get: "/admin/orders").to route_to("admin/orders#index") diff --git a/spec/routing/admin/products_routing_spec.rb b/spec/routing/admin/products_routing_spec.rb index 962d12f..c8a9a6b 100644 --- a/spec/routing/admin/products_routing_spec.rb +++ b/spec/routing/admin/products_routing_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe Admin::ProductsController do +RSpec.describe Admin::ProductsController, type: :routing do describe "routing" do it "routes to #index" do expect(get: "/admin/products").to route_to("admin/products#index") diff --git a/spec/routing/admin/stocks_routing_spec.rb b/spec/routing/admin/stocks_routing_spec.rb index 9301a03..1700b20 100644 --- a/spec/routing/admin/stocks_routing_spec.rb +++ b/spec/routing/admin/stocks_routing_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe Admin::StocksController do +RSpec.describe Admin::StocksController, type: :routing do describe "routing" do it "routes to #index" do expect(get: "/admin/products/1/stocks").to route_to("admin/stocks#index", product_slug: "1") diff --git a/spec/routing/admin/subscribers_routing_spec.rb b/spec/routing/admin/subscribers_routing_spec.rb index ebd5b8d..d164e97 100644 --- a/spec/routing/admin/subscribers_routing_spec.rb +++ b/spec/routing/admin/subscribers_routing_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe Admin::SubscribersController do +RSpec.describe Admin::SubscribersController, type: :routing do describe "routing" do it "routes to #index" do expect(get: "/admin/subscribers").to route_to("admin/subscribers#index") diff --git a/spec/support/devise.rb b/spec/support/devise.rb new file mode 100644 index 0000000..a4ef26c --- /dev/null +++ b/spec/support/devise.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +RSpec.configure { |config| config.include Devise::Test::IntegrationHelpers, type: :request } diff --git a/spec/support/factory_bot.rb b/spec/support/factory_bot.rb new file mode 100644 index 0000000..30096b6 --- /dev/null +++ b/spec/support/factory_bot.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +RSpec.configure { |config| config.include FactoryBot::Syntax::Methods } diff --git a/spec/support/pagy.rb b/spec/support/pagy.rb new file mode 100644 index 0000000..a85522e --- /dev/null +++ b/spec/support/pagy.rb @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +RSpec.configure { |config| config.include Pagy::Backend, type: :view } diff --git a/spec/support/shoulda.rb b/spec/support/shoulda.rb new file mode 100644 index 0000000..edcf9dd --- /dev/null +++ b/spec/support/shoulda.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end diff --git a/spec/views/admin/categories/edit.html.slim_spec.rb b/spec/views/admin/categories/edit.html.slim_spec.rb index 623e3b0..5b581f4 100644 --- a/spec/views/admin/categories/edit.html.slim_spec.rb +++ b/spec/views/admin/categories/edit.html.slim_spec.rb @@ -2,8 +2,8 @@ require "rails_helper" -RSpec.describe "admin/categories/edit" do - let(:category) { Category.create!(name: "MyString", description: "MyText") } +RSpec.describe "admin/categories/edit", type: :view do + let!(:category) { create :category } before { assign(:category, category) } diff --git a/spec/views/admin/categories/index.html.slim_spec.rb b/spec/views/admin/categories/index.html.slim_spec.rb index 6aa5958..d4496d7 100644 --- a/spec/views/admin/categories/index.html.slim_spec.rb +++ b/spec/views/admin/categories/index.html.slim_spec.rb @@ -2,22 +2,14 @@ require "rails_helper" -RSpec.describe "admin/categories/index" do - before do - @pagy, @categories = - pagy_array( - [ - Category.create!(name: "MyCategory1", description: "MyText"), - Category.create!(name: "MyCategoryName", description: "MyText") - ] - ) - end +RSpec.describe "admin/categories/index", type: :view do + before { @pagy, @categories = pagy_array([create(:category), create(:category)]) } it "renders a list of admin/categories" do render name_selector = "tr>th" description_selector = "tr>td" - assert_select name_selector, text: Regexp.new("MyCategory1".to_s), count: 1 - assert_select description_selector, text: Regexp.new("MyText".to_s), count: 2 + assert_select name_selector, text: Regexp.new("Category"), count: 2 + assert_select description_selector, text: Regexp.new("Description"), count: 2 end end diff --git a/spec/views/admin/categories/new.html.slim_spec.rb b/spec/views/admin/categories/new.html.slim_spec.rb index bfead15..95242bc 100644 --- a/spec/views/admin/categories/new.html.slim_spec.rb +++ b/spec/views/admin/categories/new.html.slim_spec.rb @@ -2,8 +2,8 @@ require "rails_helper" -RSpec.describe "admin/categories/new" do - before { assign(:category, Category.new(name: "MyString", description: "MyText")) } +RSpec.describe "admin/categories/new", type: :view do + before { assign(:category, build(:category)) } it "renders new admin_category form" do render diff --git a/spec/views/admin/categories/show.html.slim_spec.rb b/spec/views/admin/categories/show.html.slim_spec.rb index cd9be0b..63acb79 100644 --- a/spec/views/admin/categories/show.html.slim_spec.rb +++ b/spec/views/admin/categories/show.html.slim_spec.rb @@ -2,8 +2,8 @@ require "rails_helper" -RSpec.describe "admin/categories/show" do - let!(:category) { Category.create!(name: "Name1", description: "MyText") } +RSpec.describe "admin/categories/show", type: :view do + let!(:category) { create :category } before do assign(:category, category) @@ -16,7 +16,7 @@ it "renders attributes in

" do render - expect(rendered).to match(/Name1/) - expect(rendered).to match(/MyText/) + expect(rendered).to match(/Category/) + expect(rendered).to match(/Description/) end end diff --git a/spec/views/admin/orders/index.html.slim_spec.rb b/spec/views/admin/orders/index.html.slim_spec.rb index f76a8d7..02efc18 100644 --- a/spec/views/admin/orders/index.html.slim_spec.rb +++ b/spec/views/admin/orders/index.html.slim_spec.rb @@ -2,48 +2,18 @@ require "rails_helper" -RSpec.describe "admin/orders/index" do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass1234", - first_name: "John", - last_name: "Doe" - ) - end - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "MyProduct", category:, price: 9.99) } - let!(:stock) { product.stocks.create!(size: "L", quantity: 4) } - let!(:order) do - order = - Order.new( - user:, - customer_email: user.email, - customer_full_name: user.full_name, - customer_address: "my address 1" - ) - order.order_items.build( - product:, - stock:, - order_code: order.order_code, - product_name: product.name, - product_price: product.price, - size: stock.size, - quantity: 2 - ) - order.save! - order - end +RSpec.describe "admin/orders/index", type: :view do + let!(:user) { create :user, first_name: "John", last_name: "Doe" } + let!(:order) { create :order, user: } + let(:order2) { create :order, user: } - before { @pagy, @orders = pagy_array([order, order]) } + before { @pagy, @orders = pagy_array([order, order2]) } it "renders a list of admin/orders" do render order_id_selector = "tr>th" cell_selector = "tr>td" - assert_select order_id_selector, text: Regexp.new("SL".to_s), count: 2 - assert_select cell_selector, text: Regexp.new("John Doe".to_s), count: 2 + assert_select order_id_selector, text: Regexp.new("SL"), count: 2 + assert_select cell_selector, text: Regexp.new("John Doe"), count: 2 end end diff --git a/spec/views/admin/orders/show.html.slim_spec.rb b/spec/views/admin/orders/show.html.slim_spec.rb index 98908d9..c0b8618 100644 --- a/spec/views/admin/orders/show.html.slim_spec.rb +++ b/spec/views/admin/orders/show.html.slim_spec.rb @@ -2,40 +2,9 @@ require "rails_helper" -RSpec.describe "admin/orders/show" do - let!(:user) do - User.create!( - role: Role.find_or_create_by!(name: "Administrator"), - gender: "male", - email: "jd@gmail.com", - password: "pass1234", - first_name: "John", - last_name: "Doe" - ) - end - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "Product", category:, price: 9.99) } - let!(:stock) { product.stocks.create!(size: "L", quantity: 4) } - let!(:order) do - order = - Order.new( - user:, - customer_email: user.email, - customer_full_name: user.full_name, - customer_address: "Customer Address" - ) - order.order_items.build( - product:, - stock:, - order_code: order.order_code, - product_name: product.name, - product_price: product.price, - size: stock.size, - quantity: 2 - ) - order.save! - order - end +RSpec.describe "admin/orders/show", type: :view do + let!(:user) { create :user, email: "jd@gmail.com", first_name: "John", last_name: "Doe" } + let!(:order) { create :order, user:, customer_address: "Customer Address" } before { assign(:order, order) } diff --git a/spec/views/admin/products/edit.html.slim_spec.rb b/spec/views/admin/products/edit.html.slim_spec.rb index 79b9f33..1db3198 100644 --- a/spec/views/admin/products/edit.html.slim_spec.rb +++ b/spec/views/admin/products/edit.html.slim_spec.rb @@ -2,11 +2,9 @@ require "rails_helper" -RSpec.describe "admin/products/edit" do +RSpec.describe "admin/products/edit", type: :view do let!(:categories) { Category.active } - let!(:category) { Category.create!(name: "Category") } - - let(:product) { Product.create!(name: "MyString", description: "MyText", price: "9.99", category:) } + let!(:product) { create :product } before do assign(:categories, categories) diff --git a/spec/views/admin/products/index.html.slim_spec.rb b/spec/views/admin/products/index.html.slim_spec.rb index 08fe43f..36a74ee 100644 --- a/spec/views/admin/products/index.html.slim_spec.rb +++ b/spec/views/admin/products/index.html.slim_spec.rb @@ -2,24 +2,14 @@ require "rails_helper" -RSpec.describe "admin/products/index" do - let!(:category) { Category.create!(name: "Category") } - - before do - @pagy, @products = - pagy_array( - [ - Product.create!(name: "Product", description: "MyText", price: "9.99", category:), - Product.create!(name: "Name", description: "MyText", price: "9.99", category:) - ] - ) - end +RSpec.describe "admin/products/index", type: :view do + before { @pagy, @products = pagy_array([create(:product), create(:product)]) } it "renders a list of admin/products" do render name_selector = "tr>th" cell_selector = "tr>td" - assert_select name_selector, text: Regexp.new("Product".to_s), count: 1 - assert_select cell_selector, text: Regexp.new("9.99".to_s), count: 2 + assert_select name_selector, text: Regexp.new("Product"), count: 2 + assert_select cell_selector, text: Regexp.new("100"), count: 2 end end diff --git a/spec/views/admin/products/new.html.slim_spec.rb b/spec/views/admin/products/new.html.slim_spec.rb index 2fe2e76..72a034b 100644 --- a/spec/views/admin/products/new.html.slim_spec.rb +++ b/spec/views/admin/products/new.html.slim_spec.rb @@ -2,12 +2,12 @@ require "rails_helper" -RSpec.describe "admin/products/new" do +RSpec.describe "admin/products/new", type: :view do let!(:categories) { Category.active } before do assign(:categories, categories) - assign(:product, Product.new(name: "MyString", description: "MyText", price: "9.99")) + assign(:product, build(:product)) end it "renders new product form" do diff --git a/spec/views/admin/products/show.html.slim_spec.rb b/spec/views/admin/products/show.html.slim_spec.rb index 1fa9cdc..5a49b37 100644 --- a/spec/views/admin/products/show.html.slim_spec.rb +++ b/spec/views/admin/products/show.html.slim_spec.rb @@ -2,16 +2,15 @@ require "rails_helper" -RSpec.describe "admin/products/show" do - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "Name", description: "MyText", price: "9.99", category:) } +RSpec.describe "admin/products/show", type: :view do + let!(:product) { create :product } before { assign(:product, product) } it "renders attributes in

" do render - expect(rendered).to match(/Name/) - expect(rendered).to match(/MyText/) - expect(rendered).to match(/9.99/) + expect(rendered).to match(/Product/) + expect(rendered).to match(/Description/) + expect(rendered).to match(/100/) end end diff --git a/spec/views/admin/stocks/edit.html.slim_spec.rb b/spec/views/admin/stocks/edit.html.slim_spec.rb index 1f6ed2c..d8c6796 100644 --- a/spec/views/admin/stocks/edit.html.slim_spec.rb +++ b/spec/views/admin/stocks/edit.html.slim_spec.rb @@ -2,10 +2,9 @@ require "rails_helper" -RSpec.describe "admin/stocks/edit" do - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "MyString", description: "MyText", price: "9.99", category:) } - let(:stock) { Stock.create!(product:, size: "MyString", quantity: 1) } +RSpec.describe "admin/stocks/edit", type: :view do + let!(:product) { create :product, :with_stocks, stocks_count: 1 } + let(:stock) { product.stocks.first } before do assign(:product, product) diff --git a/spec/views/admin/stocks/index.html.slim_spec.rb b/spec/views/admin/stocks/index.html.slim_spec.rb index 9f2bf62..7d059fd 100644 --- a/spec/views/admin/stocks/index.html.slim_spec.rb +++ b/spec/views/admin/stocks/index.html.slim_spec.rb @@ -2,23 +2,19 @@ require "rails_helper" -RSpec.describe "admin/stocks/index" do - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "MyString", description: "MyText", price: "9.99", category:) } +RSpec.describe "admin/stocks/index", type: :view do + let!(:product) { create :product, :with_stocks } before do assign(:product, product) - assign( - :stocks, - [Stock.create!(product:, size: "M", quantity: 2), Stock.create!(product:, size: "L", quantity: 2)] - ) + assign(:stocks, product.stocks) end it "renders a list of admin/stocks" do render size_selector = "tr>th" quantity_selector = "tr>td" - assert_select size_selector, text: Regexp.new("M".to_s), count: 1 - assert_select quantity_selector, text: Regexp.new(2.to_s), count: 2 + assert_select size_selector, text: Regexp.new("SZ"), count: 2 + assert_select quantity_selector, text: Regexp.new("50"), count: 2 end end diff --git a/spec/views/admin/stocks/new.html.slim_spec.rb b/spec/views/admin/stocks/new.html.slim_spec.rb index 9f3ea2a..ce68b08 100644 --- a/spec/views/admin/stocks/new.html.slim_spec.rb +++ b/spec/views/admin/stocks/new.html.slim_spec.rb @@ -2,13 +2,12 @@ require "rails_helper" -RSpec.describe "admin/stocks/new" do - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "MyString", description: "MyText", price: "9.99", category:) } +RSpec.describe "admin/stocks/new", type: :view do + let!(:product) { create :product } before do assign(:product, product) - assign(:stock, Stock.new(product:, size: "MyString", quantity: 1)) + assign(:stock, build(:stock, product:)) end it "renders new stock form" do diff --git a/spec/views/admin/stocks/show.html.slim_spec.rb b/spec/views/admin/stocks/show.html.slim_spec.rb index 640e172..c965f3a 100644 --- a/spec/views/admin/stocks/show.html.slim_spec.rb +++ b/spec/views/admin/stocks/show.html.slim_spec.rb @@ -2,10 +2,9 @@ require "rails_helper" -RSpec.describe "admin/stocks/show" do - let!(:category) { Category.create!(name: "Category") } - let!(:product) { Product.create!(name: "Name", description: "MyText", price: "9.99", category:) } - let!(:stock) { Stock.create!(product:, size: "Size", quantity: 2) } +RSpec.describe "admin/stocks/show", type: :view do + let!(:product) { create :product, :with_stocks, stocks_count: 1 } + let(:stock) { product.stocks.first } before do assign(:product, product) @@ -14,7 +13,7 @@ it "renders attributes in

" do render - expect(rendered).to match(/Size/) - expect(rendered).to match(/2/) + expect(rendered).to match(/SZ/) + expect(rendered).to match(/50/) end end diff --git a/spec/views/admin/subscribers/index.html.slim_spec.rb b/spec/views/admin/subscribers/index.html.slim_spec.rb index 8685f42..97bbf21 100644 --- a/spec/views/admin/subscribers/index.html.slim_spec.rb +++ b/spec/views/admin/subscribers/index.html.slim_spec.rb @@ -2,15 +2,15 @@ require "rails_helper" -RSpec.describe "admin/subscribers/index" do - let!(:subscriber_john) { Subscriber.create!(email: "jd@gmail.com") } - let!(:subscriber_jane) { Subscriber.create!(email: "jd@email.com") } +RSpec.describe "admin/subscribers/index", type: :view do + let!(:subscriber_john) { create :subscriber, email: "jd@gmail.com" } + let!(:subscriber_jane) { create :subscriber, email: "jd@email.com" } before { @pagy, @subscribers = pagy_array([subscriber_john, subscriber_jane]) } it "renders a list of admin/subscribers" do render cell_selector = "tr>td" - assert_select cell_selector, text: Regexp.new("jd".to_s), count: 2 + assert_select cell_selector, text: Regexp.new("jd"), count: 2 end end diff --git a/spec/views/site/cart/show.html.slim_spec.rb b/spec/views/site/cart/show.html.slim_spec.rb index 83b5115..b2534ae 100644 --- a/spec/views/site/cart/show.html.slim_spec.rb +++ b/spec/views/site/cart/show.html.slim_spec.rb @@ -2,7 +2,7 @@ require "rails_helper" -RSpec.describe "site/cart/show.html.slim" do +RSpec.describe "site/cart/show.html.slim", type: :view do it "renders cart label" do render expect(rendered).to match(/cart/) diff --git a/spec/views/site/categories/show.html.slim_spec.rb b/spec/views/site/categories/show.html.slim_spec.rb index 3c7d124..283964a 100644 --- a/spec/views/site/categories/show.html.slim_spec.rb +++ b/spec/views/site/categories/show.html.slim_spec.rb @@ -2,8 +2,8 @@ require "rails_helper" -RSpec.describe "site/categories/show" do - let!(:category) { Category.create!(name: "Name2", description: "MyText") } +RSpec.describe "site/categories/show", type: :view do + let!(:category) { create :category } before do assign(:category, category) @@ -12,6 +12,6 @@ it "renders attribute" do render - expect(rendered).to match(/Name2/) + expect(rendered).to match(/Category/) end end diff --git a/spec/views/site/products/show.html.slim_spec.rb b/spec/views/site/products/show.html.slim_spec.rb index 530f8cb..d969570 100644 --- a/spec/views/site/products/show.html.slim_spec.rb +++ b/spec/views/site/products/show.html.slim_spec.rb @@ -2,9 +2,8 @@ require "rails_helper" -RSpec.describe "site/products/show" do - let!(:category) { Category.create!(name: "Name3", description: "MyText") } - let!(:product) { Product.create!(name: "Name", description: "MyText", category:) } +RSpec.describe "site/products/show", type: :view do + let!(:product) { create :product } before do assign(:product, product) @@ -13,6 +12,6 @@ it "renders attribute" do render - expect(rendered).to match(/Name/) + expect(rendered).to match(/Product/) end end