- <%= render 'shared/search_input' %>
- <%= link_to new_template_submissions_export_path(@template), class: 'order-3 md:order-1 btn btn-ghost text-base', data: { turbo_frame: 'modal' } do %>
- <%= svg_icon('download', class: 'w-6 h-6 stroke-2') %>
-
<%= render partial: 'templates/submission', collection: @submissions, locals: { template: @template, archived: true } %>
-<% elsif params[:q].present? %>
+<% elsif params[:q].present? || filter_params.present? %>
<%= t('submissions_not_found') %>
diff --git a/config/initializers/active_storage.rb b/config/initializers/active_storage.rb
index 81c7240c8..451ef815c 100644
--- a/config/initializers/active_storage.rb
+++ b/config/initializers/active_storage.rb
@@ -13,10 +13,11 @@ def signed_uuid
ActiveSupport.on_load(:active_storage_blob) do
attribute :uuid, :string, default: -> { SecureRandom.uuid }
- def self.proxy_url(blob, expires_at: nil)
+ def self.proxy_url(blob, expires_at: nil, filename: nil, host: nil)
Rails.application.routes.url_helpers.blobs_proxy_url(
- signed_uuid: blob.signed_uuid(expires_at:), filename: blob.filename,
- **Docuseal.default_url_options
+ signed_uuid: blob.signed_uuid(expires_at:), filename: filename || blob.filename,
+ **Docuseal.default_url_options,
+ **{ host: }.compact
)
end
diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml
index 12b896aaf..588699256 100644
--- a/config/locales/i18n.yml
+++ b/config/locales/i18n.yml
@@ -22,6 +22,9 @@ en: &en
thanks: Thanks
unarchive: Unarchive
first_party: 'First Party'
+ remove_filter: Remove filter
+ document_download_filename_format: Document download filename format
+ document_name: Document Name
docuseal_trusted_signature: DocuSeal Trusted Signature
hello_name: Hello %{name}
you_are_invited_to_product_name: You are invited to %{product_name}
@@ -344,7 +347,7 @@ en: &en
edit_message: Edit message
smtp_not_configured: SMTP not Configured
configure_smtp_settings_in_order_to_send_emails_: 'Configure SMTP settings in order to send emails:'
- go_to_smtp_setting: Go to SMTP setting
+ go_to_smtp_settings: Go to SMTP settings
save_as_default_template_message: Save as default template message
re_send_sms: Re-send SMS
send_sms: Send SMS
@@ -619,7 +622,7 @@ en: &en
verified: Verified
unverified: Unverified
document: Document
- completed_at: Completed At
+ completed_at: Completed at
edit_recipient: Edit Recipient
update_recipient: Update Recipient
use_international_format_1xxx_: 'Use internatioanl format: +1xxx...'
@@ -631,6 +634,18 @@ en: &en
key: Key
value: Value
webhook_secret: Webhook Secret
+ author: Author
+ to: To
+ created_at: Created at
+ apply: Apply
+ applying: Applying
+ today: Today
+ yesterday: Yesterday
+ this_week: This week
+ last_week: Last week
+ this_month: This month
+ last_month: Last month
+ this_year: This year
submission_event_names:
send_email_to_html: 'Email sent to %{submitter_name}'
send_reminder_email_to_html: 'Reminder email sent to %{submitter_name}'
@@ -667,6 +682,9 @@ en: &en
read: Read your data
es: &es
+ remove_filter: Eliminar filtro
+ document_download_filename_format: Formato del nombre del archivo de descarga del documento
+ document_name: Nombre del documento
unarchive: Desarchivar
awaiting_completion_by_the_other_party: "Esperando la finalización por la otra parte"
enforce_recipients_order: 'Hacer cumplir el orden de los destinatarios'
@@ -993,7 +1011,7 @@ es: &es
edit_message: Editar mensaje
smtp_not_configured: SMTP no configurado
configure_smtp_settings_in_order_to_send_emails_: 'Configura los ajustes de SMTP para enviar correos electrónicos:'
- go_to_smtp_setting: Ir a la configuración de SMTP
+ go_to_smtp_settings: Ir a la configuración de SMTP
save_as_default_template_message: Guardar como mensaje de plantilla predeterminado
re_send_sms: Reenviar SMS
send_sms: Enviar SMS
@@ -1280,6 +1298,18 @@ es: &es
key: Clave
value: Valor
webhook_secret: Secreto del Webhook
+ author: Autor
+ to: A
+ created_at: Creado el
+ apply: Aplicar
+ applying: Aplicando
+ today: Hoy
+ yesterday: Ayer
+ this_week: Esta Semana
+ last_week: La Semana Pasada
+ this_month: Este Mes
+ last_month: El Mes Pasado
+ this_year: Este Año
submission_event_names:
send_email_to_html: 'Correo electrónico enviado a %{submitter_name}'
send_reminder_email_to_html: 'Correo de recordatorio enviado a %{submitter_name}'
@@ -1316,6 +1346,9 @@ es: &es
read: Leer tus datos
it: &it
+ remove_filter: Rimuovi filtro
+ document_download_filename_format: Formato del nome file scaricato
+ document_name: Nome del Documento
unarchive: Ripristina
awaiting_completion_by_the_other_party: "In attesa di completamento da parte dell'altra parte"
enforce_recipients_order: Aplicar el orden de los destinatarios
@@ -1642,7 +1675,7 @@ it: &it
edit_message: Modifica messaggio
smtp_not_configured: SMTP non configurato
configure_smtp_settings_in_order_to_send_emails_: 'Configura le impostazioni SMTP per inviare email:'
- go_to_smtp_setting: Vai alle impostazioni SMTP
+ go_to_smtp_settings: Vai alle impostazioni SMTP
save_as_default_template_message: Salva come messaggio modello predefinito
re_send_sms: Reinvio SMS
send_sms: Invia SMS
@@ -1929,6 +1962,18 @@ it: &it
key: Chiave
value: Valore
webhook_secret: Segreto del Webhook
+ author: Autore
+ to: A
+ created_at: Creato il
+ apply: Applica
+ applying: Applicazione
+ today: Oggi
+ yesterday: Ieri
+ this_week: Questa Settimana
+ last_week: Settimana Scorsa
+ this_month: Questo Mese
+ last_month: Mese Scorso
+ this_year: "Quest'Anno"
submission_event_names:
send_email_to_html: 'E-mail inviato a %{submitter_name}'
send_reminder_email_to_html: 'E-mail di promemoria inviato a %{submitter_name}'
@@ -1965,6 +2010,9 @@ it: &it
read: Leggi i tuoi dati
fr: &fr
+ remove_filter: Supprimer le filtre
+ document_download_filename_format: Format du nom de fichier du téléchargement de document
+ document_name: Nom du document
unarchive: Désarchiver
awaiting_completion_by_the_other_party: "En attente de la complétion par l'autre partie"
enforce_recipients_order: "Respecter l'ordre des destinataires"
@@ -2292,7 +2340,7 @@ fr: &fr
edit_message: Modifier le message
smtp_not_configured: SMTP non configuré
configure_smtp_settings_in_order_to_send_emails_: 'Configurez les paramètres SMTP pour envoyer des e-mails:'
- go_to_smtp_setting: Aller aux paramètres SMTP
+ go_to_smtp_settings: Aller aux paramètres SMTP
save_as_default_template_message: Enregistrer comme modèle de message par défaut
re_send_sms: Renvoyer le SMS
send_sms: Envoyer SMS
@@ -2579,6 +2627,18 @@ fr: &fr
key: Clé
value: Valeur
webhook_secret: Secret du Webhook
+ author: Auteur
+ to: À
+ created_at: Créé le
+ apply: Appliquer
+ applying: Application en cours
+ today: "Aujourd'hui"
+ yesterday: Hier
+ this_week: Cette Semaine
+ last_week: La Semaine Dernière
+ this_month: Ce Mois-ci
+ last_month: Le Mois Dernier
+ this_year: Cette Année
submission_event_names:
send_email_to_html: 'E-mail envoyé à %{submitter_name}'
send_reminder_email_to_html: 'E-mail de rappel envoyé à %{submitter_name}'
@@ -2615,6 +2675,9 @@ fr: &fr
read: Lire vos données
pt: &pt
+ remove_filter: Remover filtro
+ document_download_filename_format: Formato do nome do arquivo de download do documento
+ document_name: Nome do documento
unarchive: Desarquivar
awaiting_completion_by_the_other_party: "Aguardando a conclusão pela outra parte"
enforce_recipients_order: 'Forçar a ordem dos recipientes'
@@ -2941,7 +3004,7 @@ pt: &pt
edit_message: Editar mensagem
smtp_not_configured: SMTP não configurado
configure_smtp_settings_in_order_to_send_emails_: 'Configure as configurações de SMTP para enviar e-mails:'
- go_to_smtp_setting: Ir para a configuração de SMTP
+ go_to_smtp_settings: Ir para a configuração de SMTP
save_as_default_template_message: Salvar como mensagem de modelo padrão
re_send_sms: Reenviar SMS
send_sms: Enviar SMS
@@ -3228,6 +3291,18 @@ pt: &pt
key: Chave
value: Valor
webhook_secret: Segredo do Webhook
+ author: Autor
+ to: Para
+ created_at: Criado em
+ apply: Aplicar
+ applying: Aplicando
+ today: Hoje
+ yesterday: Ontem
+ this_week: Esta Semana
+ last_week: Semana Passada
+ this_month: Este Mês
+ last_month: Mês Passado
+ this_year: Este Ano
submission_event_names:
send_email_to_html: 'E-mail enviado para %{submitter_name}'
send_reminder_email_to_html: 'E-mail de lembrete enviado para %{submitter_name}'
@@ -3264,6 +3339,9 @@ pt: &pt
read: Ler seus dados
de: &de
+ remove_filter: Filter entfernen
+ document_download_filename_format: Format des Dateinamens beim Herunterladen von Dokumenten
+ document_name: Dokumentname
unarchive: Wiederherstellen
awaiting_completion_by_the_other_party: "Warten auf die Fertigstellung durch die andere Partei"
enforce_recipients_order: 'Empfängerreihenfolge durchsetzen'
@@ -3590,7 +3668,7 @@ de: &de
edit_message: Nachricht bearbeiten
smtp_not_configured: SMTP nicht konfiguriert
configure_smtp_settings_in_order_to_send_emails_: 'Konfiguriere die SMTP-Einstellungen, um E-Mails zu senden:'
- go_to_smtp_setting: Zu den SMTP-Einstellungen gehen
+ go_to_smtp_settings: Zu den SMTP-Einstellungen gehen
save_as_default_template_message: Als Standardvorlage speichern
re_send_sms: SMS erneut senden
send_sms: SMS senden
@@ -3877,6 +3955,18 @@ de: &de
key: Schlüssel
value: Wert
webhook_secret: Webhook-Geheimnis
+ author: Autor
+ to: An
+ created_at: Erstellt am
+ apply: Anwenden
+ applying: Anwenden
+ today: Heute
+ yesterday: Gestern
+ this_week: Diese Woche
+ last_week: Letzte Woche
+ this_month: Dieser Monat
+ last_month: Letzter Monat
+ this_year: Dieses Jahr
submission_event_names:
send_email_to_html: 'E-Mail gesendet an %{submitter_name}'
send_reminder_email_to_html: 'Erinnerungs-E-Mail gesendet an %{submitter_name}'
diff --git a/config/routes.rb b/config/routes.rb
index 140503ff7..dd5683164 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -90,6 +90,7 @@
resources :folders, only: %i[show edit update destroy], controller: 'template_folders'
resources :template_sharings_testing, only: %i[create]
resources :templates, only: %i[index], controller: 'templates_dashboard'
+ resources :submissions_filters, only: %i[show], param: 'name'
resources :templates, only: %i[new create edit update show destroy] do
resource :debug, only: %i[show], controller: 'templates_debug' if Rails.env.development?
resources :documents, only: %i[create], controller: 'template_documents'
diff --git a/lib/docuseal.rb b/lib/docuseal.rb
index 34a440468..e63ef58e4 100644
--- a/lib/docuseal.rb
+++ b/lib/docuseal.rb
@@ -3,6 +3,7 @@
module Docuseal
URL_CACHE = ActiveSupport::Cache::MemoryStore.new
PRODUCT_URL = 'https://www.docuseal.com'
+ PRODUCT_EMAIL_URL = ENV.fetch('PRODUCT_EMAIL_URL', PRODUCT_URL)
NEWSLETTER_URL = "#{PRODUCT_URL}/newsletters".freeze
ENQUIRIES_URL = "#{PRODUCT_URL}/enquiries".freeze
PRODUCT_NAME = 'DocuSeal'
diff --git a/lib/replace_email_variables.rb b/lib/replace_email_variables.rb
index f403b82c5..572fd0a46 100644
--- a/lib/replace_email_variables.rb
+++ b/lib/replace_email_variables.rb
@@ -19,6 +19,8 @@ module ReplaceEmailVariables
DOCUMENTS_LINKS = /\{+documents\.links\}+/i
DOCUMENTS_LINK = /\{+documents\.link\}+/i
+ EMAIL_HOST = ENV.fetch('EMAIL_HOST', nil)
+
module_function
# rubocop:disable Metrics
@@ -64,10 +66,17 @@ def replace(text, var, html_escape: false)
def build_submitter_link(submitter, tracking_event_type)
if tracking_event_type == 'click_email'
+ url_options =
+ if EMAIL_HOST.present?
+ { host: EMAIL_HOST, protocol: ENV['FORCE_SSL'].present? ? 'https' : 'http' }
+ else
+ Docuseal.default_url_options
+ end
+
Rails.application.routes.url_helpers.submit_form_url(
slug: submitter.slug,
t: SubmissionEvents.build_tracking_param(submitter, 'click_email'),
- **Docuseal.default_url_options
+ **url_options
)
else
Rails.application.routes.url_helpers.submit_form_url(
diff --git a/lib/submissions.rb b/lib/submissions.rb
index 1c35b0c2b..ab9d98876 100644
--- a/lib/submissions.rb
+++ b/lib/submissions.rb
@@ -26,7 +26,7 @@ def search(submissions, keyword, search_values: false, search_template: false)
arel = arel.or(Template.arel_table[:name].lower.matches("%#{keyword.downcase}%"))
end
- submissions.joins(:submitters).where(arel).distinct
+ submissions.joins(:submitters).where(arel).group(:id)
end
def update_template_fields!(submission)
@@ -88,17 +88,19 @@ def create_from_submitters(template:, user:, submissions_attrs:, source:,
)
end
- def send_signature_requests(submissions)
- submissions.each do |submission|
+ def send_signature_requests(submissions, delay: nil)
+ submissions.each_with_index do |submission, index|
+ delay_seconds = (delay + index).seconds if delay
+
submitters = submission.submitters.reject(&:completed_at?)
if submission.submitters_order_preserved?
first_submitter =
submission.template_submitters.filter_map { |s| submitters.find { |e| e.uuid == s['uuid'] } }.first
- Submitters.send_signature_requests([first_submitter]) if first_submitter
+ Submitters.send_signature_requests([first_submitter], delay_seconds:) if first_submitter
else
- Submitters.send_signature_requests(submitters)
+ Submitters.send_signature_requests(submitters, delay_seconds:)
end
end
end
diff --git a/lib/submissions/filter.rb b/lib/submissions/filter.rb
new file mode 100644
index 000000000..f4a2271a2
--- /dev/null
+++ b/lib/submissions/filter.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+
+module Submissions
+ module Filter
+ ALLOWED_PARAMS = %w[
+ author
+ completed_at_from
+ completed_at_to
+ created_at_from
+ created_at_to
+ ].freeze
+
+ DATE_PARAMS = %w[
+ completed_at_from
+ completed_at_to
+ created_at_from
+ created_at_to
+ ].freeze
+
+ module_function
+
+ def call(submissions, current_user, params)
+ filters = normalize_filter_params(params, current_user)
+
+ if filters[:author].present?
+ user = current_user.account.users.find_by(email: filters[:author])
+ submissions = submissions.where(created_by_user_id: user&.id || -1)
+ end
+
+ submissions = submissions.where(created_at: filters[:created_at_from]..) if filters[:created_at_from].present?
+
+ if filters[:created_at_to].present?
+ submissions = submissions.where(created_at: ..filters[:created_at_to].end_of_day)
+ end
+
+ if filters[:completed_at_from].present? || filters[:completed_at_to].present?
+ completed_arel = Submitter.arel_table[:completed_at].maximum
+ submissions = submissions.completed.joins(:submitters).group(:id)
+
+ if filters[:completed_at_from].present?
+ submissions = submissions.having(completed_arel.gteq(filters[:completed_at_from]))
+ end
+
+ if filters[:completed_at_to].present?
+ submissions = submissions.having(completed_arel.lteq(filters[:completed_at_to].end_of_day))
+ end
+ end
+
+ submissions
+ end
+
+ def normalize_filter_params(params, current_user)
+ tz = ActiveSupport::TimeZone[current_user.account.timezone] || Time.zone
+
+ ALLOWED_PARAMS.each_with_object({}) do |key, acc|
+ next if params[key].blank?
+
+ value = DATE_PARAMS.include?(key) ? tz.parse(params[key]) : params[key]
+
+ acc[key.to_sym] = value
+ end
+ end
+ end
+end
diff --git a/lib/submissions/generate_audit_trail.rb b/lib/submissions/generate_audit_trail.rb
index 1e3e2dd1f..ab932be8d 100644
--- a/lib/submissions/generate_audit_trail.rb
+++ b/lib/submissions/generate_audit_trail.rb
@@ -23,7 +23,7 @@ module GenerateAuditTrail
RTL_REGEXP = TextUtils::RTL_REGEXP
MAX_IMAGE_HEIGHT = 100
- US_TIMEZONES = %w[EST CST MST PST HST AKDT].freeze
+ US_TIMEZONES = TimeUtils::US_TIMEZONES
module_function
@@ -64,7 +64,10 @@ def call(submission)
def build_audit_trail(submission)
account = submission.account
- verify_url = Rails.application.routes.url_helpers.settings_esign_url(**Docuseal.default_url_options)
+ verify_url = Rails.application.routes.url_helpers.settings_esign_url(
+ **Docuseal.default_url_options, host: ENV.fetch('EMAIL_HOST', Docuseal.default_url_options[:host])
+ )
+
page_size =
if TimeUtils.timezone_abbr(account.timezone, Time.current.beginning_of_year).in?(US_TIMEZONES)
:Letter
@@ -411,7 +414,7 @@ def add_logo(column, _submission = nil)
column.image(PdfIcons.logo_io, width: 40, height: 40, position: :float)
column.formatted_text([{ text: 'DocuSeal',
- link: Docuseal::PRODUCT_URL }],
+ link: Docuseal::PRODUCT_EMAIL_URL }],
font_size: 20,
font: [FONT_NAME, { variant: :bold }],
width: 100,
diff --git a/lib/submissions/generate_export_files.rb b/lib/submissions/generate_export_files.rb
index ac7cca5bf..506ee1ba5 100644
--- a/lib/submissions/generate_export_files.rb
+++ b/lib/submissions/generate_export_files.rb
@@ -110,6 +110,10 @@ def build_submission_data(submitter, submitter_name, submitters_count)
{
name: column_name(I18n.t('completed_at'), submitter_name, submitters_count),
value: submitter.completed_at.to_s
+ },
+ {
+ name: column_name(I18n.t('link'), submitter_name, submitters_count),
+ value: submitter.completed_at? ? nil : r.submit_form_url(slug: submitter.slug, **Docuseal.default_url_options)
}
].reject { |e| e[:value].blank? }
end
@@ -150,5 +154,9 @@ def submitter_formatted_fields(submitter)
{ name: template_field_name, uuid: template_field['uuid'], value: }
end
end
+
+ def r
+ Rails.application.routes.url_helpers
+ end
end
end
diff --git a/lib/submitters.rb b/lib/submitters.rb
index 18b2d1517..bfd168ad2 100644
--- a/lib/submitters.rb
+++ b/lib/submitters.rb
@@ -94,12 +94,16 @@ def normalize_preferences(account, user, params)
preferences
end
- def send_signature_requests(submitters)
- submitters.each do |submitter|
+ def send_signature_requests(submitters, delay_seconds: nil)
+ submitters.each_with_index do |submitter, index|
next if submitter.email.blank?
next if submitter.preferences['send_email'] == false
- SendSubmitterInvitationEmailJob.perform_async('submitter_id' => submitter.id)
+ if delay_seconds
+ SendSubmitterInvitationEmailJob.perform_in((delay_seconds + index).seconds, 'submitter_id' => submitter.id)
+ else
+ SendSubmitterInvitationEmailJob.perform_async('submitter_id' => submitter.id)
+ end
end
end
@@ -112,4 +116,19 @@ def current_submitter_order?(submitter)
acc && submitter.submission.submitters.find { |e| e.uuid == item['uuid'] }&.completed_at?
end
end
+
+ def build_document_filename(submitter, blob, filename_format)
+ return blob.filename.to_s if filename_format.blank?
+
+ filename = ReplaceEmailVariables.call(filename_format, submitter:)
+
+ filename = filename.gsub('{document.name}', blob.filename.base)
+
+ filename = filename.gsub(
+ '{submission.completed_at}',
+ I18n.l(submitter.completed_at.beginning_of_year.in_time_zone(submitter.account.timezone), format: :short)
+ )
+
+ "#{filename}.#{blob.filename.extension}"
+ end
end
diff --git a/lib/submitters/submit_values.rb b/lib/submitters/submit_values.rb
index 40e96618c..65ecad757 100644
--- a/lib/submitters/submit_values.rb
+++ b/lib/submitters/submit_values.rb
@@ -93,7 +93,11 @@ def normalized_values(params)
if params[:cast_boolean] == 'true'
v == 'true'
elsif params[:cast_number] == 'true'
- (v.to_f % 1).zero? ? v.to_i : v.to_f
+ if v == ''
+ nil
+ else
+ (v.to_f % 1).zero? ? v.to_i : v.to_f
+ end
elsif params[:normalize_phone] == 'true'
v.to_s.gsub(/[^0-9+]/, '')
else
@@ -207,7 +211,7 @@ def check_field_condition(submitter, field, fields_uuid_index)
end
def replace_default_variables(value, attrs, submission, with_time: false)
- return value if value.in?([true, false])
+ return value if value.in?([true, false]) || value.is_a?(Numeric)
return if value.blank?
value.to_s.gsub(VARIABLE_REGEXP) do |e|
diff --git a/lib/time_utils.rb b/lib/time_utils.rb
index 1f4991958..9150b1b97 100644
--- a/lib/time_utils.rb
+++ b/lib/time_utils.rb
@@ -22,6 +22,8 @@ module TimeUtils
DEFAULT_DATE_FORMAT_US = 'MM/DD/YYYY'
DEFAULT_DATE_FORMAT = 'DD/MM/YYYY'
+ US_TIMEZONES = %w[EST CST MST PST HST AKDT].freeze
+
module_function
def timezone_abbr(timezone, time = Time.current)
diff --git a/spec/factories/submissions.rb b/spec/factories/submissions.rb
index 7a055dbed..ea59021ee 100644
--- a/spec/factories/submissions.rb
+++ b/spec/factories/submissions.rb
@@ -17,7 +17,8 @@
submission.template_submitters.each do |template_submitter|
create(:submitter, submission:,
account_id: submission.account_id,
- uuid: template_submitter['uuid'])
+ uuid: template_submitter['uuid'],
+ created_at: submission.created_at)
end
end
end
diff --git a/spec/factories/submitters.rb b/spec/factories/submitters.rb
index 7bd771b0c..d478186ef 100644
--- a/spec/factories/submitters.rb
+++ b/spec/factories/submitters.rb
@@ -5,7 +5,6 @@
submission
email { Faker::Internet.email }
name { Faker::Name.name }
- phone { Faker::PhoneNumber.phone_number }
before(:create) do |submitter, _|
submitter.account_id = submitter.submission.account_id
diff --git a/spec/factories/templates.rb b/spec/factories/templates.rb
index f4f2ff624..0f7375121 100644
--- a/spec/factories/templates.rb
+++ b/spec/factories/templates.rb
@@ -9,9 +9,13 @@
transient do
submitter_count { 1 }
+ only_field_types do
+ %w[text date checkbox radio signature number multiple select initials image file stamp cells phone payment]
+ end
+ except_field_types { [] }
end
- after(:create) do |template, evaluator|
+ after(:create) do |template, ev|
blob = ActiveStorage::Blob.create_and_upload!(
io: Rails.root.join('spec/fixtures/sample-document.pdf').open,
filename: 'sample-document.pdf',
@@ -28,7 +32,7 @@
template.schema = [{ attachment_uuid: attachment.uuid, name: 'sample-document' }]
number_words = %w[first second third fourth fifth sixth seventh eighth ninth tenth]
- template.submitters = Array.new(evaluator.submitter_count) do |i|
+ template.submitters = Array.new(ev.submitter_count) do |i|
{
'name' => "#{number_words[i]&.capitalize} Party",
'uuid' => SecureRandom.uuid
@@ -43,12 +47,71 @@
'name' => 'First Name',
'type' => 'text',
'required' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.09273546006944444,
+ 'y' => 0.1099851117387033,
+ 'w' => 0.2701497395833333,
+ 'h' => 0.0372705365913556,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Birthday',
+ 'type' => 'date',
+ 'required' => true,
+ 'preferences' => { 'format' => 'DD/MM/YYYY' },
+ 'areas' => [
+ {
+ 'x' => 0.09166666666666666,
+ 'y' => 0.1762778204144282,
+ 'w' => 0.2763888888888889,
+ 'h' => 0.0359029261474578,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Do you agree?',
+ 'type' => 'checkbox',
+ 'required' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.09051106770833334,
+ 'y' => 0.227587027259332,
+ 'w' => 0.2784450954861111,
+ 'h' => 0.04113074042239687,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'First child',
+ 'type' => 'radio',
+ 'required' => true,
+ 'preferences' => {},
+ 'options' => [
+ { 'value' => 'Girl', 'uuid' => SecureRandom.uuid },
+ { 'value' => 'Boy', 'uuid' => SecureRandom.uuid }
+ ],
'areas' => [
{
'x' => 0.09027777777777778,
- 'y' => 0.1197252208047105,
- 'w' => 0.3069444444444444,
- 'h' => 0.03336604514229637,
+ 'y' => 0.3020184190330008,
+ 'w' => 0.2,
+ 'h' => 0.02857142857142857,
'attachment_uuid' => attachment.uuid,
'page' => 0
}
@@ -57,12 +120,213 @@
{
'uuid' => SecureRandom.uuid,
'submitter_uuid' => submitter['uuid'],
- 'name' => '',
+ 'name' => 'Signature',
'type' => 'signature',
'required' => true,
- 'areas' => []
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.08611111111111111,
+ 'y' => 0.3487183422870299,
+ 'w' => 0.2,
+ 'h' => 0.0707269155206287,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'House number',
+ 'type' => 'number',
+ 'required' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.08333333333333333,
+ 'y' => 0.4582041442824252,
+ 'w' => 0.2,
+ 'h' => 0.02857142857142857,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Colors',
+ 'type' => 'multiple',
+ 'required' => true,
+ 'preferences' => {},
+ 'options' => [
+ { 'value' => 'Red', 'uuid' => SecureRandom.uuid },
+ { 'value' => 'Green', 'uuid' => SecureRandom.uuid },
+ { 'value' => 'Blue', 'uuid' => SecureRandom.uuid }
+ ],
+ 'areas' => [
+ {
+ 'x' => 0.45,
+ 'y' => 0.1133998465080583,
+ 'w' => 0.2,
+ 'h' => 0.02857142857142857,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Gender',
+ 'type' => 'select',
+ 'required' => true,
+ 'preferences' => {},
+ 'options' => [
+ { 'value' => 'Male', 'uuid' => SecureRandom.uuid },
+ { 'value' => 'Female', 'uuid' => SecureRandom.uuid }
+ ],
+ 'areas' => [
+ {
+ 'x' => 0.4513888888888889,
+ 'y' => 0.1752954719877206,
+ 'w' => 0.2,
+ 'h' => 0.02857142857142857,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Initials',
+ 'type' => 'initials',
+ 'required' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.4486111111111111,
+ 'y' => 0.2273599386032233,
+ 'w' => 0.1,
+ 'h' => 0.02857142857142857,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Avatar',
+ 'type' => 'image',
+ 'required' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.7180555555555556,
+ 'y' => 0.1129547198772064,
+ 'w' => 0.2,
+ 'h' => 0.1414538310412574,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Attachment',
+ 'type' => 'file',
+ 'required' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.7166666666666667,
+ 'y' => 0.3020107444359171,
+ 'w' => 0.2,
+ 'h' => 0.02857142857142857,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Stamp',
+ 'type' => 'stamp',
+ 'required' => true,
+ 'readonly' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.7166666666666667,
+ 'y' => 0.3771910974673829,
+ 'w' => 0.2,
+ 'h' => 0.0707269155206287,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Cell code',
+ 'type' => 'cells',
+ 'required' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.4472222222222222,
+ 'y' => 0.3530851880276286,
+ 'w' => 0.2,
+ 'h' => 0.02857142857142857,
+ 'cell_w' => 0.04,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Payment',
+ 'type' => 'payment',
+ 'required' => true,
+ 'preferences' => { 'currency' => 'EUR', 'price' => 1000 },
+ 'areas' => [
+ {
+ 'x' => 0.4486111111111111,
+ 'y' => 0.43168073676132,
+ 'w' => 0.2,
+ 'h' => 0.02857142857142857,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
+ },
+ {
+ 'uuid' => SecureRandom.uuid,
+ 'submitter_uuid' => submitter['uuid'],
+ 'name' => 'Mobile Phone',
+ 'type' => 'phone',
+ 'required' => true,
+ 'preferences' => {},
+ 'areas' => [
+ {
+ 'x' => 0.44443359375,
+ 'y' => 0.3010283960092095,
+ 'w' => 0.2,
+ 'h' => 0.02857142857142857,
+ 'attachment_uuid' => attachment.uuid,
+ 'page' => 0
+ }
+ ]
}
- ]
+ ].select { |f| ev.only_field_types.include?(f['type']) && ev.except_field_types.exclude?(f['type']) }
fields
end
diff --git a/spec/fixtures/sample-image.png b/spec/fixtures/sample-image.png
new file mode 100644
index 000000000..f86af5d88
Binary files /dev/null and b/spec/fixtures/sample-image.png differ
diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb
index 86a15a9f8..1157da614 100644
--- a/spec/rails_helper.rb
+++ b/spec/rails_helper.rb
@@ -10,6 +10,7 @@
require 'capybara/rspec'
require 'webmock/rspec'
require 'sidekiq/testing'
+require 'signing_form_helper'
Sidekiq::Testing.fake!
@@ -50,6 +51,7 @@
config.include FactoryBot::Syntax::Methods
config.include Devise::Test::IntegrationHelpers
+ config.include SigningFormHelper
config.before(:each, type: :system) do
if ENV['HEADLESS'] == 'false'
@@ -67,3 +69,5 @@
Sidekiq::Testing.inline! if example.metadata[:sidekiq] == :inline
end
end
+
+ActiveSupport.run_load_hooks(:rails_specs, self)
diff --git a/spec/requests/templates_spec.rb b/spec/requests/templates_spec.rb
index 26bdb5462..3d6e1284b 100644
--- a/spec/requests/templates_spec.rb
+++ b/spec/requests/templates_spec.rb
@@ -176,33 +176,7 @@ def template_body(template)
id: template.id,
slug: template.slug,
name: template.name,
- fields: [
- {
- 'uuid' => template.fields[0]['uuid'],
- 'submitter_uuid' => template.submitters[0]['uuid'],
- 'name' => 'First Name',
- 'type' => 'text',
- 'required' => true,
- 'areas' => [
- {
- 'x' => 0.09027777777777778,
- 'y' => 0.1197252208047105,
- 'w' => 0.3069444444444444,
- 'h' => 0.03336604514229637,
- 'attachment_uuid' => template_attachment_uuid,
- 'page' => 0
- }
- ]
- },
- {
- 'uuid' => template.fields[1]['uuid'],
- 'submitter_uuid' => template.submitters[0]['uuid'],
- 'name' => '',
- 'type' => 'signature',
- 'required' => true,
- 'areas' => []
- }
- ],
+ fields: template.fields,
submitters: [
{
name: 'First Party',
diff --git a/spec/signing_form_helper.rb b/spec/signing_form_helper.rb
new file mode 100644
index 000000000..13aadfc26
--- /dev/null
+++ b/spec/signing_form_helper.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module SigningFormHelper
+ module_function
+
+ def draw_canvas
+ page.find('canvas').click([], { x: 150, y: 100 })
+ page.execute_script <<~JS
+ const canvas = document.getElementsByTagName('canvas')[0];
+ const ctx = canvas.getContext('2d');
+
+ ctx.beginPath();
+ ctx.moveTo(150, 100);
+ ctx.lineTo(450, 100);
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.moveTo(150, 100);
+ ctx.lineTo(150, 150);
+ ctx.stroke();
+ JS
+ sleep 1
+ end
+
+ def field_value(submitter, field_name)
+ field = template_field(submitter.template, field_name)
+
+ submitter.values[field['uuid']]
+ end
+
+ def template_field(template, field_name)
+ template.fields.find { |f| f['name'] == field_name || f['title'] == field_name } || {}
+ end
+end
diff --git a/spec/system/personalization_spec.rb b/spec/system/personalization_spec.rb
index d5e4f1131..f82173a1c 100644
--- a/spec/system/personalization_spec.rb
+++ b/spec/system/personalization_spec.rb
@@ -17,7 +17,5 @@
expect(page).to have_content('Completed Notification Email')
expect(page).to have_content('Documents Copy Email')
expect(page).to have_content('Company Logo')
- expect(page).to have_content('Unlock with DocuSeal Pro')
- expect(page).to have_content('Display your company name and logo when signing documents')
end
end
diff --git a/spec/system/signing_form_spec.rb b/spec/system/signing_form_spec.rb
new file mode 100644
index 000000000..349e029bc
--- /dev/null
+++ b/spec/system/signing_form_spec.rb
@@ -0,0 +1,598 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe 'Signing Form', type: :system do
+ let(:account) { create(:account) }
+ let(:author) { create(:user, account:) }
+
+ context 'when the template form link is opened' do
+ let(:template) { create(:template, account:, author:, except_field_types: %w[phone payment stamp]) }
+
+ before do
+ visit start_form_path(slug: template.slug)
+ end
+
+ it 'shows the email step', type: :system do
+ expect(page).to have_content('You have been invited to submit a form')
+ expect(page).to have_content("Invited by #{account.name}")
+ expect(page).to have_field('Email', type: 'email')
+ expect(page).to have_button('Start')
+ end
+
+ it 'completes the form' do
+ # Submit's email step
+ fill_in 'Email', with: 'john.dou@example.com'
+ click_button 'Start'
+
+ # Text step
+ fill_in 'First Name', with: 'John'
+ click_button 'next'
+
+ # Date step
+ fill_in 'Birthday', with: I18n.l(20.years.ago, format: '%Y-%m-%d')
+ click_button 'next'
+
+ # Checkbox step
+ check 'Do you agree?'
+ click_button 'next'
+
+ # Radio step
+ choose 'Boy'
+ click_button 'next'
+
+ # Signature step
+ draw_canvas
+ click_button 'next'
+
+ # Number step
+ fill_in 'House number', with: '123'
+ click_button 'next'
+
+ # Multiple choice step
+ %w[Red Blue].each { |color| check color }
+ click_button 'next'
+
+ # Select step
+ select 'Male', from: 'Gender'
+ click_button 'next'
+
+ # Initials step
+ draw_canvas
+ click_button 'next'
+
+ # Image step
+ find('#dropzone').click
+ find('input[type="file"]', visible: false).attach_file(Rails.root.join('spec/fixtures/sample-image.png'))
+ click_button 'next'
+
+ # File step
+ find('#dropzone').click
+ find('input[type="file"]', visible: false).attach_file(Rails.root.join('spec/fixtures/sample-document.pdf'))
+ click_button 'next'
+
+ # Cell step
+ fill_in 'Cell code', with: '123'
+ click_on 'Complete'
+
+ expect(page).to have_button('Download')
+ expect(page).to have_content('Document has been signed!')
+
+ submitter = template.submissions.last.submitters.last
+
+ expect(submitter.email).to eq('john.dou@example.com')
+ expect(submitter.ip).to eq('127.0.0.1')
+ expect(submitter.ua).to be_present
+ expect(submitter.opened_at).to be_present
+ expect(submitter.completed_at).to be_present
+ expect(submitter.declined_at).to be_nil
+
+ expect(field_value(submitter, 'First Name')).to eq 'John'
+ expect(field_value(submitter, 'Birthday')).to eq 20.years.ago.strftime('%Y-%m-%d')
+ expect(field_value(submitter, 'Do you agree?')).to be_truthy
+ expect(field_value(submitter, 'First child')).to eq 'Boy'
+ expect(field_value(submitter, 'Signature')).to be_present
+ expect(field_value(submitter, 'House number')).to eq 123
+ expect(field_value(submitter, 'Colors')).to contain_exactly('Red', 'Blue')
+ expect(field_value(submitter, 'Gender')).to eq 'Male'
+ expect(field_value(submitter, 'Initials')).to be_present
+ expect(field_value(submitter, 'Avatar')).to be_present
+ expect(field_value(submitter, 'Attachment')).to be_present
+ expect(field_value(submitter, 'Cell code')).to eq '123'
+ end
+ end
+
+ context 'when the submitter form link is opened' do
+ let(:template) { create(:template, account:, author:, except_field_types: %w[phone payment stamp]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:, email: 'robin@example.com')
+ end
+
+ before do
+ visit submit_form_path(slug: submitter.slug)
+ end
+
+ it 'complete the form' do
+ # Text step
+ fill_in 'First Name', with: 'John'
+ click_button 'next'
+
+ # Date step
+ fill_in 'Birthday', with: I18n.l(20.years.ago, format: '%Y-%m-%d')
+ click_button 'next'
+
+ # Checkbox step
+ check 'Do you agree?'
+ click_button 'next'
+
+ # Radio step
+ choose 'Boy'
+ click_button 'next'
+
+ # Signature step
+ draw_canvas
+ click_button 'next'
+
+ # Number step
+ fill_in 'House number', with: '123'
+ click_button 'next'
+
+ # Multiple choice step
+ %w[Red Blue].each { |color| check color }
+ click_button 'next'
+
+ # Select step
+ select 'Male', from: 'Gender'
+ click_button 'next'
+
+ # Initials step
+ draw_canvas
+ click_button 'next'
+
+ # Image step
+ find('#dropzone').click
+ find('input[type="file"]', visible: false).attach_file(Rails.root.join('spec/fixtures/sample-image.png'))
+ click_button 'next'
+
+ # File step
+ find('#dropzone').click
+ find('input[type="file"]', visible: false).attach_file(Rails.root.join('spec/fixtures/sample-document.pdf'))
+ click_button 'next'
+
+ # Cell step
+ fill_in 'Cell code', with: '123'
+ click_on 'Complete'
+
+ expect(page).to have_button('Download')
+ expect(page).to have_content('Document has been signed!')
+
+ submitter.reload
+
+ expect(submitter.email).to eq 'robin@example.com'
+ expect(submitter.ip).to eq('127.0.0.1')
+ expect(submitter.ua).to be_present
+ expect(submitter.opened_at).to be_present
+ expect(submitter.completed_at).to be_present
+ expect(submitter.declined_at).to be_nil
+
+ expect(field_value(submitter, 'First Name')).to eq 'John'
+ expect(field_value(submitter, 'Birthday')).to eq 20.years.ago.strftime('%Y-%m-%d')
+ expect(field_value(submitter, 'Do you agree?')).to be_truthy
+ expect(field_value(submitter, 'First child')).to eq 'Boy'
+ expect(field_value(submitter, 'Signature')).to be_present
+ expect(field_value(submitter, 'House number')).to eq 123
+ expect(field_value(submitter, 'Colors')).to contain_exactly('Red', 'Blue')
+ expect(field_value(submitter, 'Gender')).to eq 'Male'
+ expect(field_value(submitter, 'Initials')).to be_present
+ expect(field_value(submitter, 'Avatar')).to be_present
+ expect(field_value(submitter, 'Attachment')).to be_present
+ expect(field_value(submitter, 'Cell code')).to eq '123'
+ end
+ end
+
+ context 'when the text step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[text]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the field is filled' do
+ visit submit_form_path(slug: submitter.slug)
+
+ input = find_field('First Name')
+
+ expect(input[:required]).to be_truthy
+ expect(input[:placeholder]).to eq 'Type here...'
+
+ fill_in 'First Name', with: 'Mary'
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'First Name')).to eq 'Mary'
+ end
+
+ it 'toggle multiple text button' do
+ visit submit_form_path(slug: submitter.slug)
+
+ input = find_field('First Name')
+
+ expect(input.tag_name).to eq('input')
+
+ find(:css, 'div[data-tip="Toggle Multiline Text"]').click
+
+ input = find_field('First Name')
+
+ expect(input.tag_name).to eq('textarea')
+ expect(page).not_to have_selector(:css, 'div[data-tip="Toggle Multiline Text"]')
+
+ fill_in 'First Name', with: 'Very long text'
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(field_value(submitter, 'First Name')).to eq 'Very long text'
+ expect(submitter.completed_at).to be_present
+ end
+ end
+
+ context 'when the date step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[date]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the field is filled' do
+ visit submit_form_path(slug: submitter.slug)
+
+ input = find_field('Birthday')
+
+ expect(input[:required]).to be_truthy
+
+ fill_in 'Birthday', with: I18n.l(25.years.ago, format: '%Y-%m-%d')
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Birthday')).to eq 25.years.ago.strftime('%Y-%m-%d')
+ end
+
+ it 'pre-fills the current date into the form field' do
+ visit submit_form_path(slug: submitter.slug)
+
+ input = find_field('Birthday')
+
+ expect(input[:value]).to eq ''
+
+ click_button 'Set Today'
+
+ input = find_field('Birthday')
+
+ expect(input[:value]).to eq Time.zone.now.strftime('%Y-%m-%d')
+
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Birthday')).to eq Time.zone.now.strftime('%Y-%m-%d')
+ end
+ end
+
+ context 'when the checkbox step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[checkbox]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the checkbox is checked' do
+ visit submit_form_path(slug: submitter.slug)
+
+ check 'Do you agree?'
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Do you agree?')).to be true
+ end
+ end
+
+ context 'when the radio step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[radio]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the checkbox is checked' do
+ visit submit_form_path(slug: submitter.slug)
+
+ %w[Girl Boy].map { |v| find_field(v) }.each { |input| expect(input[:required]).to be_truthy }
+
+ choose 'Boy'
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'First child')).to eq 'Boy'
+ end
+ end
+
+ context 'when the signature step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[signature]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the canvas is drawn' do
+ visit submit_form_path(slug: submitter.slug)
+
+ find('#expand_form_button').click
+ draw_canvas
+ click_button 'Sign and Complete'
+
+ expect(page).to have_content('Document has been signed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Signature')).to be_present
+ end
+
+ it 'completes the form if the canvas is typed' do
+ visit submit_form_path(slug: submitter.slug)
+
+ find('#expand_form_button').click
+ click_link 'Type'
+ fill_in 'signature_text_input', with: 'John Doe'
+ click_button 'Sign and Complete'
+
+ expect(page).to have_content('Document has been signed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Signature')).to be_present
+ end
+ end
+
+ context 'when the number step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[number]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the field is filled' do
+ visit submit_form_path(slug: submitter.slug)
+
+ input = find_field('House number')
+
+ expect(input[:required]).to be_truthy
+ expect(input[:placeholder]).to eq 'Type here...'
+
+ fill_in 'House number', with: '4'
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'House number')).to eq 4
+ end
+ end
+
+ context 'when the multiple choice step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[multiple]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the multiple choice is checked' do
+ visit submit_form_path(slug: submitter.slug)
+
+ %w[Red Green].each { |color| check color }
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Colors')).to contain_exactly('Red', 'Green')
+ end
+ end
+
+ context 'when the select step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[select]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the multiple choice is checked' do
+ visit submit_form_path(slug: submitter.slug)
+
+ select 'Female', from: 'Gender'
+
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Gender')).to eq 'Female'
+ end
+ end
+
+ context 'when the initials step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[initials]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the canvas is typed' do
+ visit submit_form_path(slug: submitter.slug)
+
+ find('#expand_form_button').click
+ fill_in 'initials_text_input', with: 'John Doe'
+ click_button 'Complete'
+
+ expect(page).to have_content('Document has been signed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Initials')).to be_present
+ end
+
+ it 'completes the form if the canvas is drawn' do
+ visit submit_form_path(slug: submitter.slug)
+
+ find('#expand_form_button').click
+ click_link 'Draw'
+ draw_canvas
+ click_button 'Complete'
+
+ expect(page).to have_content('Document has been signed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Initials')).to be_present
+ end
+
+ it 'completes the form if the initials is uploaded' do
+ visit submit_form_path(slug: submitter.slug)
+
+ find('#expand_form_button').click
+ find('span[data-tip="Click to upload"]').click
+ find('input[type="file"]', visible: false).attach_file(Rails.root.join('spec/fixtures/sample-image.png'))
+ click_button 'Complete'
+
+ expect(page).to have_content('Document has been signed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Initials')).to be_present
+ end
+ end
+
+ context 'when the image step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[image]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the image is uploaded' do
+ visit submit_form_path(slug: submitter.slug)
+
+ find('#expand_form_button').click
+ find('#dropzone').click
+ find('input[type="file"]', visible: false).attach_file(Rails.root.join('spec/fixtures/sample-image.png'))
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Avatar')).to be_present
+ end
+ end
+
+ context 'when the file step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[file]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the file is uploaded' do
+ visit submit_form_path(slug: submitter.slug)
+
+ find('#expand_form_button').click
+ find('#dropzone').click
+ find('input[type="file"]', visible: false).attach_file(Rails.root.join('spec/fixtures/sample-document.pdf'))
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Attachment')).to be_present
+ end
+ end
+
+ context 'when the cells step' do
+ let(:template) { create(:template, account:, author:, only_field_types: %w[cells]) }
+ let(:submission) { create(:submission, template:) }
+ let(:submitter) do
+ create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+ end
+
+ it 'completes the form if the field is filled' do
+ visit submit_form_path(slug: submitter.slug)
+
+ input = find_field('Cell code')
+
+ expect(input[:required]).to be_truthy
+ expect(input[:placeholder]).to eq 'Type here...'
+
+ fill_in 'Cell code', with: '456'
+ click_button 'Complete'
+
+ expect(page).to have_content('Form has been completed!')
+
+ submitter.reload
+
+ expect(submitter.completed_at).to be_present
+ expect(field_value(submitter, 'Cell code')).to eq '456'
+ end
+ end
+
+ it 'sends completed email' do
+ template = create(:template, account:, author:, only_field_types: %w[text signature])
+ submission = create(:submission, template:)
+ submitter = create(:submitter, submission:, uuid: template.submitters.first['uuid'], account:)
+
+ visit submit_form_path(slug: submitter.slug)
+
+ fill_in 'First Name', with: 'Adam'
+ click_on 'next'
+ click_link 'Type'
+ fill_in 'signature_text_input', with: 'Adam'
+
+ expect do
+ click_on 'Sign and Complete'
+ end.to change(ProcessSubmitterCompletionJob.jobs, :size).by(1)
+ end
+end
diff --git a/spec/system/submit_form_spec.rb b/spec/system/submit_form_spec.rb
deleted file mode 100644
index 7c2f32230..000000000
--- a/spec/system/submit_form_spec.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-# frozen_string_literal: true
-
-require 'rails_helper'
-
-RSpec.describe 'Submit Form' do
- let(:account) { create(:account) }
- let(:user) { create(:user, account:) }
- let(:template) { create(:template, account:, author: user) }
-
- before do
- sign_in(user)
- end
-
- context 'when initialized by shared link' do
- before do
- visit start_form_path(slug: template.slug)
- end
-
- it 'shows start form page' do
- expect(page).to have_content('You have been invited to submit a form')
- expect(page).to have_content(template.name)
- expect(page).to have_content("Invited by #{template.account.name}")
- end
-
- it 'complete the form' do
- fill_in 'Email', with: 'john.dou@example.com'
- click_button 'Start'
-
- fill_in 'First Name', with: 'Adam'
- click_on 'next'
- click_on 'type_text_button'
- fill_in 'signature_text_input', with: 'Adam'
-
- expect do
- click_on 'Sign and Complete'
- end.not_to(change(Submitter, :count))
-
- submitter = Submitter.find_by(email: 'john.dou@example.com')
-
- expect(page).to have_button('Download')
- expect(submitter.email).to eq('john.dou@example.com')
- expect(submitter.ip).to eq('127.0.0.1')
- expect(submitter.ua).to be_present
- expect(submitter.opened_at).to be_present
- expect(submitter.completed_at).to be_present
- expect(submitter.values.values).to include('Adam')
- end
- end
-
- context 'when initialized by shared email address' do
- let(:submission) { create(:submission, template:, created_by_user: user) }
- let(:submitters) { template.submitters.map { |s| create(:submitter, submission:, uuid: s['uuid']) } }
- let(:submitter) { submitters.first }
-
- before do
- visit submit_form_path(slug: submitter.slug)
- end
-
- it 'completes the form' do
- fill_in 'First Name', with: 'Sally'
- click_on 'next'
- click_on 'type_text_button'
- fill_in 'signature_text_input', with: 'Sally'
- click_on 'Sign and Complete'
-
- submitter.reload
-
- expect(page).to have_button('Download')
- expect(submitter.ip).to eq('127.0.0.1')
- expect(submitter.ua).to be_present
- expect(submitter.opened_at).to be_present
- expect(submitter.completed_at).to be_present
- expect(submitter.values.values).to include('Sally')
- end
-
- it 'sends completed email' do
- fill_in 'First Name', with: 'Adam'
- click_on 'next'
- click_on 'type_text_button'
- fill_in 'signature_text_input', with: 'Adam'
-
- expect do
- click_on 'Sign and Complete'
- end.to change(ProcessSubmitterCompletionJob.jobs, :size).by(1)
- end
- end
-end
diff --git a/spec/system/template_spec.rb b/spec/system/template_spec.rb
index 0e36ef94d..0b512041f 100644
--- a/spec/system/template_spec.rb
+++ b/spec/system/template_spec.rb
@@ -5,7 +5,7 @@
RSpec.describe 'Template' do
let!(:account) { create(:account) }
let!(:user) { create(:user, account:) }
- let!(:template) { create(:template, account:, author: user) }
+ let!(:template) { create(:template, account:, author: user, except_field_types: %w[phone payment]) }
before do
sign_in(user)
@@ -140,4 +140,98 @@
end
end
end
+
+ context 'when filtering submissions' do
+ let(:second_user) { create(:user, account:) }
+
+ it 'displays only submissions by the selected author' do
+ first_user_submissions = create_list(:submission, 5, :with_submitters, template:, created_by_user: user)
+ second_user_submissions = create_list(:submission, 6, :with_submitters, template:, created_by_user: second_user)
+
+ visit template_path(template)
+
+ (first_user_submissions + second_user_submissions).map(&:submitters).flatten.last(10).uniq.each do |submitter|
+ expect(page).to have_content(submitter.name)
+ end
+
+ page.find('.dropdown', text: 'Filter').click
+ click_link 'Author'
+ within '#modal' do
+ select second_user.full_name, from: 'author'
+ click_button 'Apply'
+ end
+
+ second_user_submissions.map(&:submitters).flatten.uniq.each do |submitter|
+ expect(page).to have_content(submitter.name)
+ end
+
+ first_user_submissions.map(&:submitters).flatten.uniq.each do |submitter|
+ expect(page).not_to have_content(submitter.name)
+ end
+ end
+
+ it 'displays submissions created within the selected date range' do
+ last_week_submissions = create_list(:submission, 5, :with_submitters, template:, created_by_user: user,
+ created_at: 9.days.ago)
+ this_week_submissions = create_list(:submission, 6, :with_submitters, template:, created_by_user: user,
+ created_at: 5.days.ago)
+
+ visit template_path(template)
+
+ (last_week_submissions + this_week_submissions).map(&:submitters).flatten.last(10).uniq.each do |submitter|
+ expect(page).to have_content(submitter.name)
+ end
+
+ page.find('.dropdown', text: 'Filter').click
+ click_link 'Created at'
+ within '#modal' do
+ fill_in 'From', with: I18n.l(10.days.ago, format: '%Y-%m-%d')
+ fill_in 'To', with: I18n.l(6.days.ago, format: '%Y-%m-%d')
+ click_button 'Apply'
+ end
+
+ last_week_submissions.map(&:submitters).flatten.uniq.each do |submitter|
+ expect(page).to have_content(submitter.name)
+ end
+
+ this_week_submissions.map(&:submitters).flatten.uniq.each do |submitter|
+ expect(page).not_to have_content(submitter.name)
+ end
+ end
+
+ it 'displays submissions completed within the selected date range' do
+ last_week_submissions = create_list(:submission, 5, :with_submitters, template:, created_by_user: user)
+ this_week_submissions = create_list(:submission, 6, :with_submitters, template:, created_by_user: user)
+
+ last_week_submissions.map(&:submitters).flatten.each do |submitter|
+ submitter.update!(completed_at: rand(6..10).days.ago)
+ end
+
+ this_week_submissions.map(&:submitters).flatten.each do |submitter|
+ submitter.update!(completed_at: rand(2..5).days.ago)
+ end
+
+ visit template_path(template)
+
+ (last_week_submissions + this_week_submissions).map(&:submitters).flatten.last(10).uniq.each do |submitter|
+ expect(page).to have_content(submitter.name)
+ end
+
+ page.find('.dropdown', text: 'Filter').click
+ click_link 'Completed at'
+ within '#modal' do
+ fill_in 'From', with: I18n.l(5.days.ago, format: '%Y-%m-%d')
+ fill_in 'To', with: I18n.l(1.day.ago, format: '%Y-%m-%d')
+ click_button 'Apply'
+ end
+
+ this_week_submissions.map(&:submitters).flatten.uniq.each do |submitter|
+ expect(page).to have_content(submitter.name)
+ end
+
+ last_week_submissions.map(&:submitters).flatten.uniq.each do |submitter|
+ expect(page).not_to have_content(submitter.name)
+ end
+ end
+ end
end