From 8c57e2807812a5cf3f11385d02d9bad2f1cfd148 Mon Sep 17 00:00:00 2001 From: Andrej Date: Tue, 10 Dec 2024 12:18:35 +0100 Subject: [PATCH] Add forms controllers [SCI-11354] --- app/controllers/form_fields_controller.rb | 77 ++++++++ app/controllers/forms_controller.rb | 131 +++++++++++++ app/models/form.rb | 18 ++ app/models/form_field.rb | 15 ++ app/models/team.rb | 1 + app/serializers/form_field_serializer.rb | 9 + app/serializers/lists/form_serializer.rb | 21 ++ app/services/lists/forms_service.rb | 16 ++ config/locales/en.yml | 9 + config/routes.rb | 18 ++ db/migrate/20241209074134_create_forms.rb | 41 ++++ db/schema.rb | 56 ++++++ .../form_fields_controller_spec.rb | 137 +++++++++++++ spec/controllers/forms_controller_spec.rb | 185 ++++++++++++++++++ spec/factories/form_fields.rb | 12 ++ spec/factories/forms.rb | 18 ++ spec/models/form_field_spec.rb | 52 +++++ spec/models/form_spec.rb | 57 ++++++ 18 files changed, 873 insertions(+) create mode 100644 app/controllers/form_fields_controller.rb create mode 100644 app/controllers/forms_controller.rb create mode 100644 app/models/form.rb create mode 100644 app/models/form_field.rb create mode 100644 app/serializers/form_field_serializer.rb create mode 100644 app/serializers/lists/form_serializer.rb create mode 100644 app/services/lists/forms_service.rb create mode 100644 db/migrate/20241209074134_create_forms.rb create mode 100644 spec/controllers/form_fields_controller_spec.rb create mode 100644 spec/controllers/forms_controller_spec.rb create mode 100644 spec/factories/form_fields.rb create mode 100644 spec/factories/forms.rb create mode 100644 spec/models/form_field_spec.rb create mode 100644 spec/models/form_spec.rb diff --git a/app/controllers/form_fields_controller.rb b/app/controllers/form_fields_controller.rb new file mode 100644 index 000000000..8adb1fa28 --- /dev/null +++ b/app/controllers/form_fields_controller.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +class FormFieldsController < ApplicationController + before_action :load_form + before_action :load_form_field, only: %i(update destroy) + + def create + ActiveRecord::Base.transaction do + @form_field = FormField.new( + form_field_params.merge( + { + form: @form, + created_by: current_user, + last_modified_by: current_user, + position: @form.form_fields.length + } + ) + ) + if @form_field.save + render json: @form_field, serializer: FormFieldSerializer, user: current_user + else + render json: { error: @form_field.errors.full_messages }, status: :unprocessable_entity + end + end + end + + def update + ActiveRecord::Base.transaction do + if @form_field.update(form_field_params.merge({ last_modified_by: current_user })) + render json: @form_field, serializer: FormFieldSerializer, user: current_user + else + render json: { error: @form_field.errors.full_messages }, status: :unprocessable_entity + end + end + end + + def destroy + ActiveRecord::Base.transaction do + if @form_field.discard + render json: {} + else + render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity + end + end + end + + def reorder + ActiveRecord::Base.transaction do + params.permit(form_field_positions: %i(position id))[:form_field_positions].each do |data| + form_field = @form.form_fields.find(data['id'].to_i) + form_field.insert_at(data['position'].to_i) + end + end + + render json: params[:form_field_positions], status: :ok + rescue ActiveRecord::RecordInvalid + render json: { errors: form_field.errors }, status: :conflict + end + + private + + def load_form + @form = Form.find_by(id: params[:form_id]) + + return render_404 unless @form + end + + def load_form_field + @form_field = @form.form_fields.find_by(id: params[:id]) + + return render_404 unless @form_field + end + + def form_field_params + params.require(:form_field).permit(:name, :description, { data: [%i(type)] }, :required, :allow_not_applicable, :uid) + end +end diff --git a/app/controllers/forms_controller.rb b/app/controllers/forms_controller.rb new file mode 100644 index 000000000..b1c8961bb --- /dev/null +++ b/app/controllers/forms_controller.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +class FormsController < ApplicationController + before_action :load_form, only: %i(show update publish unpublish) + + def index + respond_to do |format| + format.html + format.json do + forms = Lists::FormsService.new(current_user, current_team, params).call + render json: forms, + each_serializer: Lists::FormSerializer, + user: current_user + end + end + end + + def show + respond_to do |format| + format.json { render json: @form, serializer: Lists::FormSerializer, include: %i(form_fields), user: current_user } + format.html + end + end + + def create + ActiveRecord::Base.transaction do + @form = Form.new( + name: I18n.t('forms.default_name'), + team: current_team, + created_by: current_user, + last_modified_by: current_user + ) + + if @form.save + render json: @form, serializer: Lists::FormSerializer, user: current_user + else + render json: { error: @form.errors.full_messages }, status: :unprocessable_entity + end + end + end + + def update + ActiveRecord::Base.transaction do + if @form.update(form_params.merge({ last_modified_by: current_user })) + render json: @form, serializer: Lists::FormSerializer, user: current_user + else + render json: { error: @form.errors.full_messages }, status: :unprocessable_entity + end + end + end + + def publish + ActiveRecord::Base.transaction do + @form.update!( + published_by: current_user, + published_on: DateTime.now + ) + + render json: @form, serializer: Lists::FormSerializer, user: current_user + end + end + + def unpublish + ActiveRecord::Base.transaction do + @form.update!( + published_by: nil, + published_on: nil + ) + + render json: @form, serializer: Lists::FormSerializer, user: current_user + end + end + + def archive + forms = current_team.forms.active.where(id: params[:form_ids]) + return render_404 if forms.blank? + + counter = 0 + + forms.each do |form| + form.transaction do + form.archive!(current_user) + counter += 1 + rescue StandardError => e + Rails.logger.error e.message + raise ActiveRecord::Rollback + end + end + + if counter.positive? + render json: { message: t('forms.archived.success_flash', number: counter) } + else + render json: { message: t('forms.archived.error_flash') }, status: :unprocessable_entity + end + end + + def restore + forms = current_team.forms.archived.where(id: params[:form_ids]) + return render_404 if forms.blank? + + counter = 0 + + forms.each do |form| + form.transaction do + form.restore!(current_user) + counter += 1 + rescue StandardError => e + Rails.logger.error e.message + raise ActiveRecord::Rollback + end + end + + if counter.positive? + render json: { message: t('forms.restored.success_flash', number: counter) } + else + render json: { message: t('forms.restored.error_flash') }, status: :unprocessable_entity + end + end + + private + + def load_form + @form = Form.find_by(id: params[:id]) + + return render_404 unless @form + end + + def form_params + params.require(:form).permit(:name, :description) + end +end diff --git a/app/models/form.rb b/app/models/form.rb new file mode 100644 index 000000000..ef0597540 --- /dev/null +++ b/app/models/form.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class Form < ApplicationRecord + include ArchivableModel + + belongs_to :team + belongs_to :parent, class_name: 'Form', optional: true + belongs_to :created_by, class_name: 'User' + belongs_to :last_modified_by, class_name: 'User' + belongs_to :published_by, class_name: 'User', optional: true + belongs_to :archived_by, class_name: 'User', optional: true + belongs_to :restored_by, class_name: 'User', optional: true + + has_many :form_fields, inverse_of: :form, dependent: :destroy + + validates :name, length: { minimum: Constants::NAME_MIN_LENGTH, maximum: Constants::NAME_MAX_LENGTH } + validates :description, length: { maximum: Constants::NAME_MAX_LENGTH } +end diff --git a/app/models/form_field.rb b/app/models/form_field.rb new file mode 100644 index 000000000..f4f432a45 --- /dev/null +++ b/app/models/form_field.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class FormField < ApplicationRecord + include Discard::Model + + belongs_to :form + belongs_to :created_by, class_name: 'User' + belongs_to :last_modified_by, class_name: 'User' + + validates :name, length: { minimum: Constants::NAME_MIN_LENGTH, maximum: Constants::NAME_MAX_LENGTH } + validates :description, length: { maximum: Constants::NAME_MAX_LENGTH } + validates :position, presence: true, uniqueness: { scope: :form } + + acts_as_list scope: :form, top_of_list: 0, sequential_updates: true +end diff --git a/app/models/team.rb b/app/models/team.rb index ac4da78fc..bced2c347 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -73,6 +73,7 @@ class Team < ApplicationRecord dependent: :destroy has_many :shareable_links, inverse_of: :team, dependent: :destroy has_many :storage_locations, dependent: :destroy + has_many :forms, dependent: :destroy attr_accessor :without_templates diff --git a/app/serializers/form_field_serializer.rb b/app/serializers/form_field_serializer.rb new file mode 100644 index 000000000..30b8d1f8d --- /dev/null +++ b/app/serializers/form_field_serializer.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class FormFieldSerializer < ActiveModel::Serializer + attributes :id, :name, :description, :updated_at, :type, :required, :allow_not_applicable, :uid, :position + + def type + object.data[:type] + end +end diff --git a/app/serializers/lists/form_serializer.rb b/app/serializers/lists/form_serializer.rb new file mode 100644 index 000000000..a44bce9bd --- /dev/null +++ b/app/serializers/lists/form_serializer.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Lists + class FormSerializer < ActiveModel::Serializer + attributes :id, :name, :description, :published_on, :published_by, :updated_at + + has_many :form_fields, key: :form_fields, serializer: FormFieldSerializer + + def published_by + object.published_by&.full_name + end + + def published_on + I18n.l(object.published_on, format: :full) if object.published_on + end + + def updated_at + I18n.l(object.updated_at, format: :full) if object.updated_at + end + end +end diff --git a/app/services/lists/forms_service.rb b/app/services/lists/forms_service.rb new file mode 100644 index 000000000..b2e046896 --- /dev/null +++ b/app/services/lists/forms_service.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Lists + class FormsService < BaseService + def initialize(user, team, params) + super(nil, params, user: user) + @team = team + end + + def fetch_records + @records = Form.where(team: @team) + end + + def filter_records; end + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 846508089..d900109db 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1054,6 +1054,15 @@ en: add_user_generic_error: "An error occurred. " can_add_user_to_project: "Can not add user to the project." + forms: + default_name: "Untitled form" + restored: + success_flash: "%{number} form(s) successfully restored." + error_flash: "Failed to restore form(s)." + archived: + success_flash: "%{number} form(s) successfully archived." + error_flash: "Failed to archive form(s)." + label_templates: types: fluics_label_template: 'Fluics' diff --git a/config/routes.rb b/config/routes.rb index 16b96e32f..2bca68964 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -850,6 +850,24 @@ Rails.application.routes.draw do end end + resources :forms, only: %i(index show create update) do + member do + post :publish + post :unpublish + end + + collection do + post :archive + post :restore + end + + resources :form_fields, only: %i(create update destroy) do + collection do + post :reorder + end + end + end + get 'search' => 'search#index' get 'search/new' => 'search#new', as: :new_search resource :search, only: [], controller: :search do diff --git a/db/migrate/20241209074134_create_forms.rb b/db/migrate/20241209074134_create_forms.rb new file mode 100644 index 000000000..3f38ef60f --- /dev/null +++ b/db/migrate/20241209074134_create_forms.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +class CreateForms < ActiveRecord::Migration[7.0] + def change + create_table :forms do |t| + t.string :name + t.string :description + t.references :team, index: true, foreign_key: { to_table: :teams } + t.references :created_by, index: true, foreign_key: { to_table: :users } + t.references :last_modified_by, index: true, foreign_key: { to_table: :users } + t.references :parent, index: true, foreign_key: { to_table: :forms } + t.references :published_by, index: true, foreign_key: { to_table: :users } + t.datetime :published_on + t.boolean :archived, default: false, null: false + t.datetime :archived_on + t.datetime :restored_on + t.references :archived_by, index: true, foreign_key: { to_table: :users } + t.references :restored_by, index: true, foreign_key: { to_table: :users } + + t.timestamps + end + + create_table :form_fields do |t| + t.references :form, index: true, foreign_key: { to_table: :forms } + t.references :created_by, index: true, foreign_key: { to_table: :users } + t.references :last_modified_by, index: true, foreign_key: { to_table: :users } + t.string :name + t.string :description + t.integer :position, null: false + t.jsonb :data, null: false, default: {} + t.boolean :required, default: false, null: false + t.boolean :allow_not_applicable, default: false, null: false + t.string :uid + t.datetime :discarded_at, index: true + + t.index %i(form_id position), unique: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 3b8768536..12450596e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -224,6 +224,52 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_28_105317) do t.index ["restored_by_id"], name: "index_experiments_on_restored_by_id" end + create_table "form_fields", force: :cascade do |t| + t.bigint "form_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" + t.string "name" + t.string "description" + t.integer "position", null: false + t.jsonb "data", default: {}, null: false + t.boolean "required", default: false, null: false + t.boolean "allow_not_applicable", default: false, null: false + t.string "uid" + t.datetime "discarded_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["created_by_id"], name: "index_form_fields_on_created_by_id" + t.index ["discarded_at"], name: "index_form_fields_on_discarded_at" + t.index ["form_id", "position"], name: "index_form_fields_on_form_id_and_position", unique: true + t.index ["form_id"], name: "index_form_fields_on_form_id" + t.index ["last_modified_by_id"], name: "index_form_fields_on_last_modified_by_id" + end + + create_table "forms", force: :cascade do |t| + t.string "name" + t.string "description" + t.bigint "team_id" + t.bigint "created_by_id" + t.bigint "last_modified_by_id" + t.bigint "parent_id" + t.bigint "published_by_id" + t.datetime "published_on" + t.boolean "archived", default: false, null: false + t.datetime "archived_on" + t.datetime "restored_on" + t.bigint "archived_by_id" + t.bigint "restored_by_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["archived_by_id"], name: "index_forms_on_archived_by_id" + t.index ["created_by_id"], name: "index_forms_on_created_by_id" + t.index ["last_modified_by_id"], name: "index_forms_on_last_modified_by_id" + t.index ["parent_id"], name: "index_forms_on_parent_id" + t.index ["published_by_id"], name: "index_forms_on_published_by_id" + t.index ["restored_by_id"], name: "index_forms_on_restored_by_id" + t.index ["team_id"], name: "index_forms_on_team_id" + end + create_table "hidden_repository_cell_reminders", force: :cascade do |t| t.bigint "repository_cell_id", null: false t.bigint "user_id", null: false @@ -1409,6 +1455,16 @@ ActiveRecord::Schema[7.0].define(version: 2024_10_28_105317) do add_foreign_key "experiments", "users", column: "created_by_id" add_foreign_key "experiments", "users", column: "last_modified_by_id" add_foreign_key "experiments", "users", column: "restored_by_id" + add_foreign_key "form_fields", "forms" + add_foreign_key "form_fields", "users", column: "created_by_id" + add_foreign_key "form_fields", "users", column: "last_modified_by_id" + add_foreign_key "forms", "forms", column: "parent_id" + add_foreign_key "forms", "teams" + add_foreign_key "forms", "users", column: "archived_by_id" + add_foreign_key "forms", "users", column: "created_by_id" + add_foreign_key "forms", "users", column: "last_modified_by_id" + add_foreign_key "forms", "users", column: "published_by_id" + add_foreign_key "forms", "users", column: "restored_by_id" add_foreign_key "hidden_repository_cell_reminders", "repository_cells" add_foreign_key "hidden_repository_cell_reminders", "users" add_foreign_key "label_templates", "teams" diff --git a/spec/controllers/form_fields_controller_spec.rb b/spec/controllers/form_fields_controller_spec.rb new file mode 100644 index 000000000..e0cb0b99c --- /dev/null +++ b/spec/controllers/form_fields_controller_spec.rb @@ -0,0 +1,137 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe FormFieldsController, type: :controller do + login_user + + include_context 'reference_project_structure' + + let!(:form) { create :form, team: team } + let!(:form_field) { create :form_field, form: form } + let!(:form_fields) { create_list :form_field, 5, form: form } + + describe 'POST create' do + let(:action) { post :create, params: params, format: :json } + let(:params) do + { + form_id: form.id, + form_field: { + name: Faker::Name.unique.name, + description: Faker::Lorem.sentence, + data: { type: 'text' }, + required: true, + allow_not_applicable: true, + uid: Faker::Name.unique.name + } + } + end + + it 'returns success response' do + action + expect(response).to have_http_status(:success) + expect(response.media_type).to eq 'application/json' + response_body = JSON.parse(response.body) + expect(response_body['data']['attributes']['name']).to eq params[:form_field][:name] + expect(response_body['data']['attributes']['description']).to eq params[:form_field][:description] + expect(response_body['data']['attributes']['required']).to eq params[:form_field][:required] + expect(response_body['data']['attributes']['allow_not_applicable']).to eq params[:form_field][:allow_not_applicable] + expect(response_body['data']['attributes']['uid']).to eq params[:form_field][:uid] + expect(response_body['data']['attributes']['position']).to eq(form_fields.length + 1) + end + + it 'invalid params' do + params[:form_field][:name] = '' + action + expect(response).to have_http_status(:unprocessable_entity) + end + end + + describe 'PUT create' do + let(:action) { put :update, params: params, format: :json } + let(:params) do + { + form_id: form.id, + id: form_field.id, + form_field: { + name: Faker::Name.unique.name, + description: Faker::Lorem.sentence, + data: { type: 'text' }, + required: true, + allow_not_applicable: true, + uid: Faker::Name.unique.name + } + } + end + + it 'returns success response' do + action + expect(response).to have_http_status(:success) + expect(response.media_type).to eq 'application/json' + response_body = JSON.parse(response.body) + expect(response_body['data']['attributes']['name']).to eq params[:form_field][:name] + expect(response_body['data']['attributes']['description']).to eq params[:form_field][:description] + expect(response_body['data']['attributes']['required']).to eq params[:form_field][:required] + expect(response_body['data']['attributes']['allow_not_applicable']).to eq params[:form_field][:allow_not_applicable] + expect(response_body['data']['attributes']['uid']).to eq params[:form_field][:uid] + expect(response_body['data']['attributes']['position']).to eq(form_field.position) + end + + it 'invalid params' do + params[:form_field][:name] = '' + action + expect(response).to have_http_status(:unprocessable_entity) + end + end + + describe 'DELETE destroy' do + + let(:action) { delete :destroy, params: params } + let(:params) do + { + form_id: form.id, + id: form_field.id, + } + end + + it 'returns success response' do + action + expect(response).to have_http_status(:success) + end + + it 'invalid id' do + params[:id] = -1 + action + expect(response).to have_http_status(:not_found) + end + end + + describe 'POST reorder' do + + let(:action) { delete :reorder, params: params } + let(:params) do + { + form_id: form.id, + form_field_positions: form_positions + } + end + + let(:form_positions) do + positions = form.form_fields.pluck(:position).reverse + form.form_fields.pluck(:id).each_with_index.map do |id, i| + { id: id, position: positions[i] } + end + end + + it 'returns success response' do + action + expect(response).to have_http_status(:success) + + new_form_fields_order = form.form_fields.order(position: :asc).pluck(:id, :position) + + expect(new_form_fields_order).to( + eq(form_positions.map(&:values).sort { |a, b| a[1] <=> b[1] }) + ) + end + end +end diff --git a/spec/controllers/forms_controller_spec.rb b/spec/controllers/forms_controller_spec.rb new file mode 100644 index 000000000..1d65a084b --- /dev/null +++ b/spec/controllers/forms_controller_spec.rb @@ -0,0 +1,185 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe FormsController, type: :controller do + login_user + + include_context 'reference_project_structure' + + let!(:form) { create :form, team: team } + let!(:form2) { create :form, team: team } + let!(:form_field) { create :form_field, form: form2} + + describe '#index' do + let(:params) { { team: team.id } } + + it 'returns success response' do + get :index, params: params, format: :json + expect(response).to have_http_status(:success) + + response_body = JSON.parse(response.body) + + expect(response_body).to match_array( + JSON.parse( + ActiveModelSerializers::SerializableResource + .new(team.forms, + each_serializer: Lists::FormSerializer) + .to_json + ) + ) + expect(response_body['data'].length).to eq 2 + expect(response.body).to include(form.name) + expect(response.body).to include(form2.name) + expect(response.body).not_to include(form_field.name) + end + end + + describe '#show' do + it 'unsuccessful response with non existing id' do + get :show, format: :json, params: { id: -1 } + expect(response).to have_http_status(:not_found) + end + + it 'successful response' do + get :show, format: :json, params: { id: form2.id } + expect(response).to have_http_status(:success) + + response_body = JSON.parse(response.body) + expect(response_body).to match_array( + JSON.parse( + ActiveModelSerializers::SerializableResource + .new(form2, + serializer: Lists::FormSerializer, + include: :form_fields) + .to_json + ) + ) + + expect(response_body['included'].first['attributes']['name']).to eq form_field.name + end + end + + describe 'POST create' do + let(:action) { post :create, format: :json } + + it 'returns success response' do + action + expect(response).to have_http_status(:success) + expect(response.media_type).to eq 'application/json' + response_body = JSON.parse(response.body) + expect(response_body['data']['attributes']['name']).to eq I18n.t('forms.default_name') + end + end + + describe 'PUT update' do + let(:action) { put :update, params: params, format: :json } + let(:params) do + { + id: form.id, + form: { + name: 'Renamed form', + description: 'test description' + } + } + end + + it 'returns success response' do + action + expect(response).to have_http_status(:success) + expect(response.media_type).to eq 'application/json' + response_body = JSON.parse(response.body) + expect(response_body['data']['attributes']['name']).to eq params[:form][:name] + expect(response_body['data']['attributes']['description']).to eq params[:form][:description] + end + + it 'incorrect id' do + get :update, format: :json, params: { id: -1 } + expect(response).to have_http_status(:not_found) + end + + end + + describe 'POST publish' do + let(:action) { put :publish, params: params, format: :json } + let(:params) do + { + id: form.id, + } + end + + it 'returns success response' do + action + expect(response).to have_http_status(:success) + expect(response.media_type).to eq 'application/json' + response_body = JSON.parse(response.body) + + expect(response_body['data']['attributes']['published_by']).to eq user.full_name + expect(response_body['data']['attributes']['published_on']).not_to eq nil + end + + it 'incorrect id' do + get :publish, format: :json, params: { id: -1 } + expect(response).to have_http_status(:not_found) + end + end + + describe 'POST unpublish' do + let(:action) { put :unpublish, params: params, format: :json } + let(:params) do + { + id: form.id, + } + end + + it 'returns success response' do + action + expect(response).to have_http_status(:success) + expect(response.media_type).to eq 'application/json' + response_body = JSON.parse(response.body) + + expect(response_body['data']['attributes']['published_by']).to eq nil + expect(response_body['data']['attributes']['published_on']).to eq nil + end + + it 'incorrect id' do + get :unpublish, format: :json, params: { id: -1 } + expect(response).to have_http_status(:not_found) + end + end + + describe 'POST archive' do + let(:action) { put :archive, params: params, format: :json } + let(:params) do + { + form_ids: [form.id] + } + end + + it 'returns success response' do + expect(form.archived?).to eq false + action + expect(response).to have_http_status(:success) + form.reload + expect(form.archived?).to eq true + end + end + + describe 'POST restore' do + let!(:form_archived) { create :form, :archived, team: team } + let(:action) { put :restore, params: params, format: :json } + let(:params) do + { + form_ids: [form_archived.id] + } + end + + it 'returns success response' do + expect(form_archived.archived?).to eq true + action + expect(response).to have_http_status(:success) + form_archived.reload + expect(form_archived.archived?).to eq false + end + end +end diff --git a/spec/factories/form_fields.rb b/spec/factories/form_fields.rb new file mode 100644 index 000000000..280261e24 --- /dev/null +++ b/spec/factories/form_fields.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :form_field do + name { Faker::Name.unique.name } + description { Faker::Lorem.sentence } + sequence(:position) { |n| n - 1 } + association :created_by, factory: :user + association :last_modified_by, factory: :user + association :form + end +end diff --git a/spec/factories/forms.rb b/spec/factories/forms.rb new file mode 100644 index 000000000..218443305 --- /dev/null +++ b/spec/factories/forms.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :form do + name { Faker::Name.unique.name } + description { Faker::Lorem.sentence } + association :created_by, factory: :user + association :last_modified_by, factory: :user + team { create :team, created_by: created_by } + archived { false } + + trait :archived do + archived { true } + archived_on { Time.zone.now } + archived_by { created_by } + end + end +end diff --git a/spec/models/form_field_spec.rb b/spec/models/form_field_spec.rb new file mode 100644 index 000000000..d2734cbc6 --- /dev/null +++ b/spec/models/form_field_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe FormField, type: :model do + let(:form_field) { build :form_field } + + it 'is valid' do + expect(form_field).to be_valid + end + + it 'should be of class form field' do + expect(subject.class).to eq FormField + end + + describe 'Database table' do + it { should have_db_column :id } + it { should have_db_column :name } + it { should have_db_column :description } + it { should have_db_column :form_id } + it { should have_db_column :created_by_id } + it { should have_db_column :last_modified_by_id } + it { should have_db_column :position } + it { should have_db_column :data } + it { should have_db_column :required } + it { should have_db_column :allow_not_applicable } + it { should have_db_column :uid } + it { should have_db_column :created_at } + it { should have_db_column :updated_at } + it { should have_db_column :discarded_at } + end + + describe 'Relations' do + it { should belong_to(:form) } + it { should belong_to(:created_by).class_name('User') } + it { should belong_to(:last_modified_by).class_name('User') } + end + + describe 'Validations' do + it { should validate_presence_of :position } + describe '#name' do + it do + is_expected.to(validate_length_of(:name).is_at_least(Constants::NAME_MIN_LENGTH).is_at_most(Constants::NAME_MAX_LENGTH)) + end + end + describe '#description' do + it do + is_expected.to(validate_length_of(:description).is_at_most(Constants::NAME_MAX_LENGTH)) + end + end + end +end diff --git a/spec/models/form_spec.rb b/spec/models/form_spec.rb new file mode 100644 index 000000000..130aa187a --- /dev/null +++ b/spec/models/form_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe Form, type: :model do + let(:form) { build :form } + + it 'is valid' do + expect(form).to be_valid + end + + it 'should be of class Form' do + expect(subject.class).to eq Form + end + + describe 'Database table' do + it { should have_db_column :id } + it { should have_db_column :name } + it { should have_db_column :description } + it { should have_db_column :team_id } + it { should have_db_column :created_by_id } + it { should have_db_column :last_modified_by_id } + it { should have_db_column :parent_id } + it { should have_db_column :published_by_id } + it { should have_db_column :published_on } + it { should have_db_column :archived } + it { should have_db_column :archived_on } + it { should have_db_column :archived_by_id } + it { should have_db_column :restored_by_id } + it { should have_db_column :restored_on } + it { should have_db_column :created_at } + it { should have_db_column :updated_at } + end + + describe 'Relations' do + it { should belong_to(:team) } + it { should belong_to(:created_by).class_name('User') } + it { should belong_to(:last_modified_by).class_name('User') } + it { should belong_to(:parent).class_name('Form').optional } + it { should belong_to(:archived_by).class_name('User').optional } + it { should belong_to(:restored_by).class_name('User').optional } + it { should have_many :form_fields } + end + + describe 'Validations' do + describe '#name' do + it do + is_expected.to(validate_length_of(:name).is_at_least(Constants::NAME_MIN_LENGTH).is_at_most(Constants::NAME_MAX_LENGTH)) + end + end + describe '#description' do + it do + is_expected.to(validate_length_of(:description).is_at_most(Constants::NAME_MAX_LENGTH)) + end + end + end +end