mirror of https://github.com/docusealco/docuseal
commit
fa2d86b20a
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class SubmissionsFiltersController < ApplicationController
|
||||
ALLOWED_NAMES = %w[
|
||||
author
|
||||
completed_at
|
||||
created_at
|
||||
].freeze
|
||||
|
||||
skip_authorization_check
|
||||
|
||||
def show
|
||||
return head :not_found unless ALLOWED_NAMES.include?(params[:name])
|
||||
|
||||
render params[:name]
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,20 @@
|
||||
export default class extends HTMLElement {
|
||||
connectedCallback () {
|
||||
this.button.addEventListener('click', () => {
|
||||
this.fromInput.value = this.dataset.fromValue || ''
|
||||
this.toInput.value = this.dataset.toValue || ''
|
||||
})
|
||||
}
|
||||
|
||||
get button () {
|
||||
return this.querySelector('button')
|
||||
}
|
||||
|
||||
get fromInput () {
|
||||
return document.getElementById(this.dataset.fromId)
|
||||
}
|
||||
|
||||
get toInput () {
|
||||
return document.getElementById(this.dataset.toId)
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 464 B |
|
After Width: | Height: | Size: 400 B |
|
After Width: | Height: | Size: 290 B |
@ -0,0 +1,50 @@
|
||||
<% query_params = params.permit(:q, :status).merge(filter_params) %>
|
||||
<% if query_params[:completed_at_from].present? || query_params[:completed_at_to].present? %>
|
||||
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('completed_at') %>">
|
||||
<%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||
<%= svg_icon('calendar_check', class: 'w-5 h-5 shrink-0') %>
|
||||
<span class="flex flex-row md:flex-col font-normal text-left md:text-center md:text-xs">
|
||||
<% if query_params[:completed_at_from] == query_params[:completed_at_to] %>
|
||||
<span><%= l(Date.parse(query_params[:completed_at_from]), locale: current_account.locale) %></span>
|
||||
<% else %>
|
||||
<span><%= query_params[:completed_at_from].present? ? l(Date.parse(query_params[:completed_at_from]), locale: current_account.locale) : '∞' %></span>
|
||||
<span class="px-1 md:px-0 md:hidden">-</span>
|
||||
<span><%= query_params[:completed_at_to].present? ? l(Date.parse(query_params[:completed_at_to]), locale: current_account.locale) : t('today') %></span>
|
||||
<% end %>
|
||||
</span>
|
||||
<% end %>
|
||||
<%= link_to url_for(params.to_unsafe_h.except(:completed_at_from, :completed_at_to)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
|
||||
<%= svg_icon('x', class: 'w-5 h-5') %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if query_params[:created_at_from].present? || query_params[:created_at_to].present? %>
|
||||
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('created_at') %>">
|
||||
<%= link_to submissions_filter_path('created_at', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||
<%= svg_icon('calendar', class: 'w-5 h-5 shrink-0') %>
|
||||
<span class="flex flex-row md:flex-col font-normal text-left md:text-center md:text-xs">
|
||||
<% if query_params[:created_at_from] == query_params[:created_at_to] %>
|
||||
<span><%= l(Date.parse(query_params[:created_at_from]), locale: current_account.locale) %></span>
|
||||
<% else %>
|
||||
<span><%= query_params[:created_at_from].present? ? l(Date.parse(query_params[:created_at_from]), locale: current_account.locale) : '∞' %></span>
|
||||
<span class="px-1 md:px-0 md:hidden">-</span>
|
||||
<span><%= query_params[:created_at_to].present? ? l(Date.parse(query_params[:created_at_to]), locale: current_account.locale) : t('today') %></span>
|
||||
<% end %>
|
||||
</span>
|
||||
<% end %>
|
||||
<%= link_to url_for(params.to_unsafe_h.except(:created_at_to, :created_at_from)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
|
||||
<%= svg_icon('x', class: 'w-5 h-5') %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% if params[:author].present? %>
|
||||
<div class="tooltip tooltip-bottom flex h-10 px-2 py-1 text-lg items-center justify-between border text-center text-neutral font-semibold rounded-xl w-full md:w-34 border-neutral-700" data-tip="<%= t('author') %>">
|
||||
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for, with_remove: true)), data: { turbo_frame: 'modal' }, class: 'flex items-center space-x-1 w-full pr-1 md:max-w-[140px]' do %>
|
||||
<%= svg_icon('user', class: 'w-5 h-5 shrink-0') %>
|
||||
<span class="font-normal truncate"><%= current_account.users.accessible_by(current_ability).where(account: current_account).find_by(email: params[:author])&.full_name || 'NA' %></span>
|
||||
<% end %>
|
||||
<%= link_to url_for(params.to_unsafe_h.except(:author)), class: 'rounded-lg ml-1 hover:bg-base-content hover:text-white' do %>
|
||||
<%= svg_icon('x', class: 'w-5 h-5') %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% end %>
|
||||
@ -0,0 +1,39 @@
|
||||
<% current_time = Time.current.in_time_zone(current_account.timezone) %>
|
||||
<% week_start = TimeUtils.timezone_abbr(current_account.timezone, current_time).in?(TimeUtils::US_TIMEZONES) ? :sunday : :monday %>
|
||||
<div class="flex flex-wrap gap-2 mt-4 md:mt-2">
|
||||
<set-date-button data-from-value="<%= current_time.to_date %>" data-to-value="<%= current_time.to_date %>" data-from-id="date_from" data-to-id="date_to">
|
||||
<button class="btn btn-xs btn-primary font-medium normal-case" data-turbo="false" type="button">
|
||||
<%= t('today') %>
|
||||
</button>
|
||||
</set-date-button>
|
||||
<set-date-button data-from-value="<%= (current_time - 1.day).to_date %>" data-to-value="<%= (current_time - 1.day).to_date %>" data-from-id="date_from" data-to-id="date_to">
|
||||
<button class="btn btn-xs btn-primary font-medium normal-case" data-turbo="false" type="button">
|
||||
<%= t('yesterday') %>
|
||||
</button>
|
||||
</set-date-button>
|
||||
<set-date-button data-from-value="<%= current_time.beginning_of_week(week_start).to_date %>" data-to-value="<%= current_time.end_of_week(week_start).to_date %>" data-from-id="date_from" data-to-id="date_to">
|
||||
<button class="btn btn-xs btn-primary font-medium normal-case" data-turbo="false" type="button">
|
||||
<%= t('this_week') %>
|
||||
</button>
|
||||
</set-date-button>
|
||||
<set-date-button data-from-value="<%= (current_time - 1.week).beginning_of_week(week_start).to_date %>" data-to-value="<%= (current_time - 1.week).end_of_week(week_start).to_date %>" data-from-id="date_from" data-to-id="date_to">
|
||||
<button class="btn btn-xs btn-primary font-medium normal-case" data-turbo="false" type="button">
|
||||
<%= t('last_week') %>
|
||||
</button>
|
||||
</set-date-button>
|
||||
<set-date-button data-from-value="<%= current_time.beginning_of_month.to_date %>" data-to-value="<%= current_time.end_of_month.to_date %>" data-from-id="date_from" data-to-id="date_to">
|
||||
<button class="btn btn-xs btn-primary font-medium normal-case" data-turbo="false" type="button">
|
||||
<%= t('this_month') %>
|
||||
</button>
|
||||
</set-date-button>
|
||||
<set-date-button data-from-value="<%= (current_time - 1.month).beginning_of_month.to_date %>" data-to-value="<%= (current_time - 1.month).end_of_month.to_date %>" data-from-id="date_from" data-to-id="date_to">
|
||||
<button class="btn btn-xs btn-primary font-medium normal-case" data-turbo="false" type="button">
|
||||
<%= t('last_month') %>
|
||||
</button>
|
||||
</set-date-button>
|
||||
<set-date-button data-from-value="<%= current_time.beginning_of_year.to_date %>" data-to-value="<%= current_time.end_of_year.to_date %>" data-from-id="date_from" data-to-id="date_to">
|
||||
<button class="btn btn-xs btn-primary font-medium normal-case" data-turbo="false" type="button">
|
||||
<%= t('this_year') %>
|
||||
</button>
|
||||
</set-date-button>
|
||||
</div>
|
||||
@ -0,0 +1,27 @@
|
||||
<% query_params = params.permit(:q, :status).merge(filter_params) %>
|
||||
<div class="dropdown dropdown-end">
|
||||
<label tabindex="0" class="cursor-pointer flex h-10 px-3 py-1 space-x-1 text-lg items-center justify-between border text-center text-neutral rounded-xl border-neutral-300 hover:border-neutral-700">
|
||||
<%= svg_icon('filter', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||
<span class="<%= filter_params.present? ? 'md:hidden' : '' %>">Filter</span>
|
||||
</label>
|
||||
<ul tabindex="0" class="z-10 dropdown-content p-2 mt-2 shadow menu text-base bg-base-100 rounded-box min-w-[180px] text-right">
|
||||
<li class="flex">
|
||||
<%= link_to submissions_filter_path('completed_at', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('calendar_check', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||
<span><%= t('completed_at') %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<%= link_to submissions_filter_path('created_at', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('calendar', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||
<span><%= t('created_at') %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
<li class="flex">
|
||||
<%= link_to submissions_filter_path('author', query_params.merge(path: url_for)), data: { turbo_frame: 'modal' } do %>
|
||||
<%= svg_icon('user', class: 'w-5 h-5 flex-shrink-0 stroke-2') %>
|
||||
<span><%= t('author') %></span>
|
||||
<% end %>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -0,0 +1,18 @@
|
||||
<%= render 'shared/turbo_modal', title: local_assigns[:title] do %>
|
||||
<%= form_for '', url: params[:path], method: :get, data: { turbo_frame: :_top }, html: { autocomplete: :off } do |f| %>
|
||||
<%= hidden_field_tag :status, params[:status] if params[:status].present? %>
|
||||
<%= hidden_field_tag :q, params[:q] if params[:q].present? %>
|
||||
<% local_assigns[:default_params].each do |key, value| %>
|
||||
<%= hidden_field_tag(key, value) if value.present? %>
|
||||
<% end %>
|
||||
<%= yield %>
|
||||
<div class="form-control mt-4">
|
||||
<%= f.button button_title(title: t('apply'), disabled_with: t('applying')), name: nil, class: 'base-button' %>
|
||||
</div>
|
||||
<% if params[:with_remove] %>
|
||||
<div class="text-center w-full mt-4">
|
||||
<%= link_to t('remove_filter'), "#{params[:path]}?#{params.to_unsafe_h.slice(:q, :status).merge(local_assigns[:default_params]).to_query}", class: 'link', data: { turbo_frame: :_top } %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@ -0,0 +1,7 @@
|
||||
<%= render 'filter_modal', title: t('author'), default_params: params.permit(*(Submissions::Filter::ALLOWED_PARAMS - ['author'])) do %>
|
||||
<div class="space-y-2">
|
||||
<div class="form-control mt-6">
|
||||
<%= select_tag :author, options_for_select(current_account.users.accessible_by(current_ability).where.not(role: :integration).where(account: current_account).map { |u| [u.full_name, u.email] }, params[:author].presence || current_user.email), required: true, class: 'base-select' %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
@ -0,0 +1,15 @@
|
||||
<%= render 'filter_modal', title: t('completed_at'), default_params: params.permit(*(Submissions::Filter::ALLOWED_PARAMS - %w[completed_at_to completed_at_from])) do %>
|
||||
<div class="space-y-3">
|
||||
<div class="flex flex-col md:flex-row gap-2">
|
||||
<div class="form-control w-full">
|
||||
<%= label_tag 'completed_at_from', t('from'), for: 'date_from', class: 'label text-sm' %>
|
||||
<%= date_field_tag 'completed_at_from', params[:completed_at_from], id: 'date_from', class: 'base-input !h-10', autocomplete: 'off' %>
|
||||
</div>
|
||||
<div class="form-control w-full">
|
||||
<%= label_tag 'completed_at_to', t('to'), for: 'date_to', class: 'label text-sm' %>
|
||||
<%= date_field_tag 'completed_at_to', params[:completed_at_to], id: 'date_to', class: 'base-input !h-10', autocomplete: 'off' %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render 'date_buttons' %>
|
||||
</div>
|
||||
<% end %>
|
||||
@ -0,0 +1,15 @@
|
||||
<%= render 'filter_modal', title: t('created_at'), default_params: params.permit(*(Submissions::Filter::ALLOWED_PARAMS - %w[created_at_to created_at_from])) do %>
|
||||
<div class="space-y-3">
|
||||
<div class="flex flex-col md:flex-row gap-2">
|
||||
<div class="form-control w-full">
|
||||
<%= label_tag 'created_at_from', t('from'), for: 'date_from', class: 'label text-sm' %>
|
||||
<%= date_field_tag 'created_at_from', params[:created_at_from], id: 'date_from', class: 'base-input !h-10', autocomplete: 'off' %>
|
||||
</div>
|
||||
<div class="form-control w-full">
|
||||
<%= label_tag 'created_at_to', t('to'), for: 'date_to', class: 'label text-sm' %>
|
||||
<%= date_field_tag 'created_at_to', params[:created_at_to], id: 'date_to', class: 'base-input !h-10', autocomplete: 'off' %>
|
||||
</div>
|
||||
</div>
|
||||
<%= render 'date_buttons' %>
|
||||
</div>
|
||||
<% end %>
|
||||
@ -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
|
||||
|
After Width: | Height: | Size: 129 KiB |
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
Loading…
Reference in new issue