diff --git a/Gemfile b/Gemfile index 0e16147..b6fe800 100644 --- a/Gemfile +++ b/Gemfile @@ -47,7 +47,6 @@ gem 'capybara' gem 'cuprite' gem 'rspec_junit_formatter' gem 'rspec-rails' -gem 'rspec-retry' gem 'simplecov', require: false # Linters diff --git a/spec/dummy/app/admin/posts.rb b/spec/dummy/app/admin/posts.rb index 7588c96..cd7f32f 100644 --- a/spec/dummy/app/admin/posts.rb +++ b/spec/dummy/app/admin/posts.rb @@ -44,7 +44,7 @@ ActiveAdmin.register Post do f.input :author f.input :title f.input :summary, as: :quill_editor, input_html: { data: { options: { modules: { toolbar: toolbar } } } } - f.input :description, as: :quill_editor, input_html: { data: { options: { modules: { toolbar: toolbar } } } } + f.input :description, as: :quill_editor # using default options f.input :category f.input :dt f.input :position diff --git a/spec/dummy/app/admin/tags.rb b/spec/dummy/app/admin/tags.rb index d4749f1..09beb57 100644 --- a/spec/dummy/app/admin/tags.rb +++ b/spec/dummy/app/admin/tags.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true ActiveAdmin.register Tag do + permit_params :name end diff --git a/spec/dummy/app/models/post.rb b/spec/dummy/app/models/post.rb index 3eb4c23..2dd30a5 100644 --- a/spec/dummy/app/models/post.rb +++ b/spec/dummy/app/models/post.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class Post < ApplicationRecord - # enum state: %i[available unavailable arriving] - belongs_to :author, inverse_of: :posts, autosave: true has_one :author_profile, through: :author, source: :profile @@ -28,12 +26,12 @@ class Post < ApplicationRecord end class << self - def ransackable_attributes(auth_object = nil) - %w[author_id category created_at description dt id position published summary title updated_at] + def ransackable_associations(_auth_object = nil) + %w[author author_profile post_tags tags images_attachments images_blobs] end - def ransackable_associations(auth_object = nil) - %w[author author_profile images_attachments images_blobs post_tags tags] + def ransackable_attributes(_auth_object = nil) + %w[author_id category created_at description dt id position published title summary updated_at] end end end diff --git a/spec/dummy/app/models/profile.rb b/spec/dummy/app/models/profile.rb index d6f373d..5f283c6 100644 --- a/spec/dummy/app/models/profile.rb +++ b/spec/dummy/app/models/profile.rb @@ -3,14 +3,12 @@ class Profile < ApplicationRecord belongs_to :author, inverse_of: :profile, touch: true - # has_rich_text :description - - # def to_s - # description - # end + def to_s + description + end class << self - def ransackable_associations(auth_object = nil) + def ransackable_associations(_auth_object = nil) %w[author] end diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb index 7107cdb..2be147b 100644 --- a/spec/dummy/config/application.rb +++ b/spec/dummy/config/application.rb @@ -1,26 +1,23 @@ require_relative 'boot' require 'rails/all' -require 'sprockets/railtie' Bundler.require(*Rails.groups) module Dummy class Application < Rails::Application - # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 6.0 if Rails::VERSION::MAJOR == 6 + config.load_defaults Rails::VERSION::STRING.to_f - # Settings in config/environments/* take precedence over those specified here. - # Application configuration can go into files in config/initializers - # -- all .rb files in that directory are automatically loaded after loading - # the framework and any gems in your application. + config.active_support.deprecation = :raise - ### + if Gem::Version.new(Rails.version) < Gem::Version.new('7.1') + config.active_record.legacy_connection_handling = false + end - config.active_record.legacy_connection_handling = false if Gem::Version.new(Rails.version) < Gem::Version.new('7.1') - - config.before_configuration do - ActiveSupport::Cache.format_version = 7.0 if Gem::Version.new(Rails.version) > Gem::Version.new('7.0') + if Gem::Version.new(Rails.version) > Gem::Version.new('7.0') + config.before_configuration do + ActiveSupport::Cache.format_version = 7.0 + end end end end diff --git a/spec/dummy/config/database.yml b/spec/dummy/config/database.yml index 58e2f54..9cf0a49 100644 --- a/spec/dummy/config/database.yml +++ b/spec/dummy/config/database.yml @@ -6,7 +6,7 @@ default: &default development: <<: *default database: db/development.sqlite3 - schema_dump: schema_development.rb + schema_dump: schema-dev.rb test: <<: *default diff --git a/spec/dummy/config/initializers/active_storage.rb b/spec/dummy/config/initializers/active_storage.rb index 1a03b5b..c32e30e 100644 --- a/spec/dummy/config/initializers/active_storage.rb +++ b/spec/dummy/config/initializers/active_storage.rb @@ -1,7 +1,9 @@ -ActiveSupport::Reloader.to_prepare do +Rails.application.reloader.to_prepare do ActiveStorage::Attachment.class_eval do - def self.ransackable_attributes(auth_object = nil) - %w[blob_id created_at id name record_id record_type] + class << self + def ransackable_attributes(auth_object = nil) + %w[blob_id created_at id name record_id record_type] + end end end end diff --git a/spec/dummy/db/schema_development.rb b/spec/dummy/db/schema_development.rb deleted file mode 100644 index 4ea933c..0000000 --- a/spec/dummy/db/schema_development.rb +++ /dev/null @@ -1,99 +0,0 @@ -# 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.define(version: 2018_06_07_053739) do - create_table "active_admin_comments", force: :cascade do |t| - t.string "namespace" - t.text "body" - t.string "resource_type" - t.integer "resource_id" - t.string "author_type" - t.integer "author_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["author_type", "author_id"], name: "index_active_admin_comments_on_author_type_and_author_id" - t.index ["namespace"], name: "index_active_admin_comments_on_namespace" - t.index ["resource_type", "resource_id"], name: "index_active_admin_comments_on_resource_type_and_resource_id" - end - - create_table "active_storage_attachments", force: :cascade do |t| - t.string "name", null: false - t.string "record_type", null: false - t.integer "record_id", null: false - t.integer "blob_id", null: false - t.datetime "created_at", precision: nil, null: false - t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" - t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true - end - - create_table "active_storage_blobs", force: :cascade do |t| - t.string "key", null: false - t.string "filename", null: false - t.string "content_type" - t.text "metadata" - t.bigint "byte_size", null: false - t.string "checksum", null: false - t.datetime "created_at", precision: nil, null: false - t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true - end - - create_table "authors", force: :cascade do |t| - t.string "name" - t.integer "age" - t.string "email" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - end - - create_table "post_tags", force: :cascade do |t| - t.integer "post_id" - t.integer "tag_id" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.index ["post_id"], name: "index_post_tags_on_post_id" - t.index ["tag_id"], name: "index_post_tags_on_tag_id" - end - - create_table "posts", force: :cascade do |t| - t.string "title" - t.text "summary" - t.text "description" - t.integer "author_id" - t.string "category" - t.datetime "dt", precision: nil - t.float "position" - t.boolean "published" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.index ["author_id"], name: "index_posts_on_author_id" - end - - create_table "profiles", force: :cascade do |t| - t.text "description" - t.integer "author_id" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - t.index ["author_id"], name: "index_profiles_on_author_id" - end - - create_table "tags", force: :cascade do |t| - t.string "name" - t.datetime "created_at", precision: nil, null: false - t.datetime "updated_at", precision: nil, null: false - end - - add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" - add_foreign_key "post_tags", "posts" - add_foreign_key "post_tags", "tags" - add_foreign_key "posts", "authors" - add_foreign_key "profiles", "authors" -end diff --git a/spec/dummy/db/seeds.rb b/spec/dummy/db/seeds.rb index 48a94e1..acc6e83 100644 --- a/spec/dummy/db/seeds.rb +++ b/spec/dummy/db/seeds.rb @@ -7,7 +7,8 @@ end (11..20).each do |i| age = 21 + 3 * (i - 10) attrs = { name: "Author #{i}", age: age, email: "some@email#{i}.com" } - Author.find_or_create_by!(attrs) do |author| + Author.find_or_create_by!(name: "Author #{i}") do |author| + author.assign_attributes(attrs) author.profile = Profile.new(description: "Profile description for Author #{i}") if (i % 3).zero? end end @@ -19,6 +20,8 @@ tags = Tag.where.not(name: 'A test tag').pluck(:id) title: "Post #{i}", author_id: authors.sample, position: rand(100), + summary: "

Summary for post #{i}

", + description: "

Some bold Some italic Some underline [#{i}]

", created_at: Time.now - rand(3600).seconds } attrs[:category] = 'news' if (i % 4).zero? diff --git a/spec/page_objects/admin/authors/edit_page.rb b/spec/page_objects/admin/authors/edit_page.rb index 542ed12..5564340 100644 --- a/spec/page_objects/admin/authors/edit_page.rb +++ b/spec/page_objects/admin/authors/edit_page.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative '../../base_page' - module Admin module Authors class EditPage < BasePage diff --git a/spec/page_objects/admin/posts/edit_page.rb b/spec/page_objects/admin/posts/edit_page.rb index 2c2d49d..f42c47b 100644 --- a/spec/page_objects/admin/posts/edit_page.rb +++ b/spec/page_objects/admin/posts/edit_page.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require_relative '../../base_page' - module Admin module Posts class EditPage < BasePage diff --git a/spec/page_objects/base_page.rb b/spec/page_objects/base_page.rb index 861d7aa..83c80d6 100644 --- a/spec/page_objects/base_page.rb +++ b/spec/page_objects/base_page.rb @@ -12,4 +12,8 @@ class BasePage def load visit(path) end + + def lookup_editor(editor_container:) + @editor = Shared::QuillEditor.new(editor_container) + end end diff --git a/spec/page_objects/shared/html_editor.rb b/spec/page_objects/shared/html_editor.rb index b8233ed..d74b934 100644 --- a/spec/page_objects/shared/html_editor.rb +++ b/spec/page_objects/shared/html_editor.rb @@ -1,35 +1,35 @@ # frozen_string_literal: true -require_relative '../base_object' +module Shared + class HtmlEditor < BaseObject + def content_element + raise NotImplementedError + end -class HtmlEditor < BaseObject - def content_element - raise NotImplementedError - end + def clear + select_all + content_element.send_keys(:delete) + self + end - def clear - select_all - content_element.send_keys(:delete) - self - end + # @return [self] + def open_dropdown + raise NotImplementedError + end - # @return [self] - def open_dropdown - raise NotImplementedError - end + def select_all + content_element.send_keys([:control, "a"]) + self + end - def select_all - content_element.send_keys([:control, "a"]) - self - end + def toolbar_control(control, ...) + send(:"toggle_#{control}", ...) + self + end - def toolbar_control(control, ...) - send(:"toggle_#{control}", ...) - self - end - - def <<(content) - content_element.send_keys(content) - self + def <<(content) + content_element.send_keys(content) + self + end end end diff --git a/spec/page_objects/shared/quill_editor.rb b/spec/page_objects/shared/quill_editor.rb index cc59586..54bfe72 100644 --- a/spec/page_objects/shared/quill_editor.rb +++ b/spec/page_objects/shared/quill_editor.rb @@ -1,44 +1,46 @@ # frozen_string_literal: true -class QuillEditor < HtmlEditor - SELECTOR = '.ql-container' - TOOLBAR_SELECTOR = '.ql-toolbar' +module Shared + class QuillEditor < HtmlEditor + SELECTOR = '.ql-container' + TOOLBAR_SELECTOR = '.ql-toolbar' - attr_reader :toolbar, :toolbar_selector + attr_reader :toolbar, :toolbar_selector - def initialize(selector: SELECTOR, toolbar_selector: TOOLBAR_SELECTOR) - super(selector: selector) - @toolbar = find(toolbar_selector) - @toolbar_selector = toolbar_selector - end + def initialize(selector: SELECTOR, toolbar_selector: TOOLBAR_SELECTOR) + super(selector: selector) + @toolbar = find(toolbar_selector) + @toolbar_selector = toolbar_selector + end - def content_element - @content_element ||= find("#{selector} .ql-editor") - end + def content_element + @content_element ||= find("#{selector} .ql-editor") + end - def control_selector(control) - case control&.to_sym - when :bold then "#{toolbar_selector} button.ql-bold" - when :italic then "#{toolbar_selector} button.ql-italic" - when :underline then "#{toolbar_selector} button.ql-underline" - when :link then "#{toolbar_selector} button.ql-link" - else raise "Invalid control #{control}" + def control_selector(control) + case control&.to_sym + when :bold then "#{toolbar_selector} button.ql-bold" + when :italic then "#{toolbar_selector} button.ql-italic" + when :underline then "#{toolbar_selector} button.ql-underline" + when :link then "#{toolbar_selector} button.ql-link" + else raise "Invalid control #{control}" + end + end + + def toggle_bold + find(control_selector(:bold)).click + end + + def toggle_italic + find(control_selector(:italic)).click + end + + def toggle_underline + find(control_selector(:underline)).click + end + + def toggle_link + find(control_selector(:link)).click end end - - def toggle_bold - find(control_selector(:bold)).click - end - - def toggle_italic - find(control_selector(:italic)).click - end - - def toggle_underline - find(control_selector(:underline)).click - end - - def toggle_link - find(control_selector(:link)).click - end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 4679cc8..72a7e92 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -2,6 +2,11 @@ require_relative 'spec_helper' +require 'zeitwerk' +loader = Zeitwerk::Loader.new +loader.push_dir("#{__dir__}/page_objects") +loader.setup + ENV['RAILS_ENV'] = 'test' require 'simplecov' @@ -13,13 +18,8 @@ abort('The Rails environment is running in production mode!') if Rails.env.produ require 'rspec/rails' require 'capybara/rails' -require 'rspec/retry' -Rails.root.glob("../support/**/*.rb").each { |f| require_relative f } -Rails.root.glob("../page_objects/**/*.rb").each { |f| require_relative f } - -# Force deprecations to raise an exception. -# ActiveSupport::Deprecation.behavior = :raise +Dir[File.expand_path('support/**/*.rb', __dir__)].each { |f| require_relative f } # Checks for pending migrations and applies them before tests are run. # If you are not using ActiveRecord, you can remove these lines. @@ -31,6 +31,12 @@ rescue ActiveRecord::PendingMigrationError => e end RSpec.configure do |config| + if Gem::Version.new(Rails.version) >= Gem::Version.new('7.1') + config.fixture_paths = [Rails.root.join('spec/fixtures')] + else + config.fixture_path = Rails.root.join('spec/fixtures') + end + config.infer_spec_type_from_file_location! config.filter_rails_from_backtrace! @@ -38,8 +44,6 @@ RSpec.configure do |config| config.use_instantiated_fixtures = false config.render_views = false - config.default_retry_count = 2 - config.before(:suite) do intro = ('-' * 80) intro << "\n" diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb index 15060c4..121f474 100644 --- a/spec/support/capybara.rb +++ b/spec/support/capybara.rb @@ -9,10 +9,10 @@ Capybara.register_driver(:capybara_cuprite) do |app| Capybara::Cuprite::Driver.new( app, - window_size: [1600, 1280], + window_size: [1600, 1024], browser_options: browser_options, - process_timeout: 20, - timeout: 20, + process_timeout: 30, + timeout: 30, inspector: true, headless: !ENV['CUPRITE_HEADLESS'].in?(%w[n 0 no false]) ) diff --git a/spec/system/quill_editor_spec.rb b/spec/system/quill_editor_spec.rb index 5d34984..e853eb9 100644 --- a/spec/system/quill_editor_spec.rb +++ b/spec/system/quill_editor_spec.rb @@ -2,9 +2,9 @@ RSpec.describe 'Quill editor' do def lookup_editor(field:) - selector = ["##{field}_input.quill_editor", QuillEditor::SELECTOR].join(' ') - toolbar_selector = ["##{field}_input.quill_editor", QuillEditor::TOOLBAR_SELECTOR].join(' ') - QuillEditor.new(selector: selector, toolbar_selector: toolbar_selector) + selector = ["##{field}_input.quill_editor", Shared::QuillEditor::SELECTOR].join(' ') + toolbar_selector = ["##{field}_input.quill_editor", Shared::QuillEditor::TOOLBAR_SELECTOR].join(' ') + Shared::QuillEditor.new(selector: selector, toolbar_selector: toolbar_selector) end let(:author) { Author.create!(email: 'some_email@example.com', name: 'John Doe', age: 30) }