diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb
index 7da1a7724..f083a4ce7 100644
--- a/app/controllers/reports_controller.rb
+++ b/app/controllers/reports_controller.rb
@@ -5,40 +5,70 @@ class ReportsController < ApplicationController
def index; end
def features
- data = Reports::FeatureReportService.report
+ forms = Reports::FormDocumentsService.live_form_documents
+ data = Reports::FeatureReportService.new(forms).report
render template: "reports/features", locals: { data: }
end
def questions_with_answer_type
answer_type = params.require(:answer_type)
- questions = Reports::FeatureReportService.questions_with_answer_type(answer_type)
+ forms = Reports::FormDocumentsService.live_form_documents
+ questions = Reports::FeatureReportService.new(forms).questions_with_answer_type(answer_type)
render template: "reports/questions_with_answer_type", locals: { answer_type:, questions: }
end
def questions_with_add_another_answer
- questions = Reports::FeatureReportService.live_questions_with_add_another_answer
+ forms = Reports::FormDocumentsService.live_form_documents
+ questions = Reports::FeatureReportService.new(forms).questions_with_add_another_answer
- render template: "reports/questions_with_add_another_answer", locals: { questions: }
+ if params[:format] == "csv"
+ send_data Reports::QuestionsCsvReportService.new(questions).csv,
+ type: "text/csv; charset=iso-8859-1",
+ disposition: "attachment; filename=#{csv_filename('live_questions_with_add_another_answer_report')}"
+ else
+ render template: "reports/feature_report", locals: { report: params[:action], records: questions }
+ end
end
def forms_with_routes
- forms = Reports::FeatureReportService.live_forms_with_routes
+ forms = Reports::FormDocumentsService.live_form_documents
+ forms = Reports::FeatureReportService.new(forms).forms_with_routes
- render template: "reports/forms_with_routes", locals: { forms: forms }
+ if params[:format] == "csv"
+ send_data Reports::FormsCsvReportService.new(forms).csv,
+ type: "text/csv; charset=iso-8859-1",
+ disposition: "attachment; filename=#{csv_filename('live_forms_with_routes_report')}"
+ else
+ render template: "reports/feature_report", locals: { report: params[:action], records: forms }
+ end
end
def forms_with_payments
- forms = Reports::FeatureReportService.live_forms_with_payments
+ forms = Reports::FormDocumentsService.live_form_documents
+ forms = Reports::FeatureReportService.new(forms).forms_with_payments
- render template: "reports/forms_with_payments", locals: { forms: forms }
+ if params[:format] == "csv"
+ send_data Reports::FormsCsvReportService.new(forms).csv,
+ type: "text/csv; charset=iso-8859-1",
+ disposition: "attachment; filename=#{csv_filename('live_forms_with_payments_report')}"
+ else
+ render template: "reports/feature_report", locals: { report: params[:action], records: forms }
+ end
end
def forms_with_csv_submission_enabled
- forms = Reports::FeatureReportService.live_forms_with_csv_submission_enabled
+ forms = Reports::FormDocumentsService.live_form_documents
+ forms = Reports::FeatureReportService.new(forms).forms_with_csv_submission_enabled
- render template: "reports/forms_with_csv_submission_enabled", locals: { forms: forms }
+ if params[:format] == "csv"
+ send_data Reports::FormsCsvReportService.new(forms).csv,
+ type: "text/csv; charset=iso-8859-1",
+ disposition: "attachment; filename=#{csv_filename('live_forms_with_csv_submission_enabled_report')}"
+ else
+ render template: "reports/feature_report", locals: { report: params[:action], records: forms }
+ end
end
def users
@@ -82,42 +112,27 @@ def selection_questions_with_checkboxes
def csv_downloads; end
def live_forms_csv
- send_data Reports::CsvReportsService.new.live_forms_csv,
- type: "text/csv; charset=iso-8859-1",
- disposition: "attachment; filename=#{csv_filename('live_forms_report')}"
- end
+ forms = Reports::FormDocumentsService.live_form_documents
- def live_forms_with_routes_csv
- send_data Reports::CsvReportsService.new.live_forms_with_routes_csv,
+ send_data Reports::FormsCsvReportService.new(forms).csv,
type: "text/csv; charset=iso-8859-1",
- disposition: "attachment; filename=#{csv_filename('live_forms_with_routes_report')}"
- end
-
- def live_forms_with_payments_csv
- send_data Reports::CsvReportsService.new.live_forms_with_payments_csv,
- type: "text/csv; charset=iso-8859-1",
- disposition: "attachment; filename=#{csv_filename('live_forms_with_payments_report')}"
- end
-
- def live_forms_with_csv_submission_enabled_csv
- send_data Reports::CsvReportsService.new.live_forms_with_csv_submission_enabled_csv,
- type: "text/csv; charset=iso-8859-1",
- disposition: "attachment; filename=#{csv_filename('live_forms_with_csv_submission_enabled_report')}"
+ disposition: "attachment; filename=#{csv_filename('live_forms_report')}"
end
def live_questions_csv
answer_type = params[:answer_type]
- send_data Reports::CsvReportsService.new.live_questions_csv(answer_type:),
+ forms = Reports::FormDocumentsService.live_form_documents
+ questions = if answer_type
+ Reports::FeatureReportService.new(forms).questions_with_answer_type(answer_type)
+ else
+ Reports::FeatureReportService.new(forms).questions
+ end
+
+ send_data Reports::QuestionsCsvReportService.new(questions).csv,
type: "text/csv; charset=iso-8859-1",
disposition: "attachment; filename=#{questions_csv_filename(answer_type)}"
end
- def live_questions_with_add_another_answer_csv
- send_data Reports::CsvReportsService.new.live_questions_with_add_another_answer_csv,
- type: "text/csv; charset=iso-8859-1",
- disposition: "attachment; filename=#{csv_filename('live_questions_with_add_another_answer_report')}"
- end
-
private
def check_user_has_permission
diff --git a/app/helpers/report_helper.rb b/app/helpers/report_helper.rb
new file mode 100644
index 000000000..d2ddd4abb
--- /dev/null
+++ b/app/helpers/report_helper.rb
@@ -0,0 +1,92 @@
+module ReportHelper
+ def report_table(records)
+ type = records.first.fetch("type", "form")
+
+ if type == "form"
+ with_routes = records.first.dig("metadata", "number_of_routes").present?
+
+ with_routes ? report_forms_with_routes_table(records) : report_forms_table(records)
+ elsif type == "question_page"
+ report_questions_table(records)
+ else
+ raise "type of records '#{type}' is not one of 'forms', 'question_page'"
+ end
+ end
+
+ def report_forms_table(forms)
+ {
+ head: report_forms_table_head,
+ rows: report_forms_table_rows(forms),
+ }
+ end
+
+ def report_forms_with_routes_table(forms)
+ {
+ head: report_forms_with_routes_table_head,
+ rows: report_forms_with_routes_table_rows(forms),
+ }
+ end
+
+ def report_questions_table(questions)
+ {
+ head: report_questions_table_head,
+ rows: report_questions_table_rows(questions),
+ }
+ end
+
+ def report_forms_table_head
+ [
+ I18n.t("reports.form_or_questions_list_table.headings.form_name"),
+ I18n.t("reports.form_or_questions_list_table.headings.organisation"),
+ ]
+ end
+
+ def report_forms_table_rows(forms)
+ forms.map { |form| report_forms_table_row(form) }
+ end
+
+ def report_forms_with_routes_table_head
+ [
+ *report_forms_table_head,
+ I18n.t("reports.form_or_questions_list_table.headings.number_of_routes"),
+ ]
+ end
+
+ def report_forms_with_routes_table_rows(forms)
+ forms.map { |form| report_forms_with_routes_table_row(form) }
+ end
+
+ def report_questions_table_head
+ [
+ *report_forms_table_head,
+ I18n.t("reports.form_or_questions_list_table.headings.question_text"),
+ ]
+ end
+
+ def report_questions_table_rows(questions)
+ questions.map { |question| report_questions_table_row(question) }
+ end
+
+private
+
+ def report_forms_table_row(form)
+ [
+ govuk_link_to(form["content"]["name"], live_form_pages_path(form_id: form["form_id"])),
+ form.dig("group", "organisation", "name") || "",
+ ]
+ end
+
+ def report_forms_with_routes_table_row(form)
+ [
+ *report_forms_table_row(form),
+ form["metadata"]["number_of_routes"].to_s,
+ ]
+ end
+
+ def report_questions_table_row(question)
+ [
+ *report_forms_table_row(question["form"]),
+ question["data"]["question_text"],
+ ]
+ end
+end
diff --git a/app/services/reports/csv_reports_service.rb b/app/services/reports/csv_reports_service.rb
deleted file mode 100644
index d1afbad54..000000000
--- a/app/services/reports/csv_reports_service.rb
+++ /dev/null
@@ -1,183 +0,0 @@
-require "csv"
-
-class Reports::CsvReportsService
- FORM_CSV_HEADERS = [
- "Form ID",
- "Status",
- "Form name",
- "Slug",
- "Organisation name",
- "Organisation ID",
- "Group name",
- "Group ID",
- "Created at",
- "Updated at",
- "Number of questions",
- "Has routes",
- "Payment URL",
- "Support URL",
- "Support URL text",
- "Support email",
- "Support phone",
- "Privacy policy URL",
- "What happens next markdown",
- "Submission type",
- ].freeze
-
- IS_REPEATABLE = "Is repeatable?".freeze
- QUESTIONS_CSV_HEADERS = [
- "Form ID",
- "Status",
- "Form name",
- "Organisation name",
- "Organisation ID",
- "Group name",
- "Group ID",
- "Question number in form",
- "Question text",
- "Answer type",
- "Hint text",
- "Page heading",
- "Guidance markdown",
- "Is optional?",
- IS_REPEATABLE,
- "Has routes?",
- "Answer settings - Input type",
- "Selection settings - Only one option?",
- "Selection settings - Number of options",
- "Name settings - Title needed?",
- "Raw answer settings",
- ].freeze
-
- IS_REPEATABLE_COLUMN_INDEX = QUESTIONS_CSV_HEADERS.find_index(IS_REPEATABLE)
-
- def live_forms_csv
- CSV.generate do |csv|
- csv << FORM_CSV_HEADERS
-
- Reports::FormDocumentsService.live_form_documents.each do |form_document|
- csv << form_row(form_document)
- end
- end
- end
-
- def live_forms_with_routes_csv
- CSV.generate do |csv|
- csv << FORM_CSV_HEADERS
-
- Reports::FormDocumentsService.live_form_documents.each do |form_document|
- csv << form_row(form_document) if Reports::FormDocumentsService.has_routes?(form_document)
- end
- end
- end
-
- def live_forms_with_payments_csv
- CSV.generate do |csv|
- csv << FORM_CSV_HEADERS
-
- Reports::FormDocumentsService.live_form_documents.each do |form_document|
- csv << form_row(form_document) if Reports::FormDocumentsService.has_payments?(form_document)
- end
- end
- end
-
- def live_forms_with_csv_submission_enabled_csv
- CSV.generate do |csv|
- csv << FORM_CSV_HEADERS
-
- Reports::FormDocumentsService.live_form_documents.each do |form_document|
- csv << form_row(form_document) if Reports::FormDocumentsService.has_csv_submission_enabled?(form_document)
- end
- end
- end
-
- def live_questions_csv(answer_type: nil)
- CSV.generate do |csv|
- csv << QUESTIONS_CSV_HEADERS
-
- Reports::FormDocumentsService.live_form_documents.each do |form_document|
- question_rows = question_rows(form_document, answer_type).compact
-
- question_rows.each do |question|
- csv << question
- end
- end
- end
- end
-
- def live_questions_with_add_another_answer_csv
- CSV.generate do |csv|
- csv << QUESTIONS_CSV_HEADERS
-
- Reports::FormDocumentsService.live_form_documents.each do |form_document|
- question_rows = question_rows(form_document, nil).compact
-
- question_rows.each do |question|
- csv << question if question[IS_REPEATABLE_COLUMN_INDEX]
- end
- end
- end
- end
-
-private
-
- def form_row(form)
- form_id = form["form_id"]
- group = GroupForm.find_by_form_id(form_id)&.group
- [
- form_id,
- form["tag"],
- form["content"]["name"],
- form["content"]["form_slug"],
- group&.organisation&.name,
- group&.organisation&.id,
- group&.name,
- group&.external_id,
- form["content"]["created_at"],
- form["content"]["updated_at"],
- form["content"]["steps"].length,
- form["content"]["steps"].any? { |step| step["routing_conditions"].present? },
- form["content"]["payment_url"],
- form["content"]["support_url"],
- form["content"]["support_url_text"],
- form["content"]["support_email"],
- form["content"]["support_phone"],
- form["content"]["privacy_policy_url"],
- form["content"]["what_happens_next_markdown"],
- form["content"]["submission_type"],
- ]
- end
-
- def question_rows(form, answer_type)
- form_id = form["form_id"]
- group = GroupForm.find_by_form_id(form_id)&.group
-
- form["content"]["steps"].each_with_index.map do |step, index|
- next if answer_type.present? && step["data"]["answer_type"] != answer_type
-
- [
- form_id,
- form["tag"],
- form["content"]["name"],
- group&.organisation&.name,
- group&.organisation&.id,
- group&.name,
- group&.external_id,
- index + 1,
- step["data"]["question_text"],
- step["data"]["answer_type"],
- step["data"]["hint_text"],
- step["data"]["page_heading"],
- step["data"]["guidance_markdown"],
- step["data"]["is_optional"],
- step["data"]["is_repeatable"],
- step["routing_conditions"].present?,
- step.dig("data", "answer_settings", "input_type"),
- step.dig("data", "answer_settings", "only_one_option").presence.try { |o| o.to_s == "true" },
- step.dig("data", "answer_settings", "selection_options")&.length,
- step.dig("data", "answer_settings", "title_needed"),
- step["data"]["answer_settings"].as_json,
- ]
- end
- end
-end
diff --git a/app/services/reports/feature_report_service.rb b/app/services/reports/feature_report_service.rb
index c1479f21d..f8ebbb1b1 100644
--- a/app/services/reports/feature_report_service.rb
+++ b/app/services/reports/feature_report_service.rb
@@ -1,100 +1,111 @@
class Reports::FeatureReportService
- class << self
- def report
- report = {
- total_live_forms: 0,
- live_forms_with_payment: 0,
- live_forms_with_routing: 0,
- live_forms_with_add_another_answer: 0,
- live_forms_with_csv_submission_enabled: 0,
- live_forms_with_answer_type: HashWithIndifferentAccess.new,
- live_steps_with_answer_type: HashWithIndifferentAccess.new,
- }
-
- Reports::FormDocumentsService.live_form_documents.each do |form|
- report[:total_live_forms] += 1
- report[:live_forms_with_payment] += 1 if Reports::FormDocumentsService.has_payments?(form)
- report[:live_forms_with_routing] += 1 if Reports::FormDocumentsService.has_routes?(form)
- report[:live_forms_with_add_another_answer] += 1 if form["content"]["steps"].any? { |step| step["data"]["is_repeatable"] }
- report[:live_forms_with_csv_submission_enabled] += 1 if Reports::FormDocumentsService.has_csv_submission_enabled?(form)
-
- answer_types_in_form = form["content"]["steps"].map { |step| step["data"]["answer_type"] }
-
- answer_types_in_form.uniq.each do |answer_type|
- report[:live_forms_with_answer_type][answer_type] ||= 0
- report[:live_forms_with_answer_type][answer_type] += 1
- end
-
- answer_types_in_form.each do |answer_type|
- report[:live_steps_with_answer_type][answer_type] ||= 0
- report[:live_steps_with_answer_type][answer_type] += 1
- end
- end
+ attr_reader :form_documents
- report
- end
+ def initialize(form_documents)
+ @form_documents = form_documents
+ end
+
+ def report
+ report = {
+ total_forms: 0,
+ forms_with_payment: 0,
+ forms_with_routing: 0,
+ forms_with_add_another_answer: 0,
+ forms_with_csv_submission_enabled: 0,
+ forms_with_answer_type: HashWithIndifferentAccess.new,
+ steps_with_answer_type: HashWithIndifferentAccess.new,
+ }
- def questions_with_answer_type(answer_type)
- Reports::FormDocumentsService.live_form_documents.flat_map do |form|
- form["content"]["steps"].select { |step| step["data"]["answer_type"] == answer_type }
- .map { |step| questions_details(form, step) }
+ form_documents.each do |form|
+ report[:total_forms] += 1
+ report[:forms_with_payment] += 1 if Reports::FormDocumentsService.has_payments?(form)
+ report[:forms_with_routing] += 1 if Reports::FormDocumentsService.has_routes?(form)
+ report[:forms_with_add_another_answer] += 1 if form["content"]["steps"].any? { |step| step["data"]["is_repeatable"] }
+ report[:forms_with_csv_submission_enabled] += 1 if Reports::FormDocumentsService.has_csv_submission_enabled?(form)
+
+ answer_types_in_form = form["content"]["steps"].map { |step| step["data"]["answer_type"] }
+
+ answer_types_in_form.uniq.each do |answer_type|
+ report[:forms_with_answer_type][answer_type] ||= 0
+ report[:forms_with_answer_type][answer_type] += 1
end
- end
- def live_questions_with_add_another_answer
- Reports::FormDocumentsService.live_form_documents.flat_map do |form|
- form["content"]["steps"].select { |step| step["data"]["is_repeatable"] }
- .map { |step| questions_details(form, step) }
+ answer_types_in_form.each do |answer_type|
+ report[:steps_with_answer_type][answer_type] ||= 0
+ report[:steps_with_answer_type][answer_type] += 1
end
end
- def live_forms_with_routes
- Reports::FormDocumentsService.live_form_documents
- .select { |form| Reports::FormDocumentsService.has_routes?(form) }
- .map { |form| form_with_routes_details(form) }
+ report
+ end
+
+ def questions
+ form_documents.flat_map do |form|
+ form["content"]["steps"]
+ .map { |step| questions_details(form, step) }
end
+ end
- def live_forms_with_payments
- Reports::FormDocumentsService.live_form_documents
- .select { |form| Reports::FormDocumentsService.has_payments?(form) }
- .map { |form| form_details(form) }
+ def questions_with_answer_type(answer_type)
+ form_documents.flat_map do |form|
+ form["content"]["steps"]
+ .select { |step| step["data"]["answer_type"] == answer_type }
+ .map { |step| questions_details(form, step) }
end
+ end
- def live_forms_with_csv_submission_enabled
- Reports::FormDocumentsService.live_form_documents
- .select { |form| Reports::FormDocumentsService.has_csv_submission_enabled?(form) }
- .map { |form| form_details(form) }
+ def questions_with_add_another_answer
+ form_documents.flat_map do |form|
+ form["content"]["steps"]
+ .select { |step| step["data"]["is_repeatable"] }
+ .map { |step| questions_details(form, step) }
end
+ end
- private
+ def forms_with_routes
+ form_documents
+ .select { |form| Reports::FormDocumentsService.has_routes?(form) }
+ .map { |form| form_with_routes_details(form) }
+ end
- def questions_details(form, step)
- form_id = form["form_id"]
- {
- form_name: form["content"]["name"],
- form_id: form_id,
- organisation_name: organisation_name(form_id),
- question_text: step["data"]["question_text"],
- }
- end
+ def forms_with_payments
+ form_documents
+ .select { |form| Reports::FormDocumentsService.has_payments?(form) }
+ .map { |form| form_details(form) }
+ end
- def form_with_routes_details(form)
- form_details = form_details(form)
- form_details[:number_of_routes] = form["content"]["steps"].count { |step| step["routing_conditions"].present? }
- form_details
- end
+ def forms_with_csv_submission_enabled
+ form_documents
+ .select { |form| Reports::FormDocumentsService.has_csv_submission_enabled?(form) }
+ .map { |form| form_details(form) }
+ end
- def form_details(form)
- form_id = form["form_id"]
- {
- form_name: form["content"]["name"],
- form_id: form_id,
- organisation_name: organisation_name(form_id),
- }
- end
+private
- def organisation_name(form_id)
- GroupForm.find_by_form_id(form_id)&.group&.organisation&.name
- end
+ def questions_details(form, step)
+ form = form_details(form)
+ step.dup.merge("form" => form)
+ end
+
+ def form_with_routes_details(form)
+ form = form_details(form)
+ form["metadata"] = {
+ "number_of_routes" => form["content"]["steps"].count { |step| step["routing_conditions"].present? },
+ }
+ form
+ end
+
+ def form_details(form)
+ form = form.dup
+ form["group"] = {
+ "organisation" => {
+ "name" => organisation_name(form["form_id"]),
+ },
+ }
+ form
+ end
+
+ def organisation_name(form_id)
+ GroupForm.find_by_form_id(form_id)&.group&.organisation&.name
end
end
diff --git a/app/services/reports/forms_csv_report_service.rb b/app/services/reports/forms_csv_report_service.rb
new file mode 100644
index 000000000..ec75c74b7
--- /dev/null
+++ b/app/services/reports/forms_csv_report_service.rb
@@ -0,0 +1,71 @@
+require "csv"
+
+class Reports::FormsCsvReportService
+ FORM_CSV_HEADERS = [
+ "Form ID",
+ "Status",
+ "Form name",
+ "Slug",
+ "Organisation name",
+ "Organisation ID",
+ "Group name",
+ "Group ID",
+ "Created at",
+ "Updated at",
+ "Number of questions",
+ "Has routes",
+ "Payment URL",
+ "Support URL",
+ "Support URL text",
+ "Support email",
+ "Support phone",
+ "Privacy policy URL",
+ "What happens next markdown",
+ "Submission type",
+ ].freeze
+
+ attr_reader :form_documents
+
+ def initialize(form_documents)
+ @form_documents = form_documents
+ end
+
+ def csv
+ CSV.generate do |csv|
+ csv << FORM_CSV_HEADERS
+
+ form_documents.each do |form_document|
+ csv << form_row(form_document)
+ end
+ end
+ end
+
+private
+
+ def form_row(form)
+ form_id = form["form_id"]
+ group = GroupForm.find_by_form_id(form_id)&.group
+ [
+ form_id,
+ form["tag"],
+ form["content"]["name"],
+ form["content"]["form_slug"],
+ group&.organisation&.name,
+ group&.organisation&.id,
+ group&.name,
+ group&.external_id,
+ form["content"]["created_at"],
+ form["content"]["updated_at"],
+ form["content"]["steps"].length,
+ form["content"]["steps"].any? { |step| step["routing_conditions"].present? },
+ form["content"]["payment_url"],
+ form["content"]["support_url"],
+ form["content"]["support_url_text"],
+ form["content"]["support_email"],
+ form["content"]["support_phone"],
+ form["content"]["privacy_policy_url"],
+ form["content"]["what_happens_next_markdown"],
+ form["content"]["submission_type"],
+ ]
+ end
+end
diff --git a/app/services/reports/questions_csv_report_service.rb b/app/services/reports/questions_csv_report_service.rb
new file mode 100644
index 000000000..c1e64e609
--- /dev/null
+++ b/app/services/reports/questions_csv_report_service.rb
@@ -0,0 +1,78 @@
+require "csv"
+
+class Reports::QuestionsCsvReportService
+ IS_REPEATABLE = "Is repeatable?".freeze
+ QUESTIONS_CSV_HEADERS = [
+ "Form ID",
+ "Status",
+ "Form name",
+ "Organisation name",
+ "Organisation ID",
+ "Group name",
+ "Group ID",
+ "Question number in form",
+ "Question text",
+ "Answer type",
+ "Hint text",
+ "Page heading",
+ "Guidance markdown",
+ "Is optional?",
+ IS_REPEATABLE,
+ "Has routes?",
+ "Answer settings - Input type",
+ "Selection settings - Only one option?",
+ "Selection settings - Number of options",
+ "Name settings - Title needed?",
+ "Raw answer settings",
+ ].freeze
+
+ IS_REPEATABLE_COLUMN_INDEX = QUESTIONS_CSV_HEADERS.find_index(IS_REPEATABLE)
+
+ attr_reader :question_page_documents
+
+ def initialize(question_page_documents)
+ @question_page_documents = question_page_documents
+ end
+
+ def csv
+ CSV.generate do |csv|
+ csv << QUESTIONS_CSV_HEADERS
+
+ question_page_documents.each do |question_page_document|
+ csv << question_row(question_page_document)
+ end
+ end
+ end
+
+private
+
+ def question_row(step)
+ form = step["form"]
+ form_id = form["form_id"]
+ group = GroupForm.find_by_form_id(form_id)&.group
+
+ [
+ form_id,
+ form["tag"],
+ form["content"]["name"],
+ group&.organisation&.name,
+ group&.organisation&.id,
+ group&.name,
+ group&.external_id,
+ step["position"],
+ step["data"]["question_text"],
+ step["data"]["answer_type"],
+ step["data"]["hint_text"],
+ step["data"]["page_heading"],
+ step["data"]["guidance_markdown"],
+ step["data"]["is_optional"],
+ step["data"]["is_repeatable"],
+ step["routing_conditions"].present?,
+ step.dig("data", "answer_settings", "input_type"),
+ step.dig("data", "answer_settings", "only_one_option").presence.try { |o| o.to_s == "true" },
+ step.dig("data", "answer_settings", "selection_options")&.length,
+ step.dig("data", "answer_settings", "title_needed"),
+ step["data"]["answer_settings"].as_json,
+ ]
+ end
+end
diff --git a/app/views/reports/feature_report.html.erb b/app/views/reports/feature_report.html.erb
new file mode 100644
index 000000000..f71186f24
--- /dev/null
+++ b/app/views/reports/feature_report.html.erb
@@ -0,0 +1,13 @@
+<% set_page_title(t("reports.#{report}.heading"))%>
+<% content_for :back_link, govuk_back_link_to(report_features_path, t("reports.back_to_feature_usage")) %>
+
+
+
<%= t("reports.#{report}.heading")%>
+
+
<%=govuk_link_to(t("reports.#{report}.download_csv"), url_for(format: :csv))%>
+
+
+
+ <%= govuk_table(**report_table(records)) %>
+
+
diff --git a/app/views/reports/features.html.erb b/app/views/reports/features.html.erb
index a2ee9f902..725646a7c 100644
--- a/app/views/reports/features.html.erb
+++ b/app/views/reports/features.html.erb
@@ -8,23 +8,23 @@
<%= govuk_summary_list do |summary_list| %>
<%= summary_list.with_row do |row| %>
<%= row.with_key(text: t(".features.total_live_forms")) %>
- <%= row.with_value(text: data[:total_live_forms]) %>
+ <%= row.with_value(text: data[:total_forms]) %>
<% end %>
<%= summary_list.with_row do |row| %>
<%= row.with_key(text: t(".features.live_forms_with_routes")) %>
- <%= row.with_value(text: govuk_link_to(data[:live_forms_with_routing], report_forms_with_routes_path, no_visited_state: true)) %>
+ <%= row.with_value(text: govuk_link_to(data[:forms_with_routing], report_forms_with_routes_path, no_visited_state: true)) %>
<% end %>
<%= summary_list.with_row do |row| %>
<%= row.with_key(text: t(".features.live_forms_with_payments")) %>
- <%= row.with_value(text: govuk_link_to(data[:live_forms_with_payment], report_forms_with_payments_path, no_visited_state: true)) %>
+ <%= row.with_value(text: govuk_link_to(data[:forms_with_payment], report_forms_with_payments_path, no_visited_state: true)) %>
<% end %>
<%= summary_list.with_row do |row| %>
<%= row.with_key(text: t(".features.live_forms_with_add_another_answer")) %>
- <%= row.with_value(text: govuk_link_to(data[:live_forms_with_add_another_answer], report_questions_with_add_another_answer_path, no_visited_state: true)) %>
+ <%= row.with_value(text: govuk_link_to(data[:forms_with_add_another_answer], report_questions_with_add_another_answer_path, no_visited_state: true)) %>
<% end %>
<%= summary_list.with_row do |row| %>
<%= row.with_key(text: t(".features.live_forms_with_csv_submission_enabled")) %>
- <%= row.with_value(text: govuk_link_to(data[:live_forms_with_csv_submission_enabled], report_forms_with_csv_submission_enabled_path, no_visited_state: true)) %>
+ <%= row.with_value(text: govuk_link_to(data[:forms_with_csv_submission_enabled], report_forms_with_csv_submission_enabled_path, no_visited_state: true)) %>
<% end %>
<% end %>
@@ -46,11 +46,11 @@
<%= body.with_row do |row| %>
<%= row.with_cell(header: true, text: t("helpers.label.page.answer_type_options.names.#{answer_type}")) %>
<% if answer_type_links[answer_type].present? %>
- <%= row.with_cell(text: govuk_link_to(data[:live_forms_with_answer_type][answer_type] || 0, answer_type_links[answer_type], no_visited_state: true), numeric: true, html_attributes: { data: { "live-forms-with-answer-type-#{answer_type.to_s.dasherize}": true } }) %>
+ <%= row.with_cell(text: govuk_link_to(data[:forms_with_answer_type][answer_type] || 0, answer_type_links[answer_type], no_visited_state: true), numeric: true, html_attributes: { data: { "live-forms-with-answer-type-#{answer_type.to_s.dasherize}": true } }) %>
<% else %>
- <%= row.with_cell(text: data[:live_forms_with_answer_type][answer_type] || 0, numeric: true, html_attributes: { data: { "live-forms-with-answer-type-#{answer_type.to_s.dasherize}": true } }) %>
+ <%= row.with_cell(text: data[:forms_with_answer_type][answer_type] || 0, numeric: true, html_attributes: { data: { "live-forms-with-answer-type-#{answer_type.to_s.dasherize}": true } }) %>
<% end %>
- <%= row.with_cell(text: govuk_link_to(data[:live_steps_with_answer_type][answer_type] || 0, report_questions_with_answer_type_path(answer_type:), no_visited_state: true), numeric: true, html_attributes: { data: { "live-pages-with-answer-type-#{answer_type.to_s.dasherize}": true } }) %>
+ <%= row.with_cell(text: govuk_link_to(data[:steps_with_answer_type][answer_type] || 0, report_questions_with_answer_type_path(answer_type:), no_visited_state: true), numeric: true, html_attributes: { data: { "live-pages-with-answer-type-#{answer_type.to_s.dasherize}": true } }) %>
<% end %>
<% end %>
<% end %>
diff --git a/app/views/reports/forms_with_csv_submission_enabled.html.erb b/app/views/reports/forms_with_csv_submission_enabled.html.erb
deleted file mode 100644
index c1d5563fa..000000000
--- a/app/views/reports/forms_with_csv_submission_enabled.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-<% set_page_title(t(".heading"))%>
-<% content_for :back_link, govuk_back_link_to(report_features_path, t("reports.back_to_feature_usage")) %>
-
-
-
<%= t(".heading")%>
-
-
<%=govuk_link_to(t(".download_csv"), report_live_forms_with_csv_submission_enabled_csv_path)%>
-
-
-
- <%= govuk_table do |table| %>
-
- <%= table.with_head do |head| %>
- <%= head.with_row do |row| %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.form_name")) %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.organisation")) %>
- <% end %>
- <% end %>
- <%= table.with_body do |body| %>
- <% forms.each do |form| %>
- <%= body.with_row do |row| %>
- <%= row.with_cell(text: govuk_link_to(form[:form_name], live_form_path(form_id: form[:form_id]))) %>
- <%= row.with_cell(text: form[:organisation_name]) %>
- <% end %>
- <% end %>
- <% end %>
- <% end %>
-
-
diff --git a/app/views/reports/forms_with_payments.html.erb b/app/views/reports/forms_with_payments.html.erb
deleted file mode 100644
index 229ea4b2f..000000000
--- a/app/views/reports/forms_with_payments.html.erb
+++ /dev/null
@@ -1,29 +0,0 @@
-<% set_page_title(t(".heading"))%>
-<% content_for :back_link, govuk_back_link_to(report_features_path, t("reports.back_to_feature_usage")) %>
-
-
-
<%= t(".heading")%>
-
-
<%=govuk_link_to(t(".download_csv"), report_live_forms_with_payments_csv_path)%>
-
-
-
- <%= govuk_table do |table| %>
-
- <%= table.with_head do |head| %>
- <%= head.with_row do |row| %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.form_name")) %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.organisation")) %>
- <% end %>
- <% end %>
- <%= table.with_body do |body| %>
- <% forms.each do |form| %>
- <%= body.with_row do |row| %>
- <%= row.with_cell(text: govuk_link_to(form[:form_name], live_form_path(form_id: form[:form_id]))) %>
- <%= row.with_cell(text: form[:organisation_name]) %>
- <% end %>
- <% end %>
- <% end %>
- <% end %>
-
-
diff --git a/app/views/reports/forms_with_routes.html.erb b/app/views/reports/forms_with_routes.html.erb
deleted file mode 100644
index 35fd9dfa0..000000000
--- a/app/views/reports/forms_with_routes.html.erb
+++ /dev/null
@@ -1,31 +0,0 @@
-<% set_page_title(t(".heading"))%>
-<% content_for :back_link, govuk_back_link_to(report_features_path, t("reports.back_to_feature_usage")) %>
-
-
-
<%= t(".heading")%>
-
-
<%=govuk_link_to(t(".download_csv"), report_live_forms_with_routes_csv_path)%>
-
-
-
- <%= govuk_table do |table| %>
-
- <%= table.with_head do |head| %>
- <%= head.with_row do |row| %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.form_name")) %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.organisation")) %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.number_of_routes")) %>
- <% end %>
- <% end %>
- <%= table.with_body do |body| %>
- <% forms.each do |form| %>
- <%= body.with_row do |row| %>
- <%= row.with_cell(text: govuk_link_to(form[:form_name], live_form_pages_path(form_id: form[:form_id]))) %>
- <%= row.with_cell(text: form[:organisation_name]) %>
- <%= row.with_cell(text: form[:number_of_routes]) %>
- <% end %>
- <% end %>
- <% end %>
- <% end %>
-
-
diff --git a/app/views/reports/questions_with_add_another_answer.html.erb b/app/views/reports/questions_with_add_another_answer.html.erb
deleted file mode 100644
index 1eb305217..000000000
--- a/app/views/reports/questions_with_add_another_answer.html.erb
+++ /dev/null
@@ -1,31 +0,0 @@
-<% set_page_title(t(".heading"))%>
-<% content_for :back_link, govuk_back_link_to(report_features_path, t("reports.back_to_feature_usage")) %>
-
-
-
<%= t(".heading")%>
-
-
<%=govuk_link_to(t(".download_csv"), report_live_questions_with_add_another_answer_csv_path)%>
-
-
-
- <%= govuk_table do |table| %>
-
- <%= table.with_head do |head| %>
- <%= head.with_row do |row| %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.form_name")) %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.organisation")) %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.question_text")) %>
- <% end %>
- <% end %>
- <%= table.with_body do |body| %>
- <% questions.each do |question| %>
- <%= body.with_row do |row| %>
- <%= row.with_cell(text: govuk_link_to(question[:form_name], live_form_pages_path(form_id: question[:form_id]))) %>
- <%= row.with_cell(text: question[:organisation_name]) %>
- <%= row.with_cell(text: question[:question_text]) %>
- <% end %>
- <% end %>
- <% end %>
- <% end %>
-
-
diff --git a/app/views/reports/questions_with_answer_type.html.erb b/app/views/reports/questions_with_answer_type.html.erb
index 35e38ab02..caf3f82ce 100644
--- a/app/views/reports/questions_with_answer_type.html.erb
+++ b/app/views/reports/questions_with_answer_type.html.erb
@@ -8,24 +8,9 @@
- <%= govuk_table do |table| %>
-
- <%= table.with_head do |head| %>
- <%= head.with_row do |row| %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.form_name")) %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.organisation")) %>
- <%= row.with_cell(text: t("reports.form_or_questions_list_table.headings.question_text")) %>
- <% end %>
- <% end %>
- <%= table.with_body do |body| %>
- <% questions.each do |question| %>
- <%= body.with_row do |row| %>
- <%= row.with_cell(text: govuk_link_to(question[:form_name], live_form_pages_path(form_id: question[:form_id]))) %>
- <%= row.with_cell(text: question[:organisation_name]) %>
- <%= row.with_cell(text: question[:question_text]) %>
- <% end %>
- <% end %>
- <% end %>
- <% end %>
+ <%= govuk_table(
+ head: report_questions_table_head,
+ rows: report_questions_table_rows(questions),
+ ) %>
diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml
index 6ba0ecc64..6e04a5680 100644
--- a/config/i18n-tasks.yml
+++ b/config/i18n-tasks.yml
@@ -132,6 +132,8 @@ ignore_unused:
- 'errors.*'
- 'date.formats.*'
- 'helpers.*'
+- 'reports.*.heading'
+- 'reports.*.download_csv'
- 'routing_page.{branch_routing,exit_pages}.*'
## Exclude these keys from the `i18n-tasks eq-base' report:
diff --git a/config/routes.rb b/config/routes.rb
index 9463f85b4..f5733054a 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -192,14 +192,18 @@
resources :memberships, only: %i[destroy update]
- scope :reports do
+ scope "/reports" do
get "/", to: "reports#index", as: :reports
- get "features", to: "reports#features", as: :report_features
- get "questions-with-answer-type/:answer_type", to: "reports#questions_with_answer_type", as: :report_questions_with_answer_type
- get "questions-with-add-another-answer", to: "reports#questions_with_add_another_answer", as: :report_questions_with_add_another_answer
- get "forms-with-routes", to: "reports#forms_with_routes", as: :report_forms_with_routes
- get "forms-with-payments", to: "reports#forms_with_payments", as: :report_forms_with_payments
- get "forms-with-csv-submission-enabled", to: "reports#forms_with_csv_submission_enabled", as: :report_forms_with_csv_submission_enabled
+
+ scope "/features" do
+ get "/", to: "reports#features", as: :report_features
+ get "questions-with-answer-type/:answer_type", to: "reports#questions_with_answer_type", as: :report_questions_with_answer_type
+ get "questions-with-add-another-answer", to: "reports#questions_with_add_another_answer", as: :report_questions_with_add_another_answer
+ get "forms-with-routes", to: "reports#forms_with_routes", as: :report_forms_with_routes
+ get "forms-with-payments", to: "reports#forms_with_payments", as: :report_forms_with_payments
+ get "forms-with-csv-submission-enabled", to: "reports#forms_with_csv_submission_enabled", as: :report_forms_with_csv_submission_enabled
+ end
+
get "users", to: "reports#users", as: :report_users
get "add_another_answer", to: "reports#add_another_answer", as: :report_add_another_answer
get "last-signed-in-at", to: "reports#last_signed_in_at", as: :report_last_signed_in_at
@@ -209,11 +213,7 @@
get "selection-questions-with-checkboxes", to: "reports#selection_questions_with_checkboxes", as: :report_selection_questions_with_checkboxes
get "csv-downloads", to: "reports#csv_downloads", as: :report_csv_downloads
get "live-forms-csv", to: "reports#live_forms_csv", as: :report_live_forms_csv
- get "live-forms-with-routes-csv", to: "reports#live_forms_with_routes_csv", as: :report_live_forms_with_routes_csv
- get "live-forms-with-payments-csv", to: "reports#live_forms_with_payments_csv", as: :report_live_forms_with_payments_csv
- get "live-forms-with-csv-submission-enabled-csv", to: "reports#live_forms_with_csv_submission_enabled_csv", as: :report_live_forms_with_csv_submission_enabled_csv
get "live-questions-csv", to: "reports#live_questions_csv", as: :report_live_questions_csv
- get "live-questions-with-add-another-answer-csv", to: "reports#live_questions_with_add_another_answer_csv", as: :report_live_questions_with_add_another_answer_csv
end
get "/maintenance" => "errors#maintenance", as: :maintenance_page
diff --git a/spec/fixtures/files/form_documents_response.json b/spec/fixtures/files/form_documents_response.json
index 80a585bd7..d05c906c3 100644
--- a/spec/fixtures/files/form_documents_response.json
+++ b/spec/fixtures/files/form_documents_response.json
@@ -425,5 +425,101 @@
},
"created_at": "2025-01-02T16:24:31.445Z",
"updated_at": "2025-01-02T16:24:31.445Z"
+ },
+ {
+ "id": 4,
+ "form_id": 4,
+ "tag": "live",
+ "content": {
+ "name": "Skip route form",
+ "steps": [
+ {
+ "id": 17,
+ "data": {
+ "hint_text": null,
+ "answer_type": "selection",
+ "is_optional": false,
+ "page_heading": null,
+ "is_repeatable": false,
+ "question_text": "Would you like to submit anonymously?",
+ "answer_settings": {
+ "only_one_option": "true",
+ "selection_options": [
+ {
+ "name": "Yes"
+ },
+ {
+ "name": "No"
+ }
+ ]
+ },
+ "guidance_markdown": null
+ },
+ "type": "question_page",
+ "position": 1,
+ "next_step_id": 18,
+ "routing_conditions": [
+ {
+ "id": 4,
+ "created_at": "2025-05-07T10:28:31.149Z",
+ "updated_at": "2025-05-07T10:28:31.149Z",
+ "skip_to_end": true,
+ "answer_value": "Yes",
+ "goto_page_id": null,
+ "check_page_id": 17,
+ "routing_page_id": 17,
+ "exit_page_heading": null,
+ "validation_errors": [],
+ "exit_page_markdown": null
+ }
+ ]
+ },
+ {
+ "id": 18,
+ "data": {
+ "hint_text": null,
+ "answer_type": "name",
+ "is_optional": false,
+ "page_heading": null,
+ "is_repeatable": false,
+ "question_text": "What’s your name?",
+ "answer_settings": {
+ "input_type": "full_name",
+ "title_needed": false
+ },
+ "guidance_markdown": null
+ },
+ "type": "question_page",
+ "position": 2,
+ "next_step_id": null,
+ "routing_conditions": []
+ }
+ ],
+ "form_id": "4",
+ "live_at": "2025-05-07T10:28:31.206Z",
+ "form_slug": "skip-route-form",
+ "created_at": "2025-05-07T10:28:31.128Z",
+ "creator_id": null,
+ "start_page": 17,
+ "updated_at": "2025-05-07T10:28:31.206Z",
+ "payment_url": null,
+ "support_url": null,
+ "support_email": "your.email+fakedata84701@gmail.com.gov.uk",
+ "support_phone": null,
+ "s3_bucket_name": null,
+ "submission_type": "email",
+ "declaration_text": "",
+ "s3_bucket_region": null,
+ "submission_email": "",
+ "support_url_text": null,
+ "privacy_policy_url": "https://www.gov.uk/help/privacy-notice",
+ "share_preview_completed": true,
+ "s3_bucket_aws_account_id": null,
+ "question_section_completed": true,
+ "what_happens_next_markdown": "This is a test form",
+ "declaration_section_completed": true
+ },
+ "created_at": "2025-05-07T10:28:31.274Z",
+ "updated_at": "2025-05-07T10:28:31.274Z"
}
]
diff --git a/spec/helpers/report_helpers_spec.rb b/spec/helpers/report_helpers_spec.rb
new file mode 100644
index 000000000..e5ddc041a
--- /dev/null
+++ b/spec/helpers/report_helpers_spec.rb
@@ -0,0 +1,274 @@
+require "rails_helper"
+
+RSpec.describe ReportHelper, type: :helper do
+ let(:forms) do
+ [
+ { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } } },
+ { "form_id" => 3, "content" => { "name" => "Branch route form" }, "group" => { "organisation" => { "name" => "Ministry of Tests" } } },
+ { "form_id" => 4, "content" => { "name" => "Skip route form" }, "group" => { "organisation" => { "name" => "Department for Testing" } } },
+ ]
+ end
+
+ let(:forms_with_routes) do
+ [
+ { "form_id" => 3, "content" => { "name" => "Branch route form" }, "group" => { "organisation" => { "name" => "Ministry of Tests" } }, "metadata" => { "number_of_routes" => 2 } },
+ { "form_id" => 4, "content" => { "name" => "Skip route form" }, "group" => { "organisation" => { "name" => "Department for Testing" } }, "metadata" => { "number_of_routes" => 1 } },
+ ]
+ end
+
+ let(:questions) do
+ [
+ { "type" => "question_page", "data" => { "question_text" => "Email address" }, "form" => { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } } } },
+ { "type" => "question_page", "data" => { "question_text" => "What’s your email address?" }, "form" => { "form_id" => 3, "content" => { "name" => "Branch route form" }, "group" => { "organisation" => { "name" => "Ministry of Tests" } } } },
+ ]
+ end
+
+ describe "#report_table" do
+ before do
+ allow(helper).to receive(:report_forms_table).and_call_original
+ allow(helper).to receive(:report_forms_with_routes_table).and_call_original
+ allow(helper).to receive(:report_questions_table).and_call_original
+ end
+
+ context "with list of forms" do
+ it "calls #report_forms_table" do
+ helper.report_table(forms)
+ expect(helper).to have_received(:report_forms_table).with(forms)
+ end
+ end
+
+ context "with list of forms with routes" do
+ it "calls #report_forms_with_routes_table" do
+ helper.report_table(forms_with_routes)
+ expect(helper).to have_received(:report_forms_with_routes_table).with(forms_with_routes)
+ end
+ end
+
+ context "with list of questions" do
+ it "calls #report_questions_table" do
+ helper.report_table(questions)
+ expect(helper).to have_received(:report_questions_table).with(questions)
+ end
+ end
+ end
+
+ describe "#report_forms_table" do
+ it "has table head" do
+ expect(helper.report_forms_table(forms)).to include(
+ head: helper.report_forms_table_head,
+ )
+ end
+
+ it "has table rows" do
+ expect(helper.report_forms_table(forms)).to include(
+ rows: helper.report_forms_table_rows(forms),
+ )
+ end
+ end
+
+ describe "#report_forms_with_routes_table" do
+ it "has table head" do
+ expect(helper.report_forms_with_routes_table(forms_with_routes)).to include(
+ head: helper.report_forms_with_routes_table_head,
+ )
+ end
+
+ it "has table rows" do
+ expect(helper.report_forms_with_routes_table(forms_with_routes)).to include(
+ rows: helper.report_forms_with_routes_table_rows(forms_with_routes),
+ )
+ end
+ end
+
+ describe "#report_questions_table" do
+ it "has table head" do
+ expect(helper.report_questions_table(questions)).to include(
+ head: helper.report_questions_table_head,
+ )
+ end
+
+ it "has table rows" do
+ expect(helper.report_questions_table(questions)).to include(
+ rows: helper.report_questions_table_rows(questions),
+ )
+ end
+ end
+
+ describe "#report_forms_table_head" do
+ it "returns the column headings for a table of forms" do
+ expect(helper.report_forms_table_head).to eq [
+ "Form name",
+ "Organisation",
+ ]
+ end
+ end
+
+ describe "#report_forms_table_rows" do
+ it "returns an array of arrays of strings" do
+ expect(helper.report_forms_table_rows(forms))
+ .to be_an(Array)
+ .and(all(be_an(Array)))
+ .and(all(all(be_a(String))))
+ end
+
+ it "returns a row for each form" do
+ expect(helper.report_forms_table_rows(forms).length)
+ .to eq forms.length
+ end
+
+ it "has a column in each row for each column heading" do
+ expect(helper.report_forms_table_rows(forms).map(&:length))
+ .to all eq helper.report_forms_table_head.length
+ end
+
+ it "formats a link for each form for the first column of each row" do
+ expect(helper.report_forms_table_rows(forms).map(&:first)).to eq [
+ "All question types form",
+ "Branch route form",
+ "Skip route form",
+ ]
+ end
+
+ it "includes the organisation name for each form for the second column of each row" do
+ expect(helper.report_forms_table_rows(forms).map(&:second)).to eq [
+ "Government Digital Service",
+ "Ministry of Tests",
+ "Department for Testing",
+ ]
+ end
+
+ context "when form is not in a group" do
+ let(:forms) do
+ [
+ { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => nil },
+ ]
+ end
+
+ it "returns the empty string for the organisation name" do
+ expect(helper.report_forms_table_rows(forms).map(&:second)).to eq [
+ "",
+ ]
+ end
+ end
+ end
+
+ describe "#report_forms_with_routes_table_head" do
+ it "returns the column headings for a table of forms and details of their routes" do
+ expect(helper.report_forms_with_routes_table_head).to eq [
+ "Form name",
+ "Organisation",
+ "Number of routes",
+ ]
+ end
+ end
+
+ describe "#report_forms_with_routes_table_rows" do
+ let(:forms) { forms_with_routes }
+
+ it "returns an array of arrays of strings" do
+ expect(helper.report_forms_with_routes_table_rows(forms))
+ .to be_an(Array)
+ .and(all(be_an(Array)))
+ .and(all(all(be_a(String))))
+ end
+
+ it "returns a row for each form" do
+ expect(helper.report_forms_with_routes_table_rows(forms).length)
+ .to eq forms.length
+ end
+
+ it "has a column in each row for each column heading" do
+ expect(helper.report_forms_with_routes_table_rows(forms).map(&:length))
+ .to all eq helper.report_forms_with_routes_table_head.length
+ end
+
+ it "formats a link for each form for the first column of each row" do
+ expect(helper.report_forms_with_routes_table_rows(forms).map(&:first)).to eq [
+ "Branch route form",
+ "Skip route form",
+ ]
+ end
+
+ it "includes the organisation name for each form for the second column of each row" do
+ expect(helper.report_forms_with_routes_table_rows(forms).map(&:second)).to eq [
+ "Ministry of Tests",
+ "Department for Testing",
+ ]
+ end
+
+ it "includes the number of routes in the form" do
+ expect(helper.report_forms_with_routes_table_rows(forms).map(&:third)).to eq %w[
+ 2
+ 1
+ ]
+ end
+
+ context "when form is not in a group" do
+ let(:forms) do
+ [
+ { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => nil },
+ ]
+ end
+
+ it "returns the empty string for the organisation name" do
+ expect(helper.report_forms_table_rows(forms).map(&:second)).to eq [
+ "",
+ ]
+ end
+ end
+ end
+
+ describe "#report_questions_table_head" do
+ it "returns the column headings for a table of questions" do
+ expect(helper.report_questions_table_head).to eq [
+ "Form name",
+ "Organisation",
+ "Question text",
+ ]
+ end
+ end
+
+ describe "#report_questions_table_rows" do
+ it "returns an array of arrays of strings" do
+ expect(helper.report_questions_table_rows(questions))
+ .to be_an(Array)
+ .and(all(be_an(Array)))
+ .and(all(all(be_a(String))))
+ end
+
+ it "formats a link for each form for the first column of each row" do
+ expect(helper.report_questions_table_rows(questions).map(&:first)).to eq [
+ "All question types form",
+ "Branch route form",
+ ]
+ end
+
+ it "includes the organisation name for each form for the second column of each row" do
+ expect(helper.report_questions_table_rows(questions).map(&:second)).to eq [
+ "Government Digital Service",
+ "Ministry of Tests",
+ ]
+ end
+
+ it "includes the question text for each question for the third column of each row" do
+ expect(helper.report_questions_table_rows(questions).map(&:third)).to eq [
+ "Email address",
+ "What’s your email address?",
+ ]
+ end
+
+ context "when form is not in a group" do
+ let(:questions) do
+ [
+ { "type" => "question_page", "data" => { "question_text" => "Email address" }, "form" => { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => nil } },
+ ]
+ end
+
+ it "returns the empty string for the organisation name" do
+ expect(helper.report_questions_table_rows(questions).map(&:second)).to eq [
+ "",
+ ]
+ end
+ end
+ end
+end
diff --git a/spec/requests/reports_controller_spec.rb b/spec/requests/reports_controller_spec.rb
index 4407cd411..68321ff9e 100644
--- a/spec/requests/reports_controller_spec.rb
+++ b/spec/requests/reports_controller_spec.rb
@@ -233,7 +233,7 @@
end
it "renders the features report view" do
- expect(response).to render_template("reports/questions_with_add_another_answer")
+ expect(response).to render_template("reports/feature_report")
end
it "includes the report data" do
@@ -291,7 +291,7 @@
end
it "renders the features report view" do
- expect(response).to render_template("reports/forms_with_routes")
+ expect(response).to render_template("reports/feature_report")
end
it "includes the report data" do
@@ -349,7 +349,7 @@
end
it "renders the features report view" do
- expect(response).to render_template("reports/forms_with_payments")
+ expect(response).to render_template("reports/feature_report")
end
it "includes the report data" do
@@ -407,7 +407,7 @@
end
it "renders the features report view" do
- expect(response).to render_template("reports/forms_with_csv_submission_enabled")
+ expect(response).to render_template("reports/feature_report")
end
it "includes the report data" do
@@ -695,62 +695,158 @@
expect(response.body).to include "A question"
end
end
+ end
- describe "#live_forms_csv" do
- let(:csv_reports_service_mock) { instance_double(Reports::CsvReportsService) }
- let(:dummy_csv) { '"Column 1", "Column 2"\n"Value 1", "Value 2"' }
+ describe "csv downloads" do
+ shared_examples_for "csv response" do
+ it "returns http code 200" do
+ expect(response).to have_http_status(:ok)
+ end
- before do
- allow(Reports::CsvReportsService).to receive(:new).and_return(csv_reports_service_mock)
- allow(csv_reports_service_mock).to receive(:live_forms_csv).and_return(dummy_csv)
+ it "has content-type text/csv" do
+ expect(response.headers["content-type"]).to eq "text/csv; charset=iso-8859-1"
+ end
+ end
+ describe "#live_forms_csv" do
+ before do
login_as_super_admin_user
+
+ travel_to Time.utc(2025, 5, 15, 15, 31, 57)
+
get report_live_forms_csv_path
end
- it "returns http code 200" do
- expect(response).to have_http_status(:ok)
- end
+ it_behaves_like "csv response"
it "responds with an attachment content-disposition header" do
- expect(response.headers["content-disposition"]).to match(/attachment; filename=live_forms_report-.*?\.csv/)
+ expect(response.headers["content-disposition"]).to match("attachment; filename=live_forms_report-2025-05-15 15:31:57 UTC.csv")
end
- it "has content-type text/csv" do
- expect(response.headers["content-type"]).to eq "text/csv; charset=iso-8859-1"
+ it "has expected response body" do
+ csv = CSV.parse(response.body, headers: true)
+ expect(csv.headers).to eq Reports::FormsCsvReportService::FORM_CSV_HEADERS
+ expect(csv.length).to eq 4
+ end
+ end
+
+ describe "#forms_with_routes as csv" do
+ before do
+ login_as_super_admin_user
+
+ travel_to Time.utc(2025, 5, 15, 15, 31, 57)
+
+ get report_forms_with_routes_path(format: :csv)
+ end
+
+ it_behaves_like "csv response"
+
+ it "responds with an attachment content-disposition header" do
+ expect(response.headers["content-disposition"]).to match("attachment; filename=live_forms_with_routes_report-2025-05-15 15:31:57 UTC.csv")
end
it "has expected response body" do
- expect(response.body).to eq(dummy_csv)
+ csv = CSV.parse(response.body, headers: true)
+ expect(csv.headers).to eq Reports::FormsCsvReportService::FORM_CSV_HEADERS
+ expect(csv.length).to eq 2
+ expect(csv.by_col["Form name"]).to eq [
+ "Branch route form",
+ "Skip route form",
+ ]
end
end
- describe "#live_questions_csv" do
- let(:csv_reports_service_mock) { instance_double(Reports::CsvReportsService) }
- let(:dummy_csv) { '"Column 1", "Column 2"\n"Value 1", "Value 2"' }
+ describe "#forms_with_payments as csv" do
+ before do
+ login_as_super_admin_user
+
+ travel_to Time.utc(2025, 5, 15, 15, 31, 57)
+
+ get report_forms_with_payments_path(format: :csv)
+ end
+
+ it_behaves_like "csv response"
+
+ it "responds with an attachment content-disposition header" do
+ expect(response.headers["content-disposition"]).to match("attachment; filename=live_forms_with_payments_report-2025-05-15 15:31:57 UTC.csv")
+ end
+
+ it "has expected response body" do
+ csv = CSV.parse(response.body, headers: true)
+ expect(csv.headers).to eq Reports::FormsCsvReportService::FORM_CSV_HEADERS
+ expect(csv.length).to eq 1
+ expect(csv.by_col["Form name"]).to eq [
+ "All question types form",
+ ]
+ end
+ end
+ describe "#forms_with_csv_submission_enabled as csv" do
before do
- allow(Reports::CsvReportsService).to receive(:new).and_return(csv_reports_service_mock)
- allow(csv_reports_service_mock).to receive(:live_questions_csv).and_return(dummy_csv)
+ login_as_super_admin_user
+
+ travel_to Time.utc(2025, 5, 15, 15, 31, 57)
+
+ get report_forms_with_csv_submission_enabled_path(format: :csv)
+ end
+ it_behaves_like "csv response"
+
+ it "responds with an attachment content-disposition header" do
+ expect(response.headers["content-disposition"]).to match("attachment; filename=live_forms_with_csv_submission_enabled_report-2025-05-15 15:31:57 UTC.csv")
+ end
+
+ it "has expected response body" do
+ csv = CSV.parse(response.body, headers: true)
+ expect(csv.headers).to eq Reports::FormsCsvReportService::FORM_CSV_HEADERS
+ expect(csv.length).to eq 1
+ expect(csv.by_col["Form name"]).to eq [
+ "All question types form",
+ ]
+ end
+ end
+
+ describe "#live_questions_csv" do
+ before do
login_as_super_admin_user
+
+ travel_to Time.utc(2025, 5, 15, 15, 31, 57)
+
get report_live_questions_csv_path
end
- it "returns http code 200" do
- expect(response).to have_http_status(:ok)
- end
+ it_behaves_like "csv response"
it "responds with an attachment content-disposition header" do
- expect(response.headers["content-disposition"]).to match(/attachment; filename=live_questions_report-.*?\.csv/)
+ expect(response.headers["content-disposition"]).to match("attachment; filename=live_questions_report-2025-05-15 15:31:57 UTC.csv")
end
- it "has content-type text/csv" do
- expect(response.headers["content-type"]).to eq "text/csv; charset=iso-8859-1"
+ it "has expected response body" do
+ csv = CSV.parse(response.body, headers: true)
+ expect(csv.headers).to eq Reports::QuestionsCsvReportService::QUESTIONS_CSV_HEADERS
+ expect(csv.length).to eq 17
+ end
+ end
+
+ describe "#questions_with_add_another_answer as csv" do
+ before do
+ login_as_super_admin_user
+
+ travel_to Time.utc(2025, 5, 15, 15, 31, 57)
+
+ get report_questions_with_add_another_answer_path(format: :csv)
+ end
+
+ it_behaves_like "csv response"
+
+ it "responds with an attachment content-disposition header" do
+ expect(response.headers["content-disposition"]).to match("attachment; filename=live_questions_with_add_another_answer_report-2025-05-15 15:31:57 UTC.csv")
end
it "has expected response body" do
- expect(response.body).to eq(dummy_csv)
+ csv = CSV.parse(response.body, headers: true)
+ expect(csv.headers).to eq Reports::QuestionsCsvReportService::QUESTIONS_CSV_HEADERS
+ expect(csv.length).to eq 2
end
end
end
diff --git a/spec/routing/reports_routing_spec.rb b/spec/routing/reports_routing_spec.rb
new file mode 100644
index 000000000..c2aad0ddd
--- /dev/null
+++ b/spec/routing/reports_routing_spec.rb
@@ -0,0 +1,65 @@
+require "rails_helper"
+
+RSpec.describe ReportsController, type: :routing do
+ describe "feature reports" do
+ describe "routing" do
+ it "routes to#features" do
+ expect(get: "/reports/features").to route_to("reports#features")
+ end
+
+ it "routes to #questions_with_answer_type" do
+ expect(get: "/reports/features/questions-with-answer-type/text").to route_to("reports#questions_with_answer_type", answer_type: "text")
+ end
+
+ it "routes to #questions_with_add_another_answer" do
+ expect(get: "/reports/features/questions-with-add-another-answer").to route_to("reports#questions_with_add_another_answer")
+ end
+
+ it "routes to #questions_with_add_another_answer with csv format" do
+ expect(get: "/reports/features/questions-with-add-another-answer.csv").to route_to("reports#questions_with_add_another_answer", format: "csv")
+ end
+
+ it "routes to #forms_with_routes" do
+ expect(get: "/reports/features/forms-with-routes").to route_to("reports#forms_with_routes")
+ end
+
+ it "routes to #forms_with_routes with csv format" do
+ expect(get: "/reports/features/forms-with-routes.csv").to route_to("reports#forms_with_routes", format: "csv")
+ end
+
+ it "routes to #forms_with_payments" do
+ expect(get: "/reports/features/forms-with-payments").to route_to("reports#forms_with_payments")
+ end
+
+ it "routes to #forms_with_payments with csv format" do
+ expect(get: "/reports/features/forms-with-payments.csv").to route_to("reports#forms_with_payments", format: "csv")
+ end
+
+ it "routes to #forms_with_csv_submission_enabled" do
+ expect(get: "/reports/features/forms-with-csv-submission-enabled").to route_to("reports#forms_with_csv_submission_enabled")
+ end
+
+ it "routes to #forms_with_csv_submission_enabled with csv format" do
+ expect(get: "/reports/features/forms-with-csv-submission-enabled.csv").to route_to("reports#forms_with_csv_submission_enabled", format: "csv")
+ end
+ end
+
+ describe "path helpers" do
+ it "routes to #questions_with_add_another_answer as csv" do
+ expect(get: report_questions_with_add_another_answer_path(format: :csv)).to route_to("reports#questions_with_add_another_answer", format: "csv")
+ end
+
+ it "routes to #forms_with_routes as csv" do
+ expect(get: report_forms_with_routes_path(format: :csv)).to route_to("reports#forms_with_routes", format: "csv")
+ end
+
+ it "routes to #forms_with_payments as csv" do
+ expect(get: report_forms_with_payments_path(format: :csv)).to route_to("reports#forms_with_payments", format: "csv")
+ end
+
+ it "routes to #forms_with_csv_submission_enabled as csv" do
+ expect(get: report_forms_with_csv_submission_enabled_path(format: :csv)).to route_to("reports#forms_with_csv_submission_enabled", format: "csv")
+ end
+ end
+ end
+end
diff --git a/spec/services/reports/csv_reports_service_spec.rb b/spec/services/reports/csv_reports_service_spec.rb
deleted file mode 100644
index 5d80e1f45..000000000
--- a/spec/services/reports/csv_reports_service_spec.rb
+++ /dev/null
@@ -1,257 +0,0 @@
-require "rails_helper"
-
-RSpec.describe Reports::CsvReportsService do
- subject(:csv_reports_service) do
- described_class.new
- end
-
- let(:form_documents_response_json) { JSON.parse(file_fixture("form_documents_response.json").read) }
-
- let(:group) { create(:group) }
-
- before do
- GroupForm.create!(form_id: 1, group:)
- GroupForm.create!(form_id: 2, group:)
- GroupForm.create!(form_id: 3, group:)
-
- allow(Reports::FormDocumentsService).to receive(:live_form_documents).and_return(form_documents_response_json)
- end
-
- describe "#live_forms_csv" do
- it "returns a CSV with 4 rows, including the header row" do
- csv = csv_reports_service.live_forms_csv
- rows = CSV.parse(csv)
- expect(rows.length).to eq 4
- end
-
- it "has expected values" do
- csv = csv_reports_service.live_forms_csv
- rows = CSV.parse(csv)
- expect(rows[1]).to eq([
- "1",
- "live",
- "All question types form",
- "all-question-types-form",
- group.organisation.name,
- group.organisation.id.to_s,
- group.name,
- group.external_id,
- "2025-01-02T16:24:31.203Z",
- "2025-01-02T16:24:31.255Z",
- "9",
- "false",
- "https://www.gov.uk/payments/your-payment-link",
- nil,
- nil,
- "your.email+fakedata84701@gmail.com.gov.uk",
- "08000800",
- "https://www.gov.uk/help/privacy-notice",
- "Test",
- "email_with_csv",
- ])
- end
- end
-
- describe "#live_forms_with_routes_csv" do
- it "returns a CSV with 2 rows, including the header row" do
- csv = csv_reports_service.live_forms_with_routes_csv
- rows = CSV.parse(csv)
- expect(rows.length).to eq 2
- end
-
- it "includes form with routes" do
- csv = csv_reports_service.live_forms_with_routes_csv
- rows = CSV.parse(csv)
- has_routes_column_index = rows[0].find_index("Has routes")
- expect(rows[1][has_routes_column_index]).to eq "true"
- end
- end
-
- describe "#live_forms_with_payments_csv" do
- it "returns a CSV with 2 rows, including the header row" do
- csv = csv_reports_service.live_forms_with_payments_csv
- rows = CSV.parse(csv)
- expect(rows.length).to eq 2
- end
-
- it "includes form with payments" do
- csv = csv_reports_service.live_forms_with_payments_csv
- rows = CSV.parse(csv)
- payment_url_column_index = rows[0].find_index("Payment URL")
- expect(rows[1][payment_url_column_index]).to eq "https://www.gov.uk/payments/your-payment-link"
- end
- end
-
- describe "#live_forms_with_csv_submission_enabled_csv" do
- it "returns a CSV with 2 rows, including the header row" do
- csv = csv_reports_service.live_forms_with_csv_submission_enabled_csv
- rows = CSV.parse(csv)
- expect(rows.length).to eq 2
- end
-
- it "includes form with submission type email_with_csv" do
- csv = csv_reports_service.live_forms_with_csv_submission_enabled_csv
- rows = CSV.parse(csv)
- submission_type_column_index = rows[0].find_index("Submission type")
- expect(rows[1][submission_type_column_index]).to eq "email_with_csv"
- end
- end
-
- describe "#live_questions_csv" do
- context "when answer_type is nil" do
- it "returns a CSV with 16 rows, including the header row" do
- csv = csv_reports_service.live_questions_csv
- rows = CSV.parse(csv)
- expect(rows.length).to eq 16
- end
-
- it "has expected values for text question" do
- csv = csv_reports_service.live_questions_csv
- rows = CSV.parse(csv)
- text_question_row = rows.detect { |row| row.include? "Single line of text" }
- expect(text_question_row).to eq([
- "1",
- "live",
- "All question types form",
- group.organisation.name,
- group.organisation.id.to_s,
- group.name,
- group.external_id,
- "1",
- "Single line of text",
- "text",
- nil,
- nil,
- nil,
- "false",
- "true",
- "false",
- "single_line",
- nil,
- nil,
- nil,
- "{\"input_type\" => \"single_line\"}",
- ])
- end
-
- it "has expected values for selection question" do
- csv = csv_reports_service.live_questions_csv
- rows = CSV.parse(csv)
- selection_question_row = rows.detect { |row| row.include? "Selection from a list of options" }
- expect(selection_question_row).to eq([
- "1",
- "live",
- "All question types form",
- group.organisation.name,
- group.organisation.id.to_s,
- group.name,
- group.external_id,
- "8",
- "Selection from a list of options",
- "selection",
- nil,
- nil,
- nil,
- "true",
- "false",
- "false",
- nil,
- "false",
- "3",
- nil,
- "{\"only_one_option\" => \"0\", \"selection_options\" => [{\"name\" => \"Option 1\"}, {\"name\" => \"Option 2\"}, {\"name\" => \"Option 3\"}]}",
- ])
- end
-
- it "has expected values for name question" do
- csv = csv_reports_service.live_questions_csv
- rows = CSV.parse(csv)
- name_question_row = rows.detect { |row| row.include? "What’s your name?" }
- expect(name_question_row).to eq([
- "3",
- "live",
- "Branch route form",
- group.organisation.name,
- group.organisation.id.to_s,
- group.name,
- group.external_id,
- "2",
- "What’s your name?",
- "name",
- nil,
- nil,
- nil,
- "false",
- "false",
- "false",
- "full_name",
- nil,
- nil,
- "false",
- "{\"input_type\" => \"full_name\", \"title_needed\" => false}",
- ])
- end
-
- it "has expected values for question with routing conditions" do
- csv = csv_reports_service.live_questions_csv
- rows = CSV.parse(csv)
- routing_question_row = rows.detect { |row| row.include? "How many times have you filled out this form?" }
- expect(routing_question_row).to eq([
- "3",
- "live",
- "Branch route form",
- group.organisation.name,
- group.organisation.id.to_s,
- group.name,
- group.external_id,
- "1",
- "How many times have you filled out this form?",
- "selection",
- nil,
- nil,
- nil,
- "false",
- "false",
- "true",
- nil,
- "true",
- "2",
- nil,
- "{\"only_one_option\" => \"true\", \"selection_options\" => [{\"name\" => \"Once\"}, {\"name\" => \"More than once\"}]}",
- ])
- end
- end
-
- context "when answer_type is provided" do
- it "returns 3 rows, including the header row" do
- csv = csv_reports_service.live_questions_csv(answer_type: "email")
- rows = CSV.parse(csv)
- expect(rows.length).to eq 3
- end
-
- it "only includes the desired answer_type" do
- csv = csv_reports_service.live_questions_csv(answer_type: "email")
- rows = CSV.parse(csv)
- answer_type_column_index = rows[0].find_index("Answer type")
- expect(rows[1][answer_type_column_index]).to eq "email"
- expect(rows[2][answer_type_column_index]).to eq "email"
- end
- end
- end
-
- describe "#live_questions_with_add_another_answer_csv" do
- it "returns 3 rows, including the header row" do
- csv = csv_reports_service.live_questions_with_add_another_answer_csv
- rows = CSV.parse(csv)
- expect(rows.length).to eq 3
- end
-
- it "only includes questions with add another answer" do
- csv = csv_reports_service.live_questions_with_add_another_answer_csv
- rows = CSV.parse(csv)
- answer_type_column_index = rows[0].find_index("Is repeatable?")
- expect(rows[1][answer_type_column_index]).to eq "true"
- expect(rows[2][answer_type_column_index]).to eq "true"
- end
- end
-end
diff --git a/spec/services/reports/feature_report_service_spec.rb b/spec/services/reports/feature_report_service_spec.rb
index 1c4859803..949228e2d 100644
--- a/spec/services/reports/feature_report_service_spec.rb
+++ b/spec/services/reports/feature_report_service_spec.rb
@@ -1,127 +1,337 @@
require "rails_helper"
RSpec.describe Reports::FeatureReportService do
- let(:form_documents_response_json) { JSON.parse(file_fixture("form_documents_response.json").read) }
+ let(:form_documents) { JSON.parse(file_fixture("form_documents_response.json").read) }
let(:group) { create(:group) }
before do
GroupForm.create!(form_id: 1, group:)
GroupForm.create!(form_id: 2, group:)
GroupForm.create!(form_id: 3, group:)
-
- allow(Reports::FormDocumentsService).to receive(:live_form_documents).and_return(form_documents_response_json)
+ GroupForm.create!(form_id: 4, group:)
end
describe "#report" do
it "returns the feature report" do
- report = described_class.report
+ report = described_class.new(form_documents).report
expect(report).to eq({
- total_live_forms: 3,
- live_forms_with_payment: 1,
- live_forms_with_routing: 1,
- live_forms_with_add_another_answer: 1,
- live_forms_with_csv_submission_enabled: 1,
- live_forms_with_answer_type: {
+ total_forms: 4,
+ forms_with_payment: 1,
+ forms_with_routing: 2,
+ forms_with_add_another_answer: 1,
+ forms_with_csv_submission_enabled: 1,
+ forms_with_answer_type: {
"address" => 1,
"date" => 1,
"email" => 2,
- "name" => 1,
+ "name" => 2,
"national_insurance_number" => 1,
"number" => 1,
"phone_number" => 1,
- "selection" => 2,
+ "selection" => 3,
"text" => 3,
},
- live_steps_with_answer_type: {
+ steps_with_answer_type: {
"address" => 1,
"date" => 1,
"email" => 2,
- "name" => 1,
+ "name" => 2,
"national_insurance_number" => 1,
"number" => 1,
"phone_number" => 1,
- "selection" => 2,
+ "selection" => 3,
"text" => 5,
},
})
end
end
+ describe "#questions" do
+ it "returns all questions in all forms given" do
+ questions = described_class.new(form_documents).questions
+ expect(questions.length).to eq 17
+ end
+
+ it "returns details needed to render report" do
+ questions = described_class.new(form_documents).questions
+ expect(questions).to all match(
+ a_hash_including(
+ "form" => a_hash_including(
+ "form_id" => an_instance_of(Integer),
+ "content" => a_hash_including(
+ "name" => a_kind_of(String),
+ ),
+ ),
+ "data" => a_hash_including(
+ "question_text" => a_kind_of(String),
+ ),
+ ),
+ )
+ end
+
+ it "includes a reference to the form document" do
+ questions = described_class.new(form_documents).questions_with_answer_type("text")
+ expect(questions).to all include(
+ "form" => a_hash_including(
+ "form_id",
+ "content" => a_hash_including(
+ "name",
+ ),
+ ),
+ )
+ end
+ end
+
describe "#questions_with_answer_type" do
+ it "returns details needed to render report" do
+ questions = described_class.new(form_documents).questions_with_answer_type("email")
+ expect(questions).to match [
+ a_hash_including(
+ "form" => a_hash_including(
+ "form_id" => 1,
+ "content" => a_hash_including(
+ "name" => "All question types form",
+ ),
+ ),
+ "data" => a_hash_including(
+ "question_text" => "Email address",
+ ),
+ ),
+ a_hash_including(
+ "form" => a_hash_including(
+ "form_id" => 3,
+ "content" => a_hash_including(
+ "name" => "Branch route form",
+ ),
+ ),
+ "data" => a_hash_including(
+ "question_text" => "What’s your email address?",
+ ),
+ ),
+ ]
+ end
+
it "returns questions with the given answer type" do
- questions = described_class.questions_with_answer_type("email")
+ questions = described_class.new(form_documents).questions_with_answer_type("name")
expect(questions.length).to eq 2
- expect(questions).to include(
- {
- form_name: "All question types form",
- form_id: 1,
- organisation_name: group.organisation.name,
- question_text: "Email address",
- },
+ expect(questions).to all match(
+ a_hash_including(
+ "data" => a_hash_including(
+ "answer_type" => "name",
+ ),
+ ),
+ )
+ end
+
+ it "includes a reference to the form document" do
+ questions = described_class.new(form_documents).questions_with_answer_type("text")
+ expect(questions).to all include(
+ "form" => a_hash_including(
+ "form_id",
+ "content" => a_hash_including(
+ "name",
+ ),
+ ),
)
- expect(questions).to include({
- form_name: "Branch route form",
- form_id: 3,
- organisation_name: group.organisation.name,
- question_text: "What’s your email address?",
- })
end
end
- describe "#live_questions_with_add_another_answer" do
+ describe "#questions_with_add_another_answer" do
+ it "returns details needed to render report" do
+ questions = described_class.new(form_documents).questions_with_add_another_answer
+ expect(questions).to match [
+ a_hash_including(
+ "form" => a_hash_including(
+ "form_id" => 1,
+ "content" => a_hash_including(
+ "name" => "All question types form",
+ ),
+ "group" => a_hash_including(
+ "organisation" => a_hash_including(
+ "name" => group.organisation.name,
+ ),
+ ),
+ ),
+ "data" => a_hash_including(
+ "question_text" => "Single line of text",
+ ),
+ ),
+ a_hash_including(
+ "form" => a_hash_including(
+ "form_id" => 1,
+ "content" => a_hash_including(
+ "name" => "All question types form",
+ ),
+ "group" => a_hash_including(
+ "organisation" => a_hash_including(
+ "name" => group.organisation.name,
+ ),
+ ),
+ ),
+ "data" => a_hash_including(
+ "question_text" => "Number",
+ ),
+ ),
+ ]
+ end
+
it "returns questions with add another answer" do
- questions = described_class.live_questions_with_add_another_answer
- expect(questions.length).to eq 2
- expect(questions).to include(
- {
- form_name: "All question types form",
- form_id: 1,
- organisation_name: group.organisation.name,
- question_text: "Single line of text",
- },
+ questions = described_class.new(form_documents).questions_with_add_another_answer
+ expect(questions).to all match(
+ a_hash_including(
+ "data" => a_hash_including(
+ "is_repeatable" => true,
+ ),
+ ),
+ )
+ end
+
+ it "includes a reference to the form document" do
+ questions = described_class.new(form_documents).questions_with_answer_type("text")
+ expect(questions).to all include(
+ "form" => a_hash_including(
+ "form_id",
+ "content" => a_hash_including(
+ "name",
+ ),
+ ),
+ )
+ end
+
+ it "includes a reference to the organisation record" do
+ questions = described_class.new(form_documents).questions_with_answer_type("text")
+ expect(questions).to all include(
+ "form" => a_hash_including(
+ "group" => a_hash_including(
+ "organisation" => a_hash_including(
+ "name",
+ ),
+ ),
+ ),
)
- expect(questions).to include({
- form_name: "All question types form",
- form_id: 1,
- organisation_name: group.organisation.name,
- question_text: "Number",
- })
end
end
- describe "#live_forms_with_routes" do
+ describe "#forms_with_routes" do
+ it "returns details needed to render report" do
+ forms = described_class.new(form_documents).forms_with_routes
+ expect(forms).to match [
+ a_hash_including(
+ "form_id" => 3,
+ "content" => a_hash_including(
+ "name" => "Branch route form",
+ ),
+ "group" => a_hash_including(
+ "organisation" => a_hash_including(
+ "name" => group.organisation.name,
+ ),
+ ),
+ "metadata" => {
+ "number_of_routes" => 2,
+ },
+ ),
+ a_hash_including(
+ "form_id" => 4,
+ "content" => a_hash_including(
+ "name" => "Skip route form",
+ ),
+ "group" => a_hash_including(
+ "organisation" => a_hash_including(
+ "name" => group.organisation.name,
+ ),
+ ),
+ "metadata" => {
+ "number_of_routes" => 1,
+ },
+ ),
+ ]
+ end
+
it "returns forms with routes" do
- forms = described_class.live_forms_with_routes
- expect(forms.length).to eq 1
- expect(forms).to include(
- form_name: "Branch route form",
- form_id: 3,
- organisation_name: group.organisation.name,
- number_of_routes: 2,
+ forms = described_class.new(form_documents).forms_with_routes
+ expect(forms).to match [
+ a_hash_including(
+ "form_id" => 3,
+ "content" => a_hash_including(
+ "name" => "Branch route form",
+ ),
+ ),
+ a_hash_including(
+ "form_id" => 4,
+ "content" => a_hash_including(
+ "name" => "Skip route form",
+ ),
+ ),
+ ]
+ end
+
+ it "includes counts of routes" do
+ forms = described_class.new(form_documents).forms_with_routes
+ expect(forms).to all include(
+ "metadata" => a_hash_including(
+ "number_of_routes" => an_instance_of(Integer),
+ ),
+ )
+ end
+
+ it "includes a reference to the organisation record" do
+ forms = described_class.new(form_documents).forms_with_routes
+ expect(forms).to all include(
+ "group" => a_hash_including(
+ "organisation" => a_hash_including(
+ "name",
+ ),
+ ),
)
end
end
- describe "#live_forms_with_payments" do
+ describe "#forms_with_payments" do
it "returns live forms with payments" do
- forms = described_class.live_forms_with_payments
- expect(forms.length).to eq 1
- expect(forms).to include(
- form_name: "All question types form",
- form_id: 1,
- organisation_name: group.organisation.name,
+ forms = described_class.new(form_documents).forms_with_payments
+ expect(forms).to match [
+ a_hash_including(
+ "form_id" => 1,
+ "content" => a_hash_including(
+ "name" => "All question types form",
+ ),
+ ),
+ ]
+ end
+
+ it "includes a reference to the organisation record" do
+ forms = described_class.new(form_documents).forms_with_routes
+ expect(forms).to all include(
+ "group" => a_hash_including(
+ "organisation" => a_hash_including(
+ "name",
+ ),
+ ),
)
end
end
- describe "#live_forms_with_csv_submission_enabled" do
+ describe "#forms_with_csv_submission_enabled" do
it "returns live forms with csv enabled" do
- forms = described_class.live_forms_with_csv_submission_enabled
- expect(forms.length).to eq 1
- expect(forms).to include(
- form_name: "All question types form",
- form_id: 1,
- organisation_name: group.organisation.name,
+ forms = described_class.new(form_documents).forms_with_csv_submission_enabled
+ expect(forms).to match [
+ a_hash_including(
+ "form_id" => 1,
+ "content" => a_hash_including(
+ "name" => "All question types form",
+ ),
+ ),
+ ]
+ end
+
+ it "includes a reference to the organisation record" do
+ forms = described_class.new(form_documents).forms_with_routes
+ expect(forms).to all include(
+ "group" => a_hash_including(
+ "organisation" => a_hash_including(
+ "name",
+ ),
+ ),
)
end
end
diff --git a/spec/services/reports/form_documents_service_spec.rb b/spec/services/reports/form_documents_service_spec.rb
index bad1082e3..c4639a0fd 100644
--- a/spec/services/reports/form_documents_service_spec.rb
+++ b/spec/services/reports/form_documents_service_spec.rb
@@ -1,80 +1,83 @@
require "rails_helper"
RSpec.describe Reports::FormDocumentsService do
- let(:form_documents_url) { "#{Settings.forms_api.base_url}/api/v2/form-documents".freeze }
- # This response JSON was generated by making a real API request to forms-api with the data from the database seeds.
- # Once we have transitioned to using the V2 API in forms-admin, it might make more sense to use factories to generate
- # the response.
- let(:form_documents_response_json) { file_fixture("form_documents_response.json").read }
+ describe ".live_form_documents" do
+ let(:form_documents_url) { "#{Settings.forms_api.base_url}/api/v2/form-documents".freeze }
+ # This response JSON was generated by making a real API request to forms-api with the data from the database seeds.
+ # Once we have transitioned to using the V2 API in forms-admin, it might make more sense to use factories to generate
+ # the response.
+ let(:form_documents_response_json) { file_fixture("form_documents_response.json").read }
- before do
- stub_request(:get, form_documents_url)
- .with(query: { page: "1", per_page: "3", tag: "live" })
- .to_return(body: form_documents_response_json, headers: response_headers(9, 0, 3))
- stub_request(:get, form_documents_url)
- .with(query: { page: "2", per_page: "3", tag: "live" })
- .to_return(body: form_documents_response_json, headers: response_headers(9, 3, 3))
- stub_request(:get, form_documents_url)
- .with(query: { page: "3", per_page: "3", tag: "live" })
- .to_return(body: form_documents_response_json, headers: response_headers(9, 6, 3))
- end
-
- it "makes request to forms-api for each page of results" do
- form_documents = described_class.live_form_documents.to_a
- expect(form_documents.size).to eq(9)
- assert_requested(:get, form_documents_url, query: { page: "1", per_page: "3", tag: "live" }, headers:, times: 1)
- assert_requested(:get, form_documents_url, query: { page: "2", per_page: "3", tag: "live" }, headers:, times: 1)
- assert_requested(:get, form_documents_url, query: { page: "3", per_page: "3", tag: "live" }, headers:, times: 1)
- end
-
- it "returns form documents" do
- form_document = described_class.live_form_documents.first
- expect(form_document).to be_a(Hash)
- expect(form_document).to have_key("form_id")
- end
-
- context "when forms-api responds with a non-success status code" do
before do
+ allow(Settings.reports).to receive(:forms_api_forms_per_request_page).and_return 4
+
+ stub_request(:get, form_documents_url)
+ .with(query: { page: "1", per_page: "4", tag: "live" })
+ .to_return(body: form_documents_response_json, headers: response_headers(12, 0, 4))
stub_request(:get, form_documents_url)
- .with(query: { page: "1", per_page: "3", tag: "live" })
- .to_return(body: "There was an error", status: 400)
+ .with(query: { page: "2", per_page: "4", tag: "live" })
+ .to_return(body: form_documents_response_json, headers: response_headers(12, 4, 4))
+ stub_request(:get, form_documents_url)
+ .with(query: { page: "3", per_page: "4", tag: "live" })
+ .to_return(body: form_documents_response_json, headers: response_headers(12, 8, 4))
end
- it "raises a StandardError" do
- expect { described_class.live_form_documents.first }.to raise_error(
- StandardError, "Forms API responded with a non-success HTTP code when retrieving form documents: status 400"
- )
+ it "makes request to forms-api for each page of results" do
+ form_documents = described_class.live_form_documents.to_a
+ expect(form_documents.size).to eq(12)
+ assert_requested(:get, form_documents_url, query: { page: "1", per_page: "4", tag: "live" }, headers:, times: 1)
+ assert_requested(:get, form_documents_url, query: { page: "2", per_page: "4", tag: "live" }, headers:, times: 1)
+ assert_requested(:get, form_documents_url, query: { page: "3", per_page: "4", tag: "live" }, headers:, times: 1)
end
- end
- context "when there are forms from internal organisations" do
- let(:organisation) { build :organisation, id: 1, internal: false }
- let(:internal_organisation) { build :organisation, id: 1, internal: true }
- let(:group) { build :group, id: 1, organisation: }
- let(:internal_group) { build :group, id: 1, organisation: internal_organisation }
- let(:group_form) { GroupForm.new(group:) }
- let(:internal_group_form) { GroupForm.new(group: internal_group) }
+ it "returns form documents" do
+ form_document = described_class.live_form_documents.first
+ expect(form_document).to be_a(Hash)
+ expect(form_document).to have_key("form_id")
+ end
- before do
- allow(GroupForm).to receive(:find_by_form_id).with(1).and_return(group_form)
- allow(GroupForm).to receive(:find_by_form_id).with(2).and_return(group_form)
- allow(GroupForm).to receive(:find_by_form_id).with(3).and_return(internal_group_form)
+ context "when forms-api responds with a non-success status code" do
+ before do
+ stub_request(:get, form_documents_url)
+ .with(query: { page: "1", per_page: "4", tag: "live" })
+ .to_return(body: "There was an error", status: 400)
+ end
+
+ it "raises a StandardError" do
+ expect { described_class.live_form_documents.first }.to raise_error(
+ StandardError, "Forms API responded with a non-success HTTP code when retrieving form documents: status 400"
+ )
+ end
end
- it "does not include these forms in the live_form_documents output" do
- form_documents = described_class.live_form_documents.to_a
- expect(form_documents.size).to eq(6)
+ context "when there are forms from internal organisations" do
+ let(:organisation) { build :organisation, id: 1, internal: false }
+ let(:internal_organisation) { build :organisation, id: 1, internal: true }
+ let(:group) { build :group, id: 1, organisation: }
+ let(:internal_group) { build :group, id: 1, organisation: internal_organisation }
+ let(:group_form) { GroupForm.new(group:) }
+ let(:internal_group_form) { GroupForm.new(group: internal_group) }
- form_documents_with_internal_id = form_documents.filter { it["id"] == 3 }
- expect(form_documents_with_internal_id).to be_empty
+ before do
+ allow(GroupForm).to receive(:find_by_form_id).and_return(group_form)
+ allow(GroupForm).to receive(:find_by_form_id).with(3).and_return(internal_group_form)
+ end
+
+ it "does not include these forms in the live_form_documents output" do
+ form_documents = described_class.live_form_documents.to_a
+ expect(form_documents.size).to eq(9)
+
+ form_documents_with_internal_id = form_documents.filter { it["id"] == 3 }
+ expect(form_documents_with_internal_id).to be_empty
+ end
end
- end
- def response_headers(total, offset, limit)
- {
- "pagination-total" => total.to_s,
- "pagination-offset" => offset.to_s,
- "pagination-limit" => limit.to_s,
- }
+ def response_headers(total, offset, limit)
+ {
+ "pagination-total" => total.to_s,
+ "pagination-offset" => offset.to_s,
+ "pagination-limit" => limit.to_s,
+ }
+ end
end
end
diff --git a/spec/services/reports/forms_csv_report_service_spec.rb b/spec/services/reports/forms_csv_report_service_spec.rb
new file mode 100644
index 000000000..c6eff3072
--- /dev/null
+++ b/spec/services/reports/forms_csv_report_service_spec.rb
@@ -0,0 +1,53 @@
+require "rails_helper"
+
+RSpec.describe Reports::FormsCsvReportService do
+ subject(:csv_reports_service) do
+ described_class.new(form_documents)
+ end
+
+ let(:form_documents) { JSON.parse(file_fixture("form_documents_response.json").read) }
+
+ let(:group) { create(:group) }
+
+ before do
+ GroupForm.create!(form_id: 1, group:)
+ GroupForm.create!(form_id: 2, group:)
+ GroupForm.create!(form_id: 3, group:)
+ GroupForm.create!(form_id: 4, group:)
+ end
+
+ describe "#csv" do
+ it "returns a CSV with a header row and a row for each form" do
+ csv = csv_reports_service.csv
+ rows = CSV.parse(csv)
+ expect(rows.length).to eq 5
+ end
+
+ it "has expected values" do
+ csv = csv_reports_service.csv
+ rows = CSV.parse(csv)
+ expect(rows[1]).to eq([
+ "1",
+ "live",
+ "All question types form",
+ "all-question-types-form",
+ group.organisation.name,
+ group.organisation.id.to_s,
+ group.name,
+ group.external_id,
+ "2025-01-02T16:24:31.203Z",
+ "2025-01-02T16:24:31.255Z",
+ "9",
+ "false",
+ "https://www.gov.uk/payments/your-payment-link",
+ nil,
+ nil,
+ "your.email+fakedata84701@gmail.com.gov.uk",
+ "08000800",
+ "https://www.gov.uk/help/privacy-notice",
+ "Test",
+ "email_with_csv",
+ ])
+ end
+ end
+end
diff --git a/spec/services/reports/questions_csv_report_service_spec.rb b/spec/services/reports/questions_csv_report_service_spec.rb
new file mode 100644
index 000000000..f6ccf96ab
--- /dev/null
+++ b/spec/services/reports/questions_csv_report_service_spec.rb
@@ -0,0 +1,143 @@
+require "rails_helper"
+
+RSpec.describe Reports::QuestionsCsvReportService do
+ subject(:csv_reports_service) do
+ described_class.new(question_page_documents)
+ end
+
+ let(:question_page_documents) { Reports::FeatureReportService.new(form_documents).questions }
+ let(:form_documents) { JSON.parse(file_fixture("form_documents_response.json").read) }
+
+ let(:group) { create(:group) }
+
+ before do
+ GroupForm.create!(form_id: 1, group:)
+ GroupForm.create!(form_id: 2, group:)
+ GroupForm.create!(form_id: 3, group:)
+ GroupForm.create!(form_id: 4, group:)
+ end
+
+ describe "#csv" do
+ it "returns a CSV with a header row and a rows for each question" do
+ csv = csv_reports_service.csv
+ rows = CSV.parse(csv)
+ expect(rows.length).to eq 18
+ end
+
+ it "has expected values for text question" do
+ csv = csv_reports_service.csv
+ rows = CSV.parse(csv)
+ text_question_row = rows.detect { |row| row.include? "Single line of text" }
+ expect(text_question_row).to eq([
+ "1",
+ "live",
+ "All question types form",
+ group.organisation.name,
+ group.organisation.id.to_s,
+ group.name,
+ group.external_id,
+ "1",
+ "Single line of text",
+ "text",
+ nil,
+ nil,
+ nil,
+ "false",
+ "true",
+ "false",
+ "single_line",
+ nil,
+ nil,
+ nil,
+ "{\"input_type\" => \"single_line\"}",
+ ])
+ end
+
+ it "has expected values for selection question" do
+ csv = csv_reports_service.csv
+ rows = CSV.parse(csv)
+ selection_question_row = rows.detect { |row| row.include? "Selection from a list of options" }
+ expect(selection_question_row).to eq([
+ "1",
+ "live",
+ "All question types form",
+ group.organisation.name,
+ group.organisation.id.to_s,
+ group.name,
+ group.external_id,
+ "8",
+ "Selection from a list of options",
+ "selection",
+ nil,
+ nil,
+ nil,
+ "true",
+ "false",
+ "false",
+ nil,
+ "false",
+ "3",
+ nil,
+ "{\"only_one_option\" => \"0\", \"selection_options\" => [{\"name\" => \"Option 1\"}, {\"name\" => \"Option 2\"}, {\"name\" => \"Option 3\"}]}",
+ ])
+ end
+
+ it "has expected values for name question" do
+ csv = csv_reports_service.csv
+ rows = CSV.parse(csv)
+ name_question_row = rows.detect { |row| row.include? "What’s your name?" }
+ expect(name_question_row).to eq([
+ "3",
+ "live",
+ "Branch route form",
+ group.organisation.name,
+ group.organisation.id.to_s,
+ group.name,
+ group.external_id,
+ "2",
+ "What’s your name?",
+ "name",
+ nil,
+ nil,
+ nil,
+ "false",
+ "false",
+ "false",
+ "full_name",
+ nil,
+ nil,
+ "false",
+ "{\"input_type\" => \"full_name\", \"title_needed\" => false}",
+ ])
+ end
+
+ it "has expected values for question with routing conditions" do
+ csv = csv_reports_service.csv
+ rows = CSV.parse(csv)
+ routing_question_row = rows.detect { |row| row.include? "How many times have you filled out this form?" }
+ expect(routing_question_row).to eq([
+ "3",
+ "live",
+ "Branch route form",
+ group.organisation.name,
+ group.organisation.id.to_s,
+ group.name,
+ group.external_id,
+ "1",
+ "How many times have you filled out this form?",
+ "selection",
+ nil,
+ nil,
+ nil,
+ "false",
+ "false",
+ "true",
+ nil,
+ "true",
+ "2",
+ nil,
+ "{\"only_one_option\" => \"true\", \"selection_options\" => [{\"name\" => \"Once\"}, {\"name\" => \"More than once\"}]}",
+ ])
+ end
+ end
+end
diff --git a/spec/views/reports/feature_report.html.erb_spec.rb b/spec/views/reports/feature_report.html.erb_spec.rb
new file mode 100644
index 000000000..9d8cd48ca
--- /dev/null
+++ b/spec/views/reports/feature_report.html.erb_spec.rb
@@ -0,0 +1,202 @@
+require "rails_helper"
+
+describe "reports/feature_report" do
+ let(:report) {}
+ let(:records) { [] }
+
+ before do
+ controller.request.path_parameters[:action] = report
+
+ render locals: { report:, records: }
+ end
+
+ context "with forms_with_csv_submission_enabled report" do
+ let(:report) { "forms_with_csv_submission_enabled" }
+ let(:records) do
+ [
+ { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } } },
+ { "form_id" => 3, "content" => { "name" => "Branch route form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } } },
+ ]
+ end
+
+ describe "page title" do
+ it "matches the heading" do
+ expect(view.content_for(:title)).to eq "Live forms with CSV submission enabled"
+ end
+ end
+
+ it "has a back link to feature usage report" do
+ expect(view.content_for(:back_link)).to have_link("Back to feature and answer type usage", href: report_features_path)
+ end
+
+ it "has a link to download the CSV" do
+ expect(rendered).to have_link("Download data about all live forms with CSV submission enabled as a CSV file", href: report_forms_with_csv_submission_enabled_path(format: :csv))
+ end
+
+ describe "questions table" do
+ it "has the correct headers" do
+ page = Capybara.string(rendered.html)
+ within(page.find(".govuk-table__head")) do
+ expect(page.find_all(".govuk-table__header"[0])).to have_text "Form name"
+ expect(page.find_all(".govuk-table__header"[1])).to have_text "Organisation"
+ end
+ end
+
+ it "has rows for each question" do
+ page = Capybara.string(rendered.html)
+ within(page.find_all(".govuk-table__row")[1]) do
+ expect(page.find_all(".govuk-table__cell"[0])).to have_text "All question types form"
+ expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
+ end
+ within(page.find_all(".govuk-table__row")[2]) do
+ expect(page.find_all(".govuk-table__cell"[0])).to have_text "Branch route form"
+ expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
+ end
+ end
+ end
+ end
+
+ context "with forms_with_payments report" do
+ let(:report) { "forms_with_payments" }
+ let(:records) do
+ [
+ { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } } },
+ { "form_id" => 3, "content" => { "name" => "Branch route form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } } },
+ ]
+ end
+
+ describe "page title" do
+ it "matches the heading" do
+ expect(view.content_for(:title)).to eq "Live forms with payments"
+ end
+ end
+
+ it "has a back link to feature usage report" do
+ expect(view.content_for(:back_link)).to have_link("Back to feature and answer type usage", href: report_features_path)
+ end
+
+ it "has a link to download the CSV" do
+ expect(rendered).to have_link("Download data about all live forms with payments as a CSV file", href: report_forms_with_payments_path(format: :csv))
+ end
+
+ describe "questions table" do
+ it "has the correct headers" do
+ page = Capybara.string(rendered.html)
+ within(page.find(".govuk-table__head")) do
+ expect(page.find_all(".govuk-table__header"[0])).to have_text "Form name"
+ expect(page.find_all(".govuk-table__header"[1])).to have_text "Organisation"
+ end
+ end
+
+ it "has rows for each question" do
+ page = Capybara.string(rendered.html)
+ within(page.find_all(".govuk-table__row")[1]) do
+ expect(page.find_all(".govuk-table__cell"[0])).to have_text "All question types form"
+ expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
+ end
+ within(page.find_all(".govuk-table__row")[2]) do
+ expect(page.find_all(".govuk-table__cell"[0])).to have_text "Branch route form"
+ expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
+ end
+ end
+ end
+ end
+
+ context "with forms_with_routes report" do
+ let(:report) { "forms_with_routes" }
+ let(:records) do
+ [
+ { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } }, "metadata" => { "number_of_routes" => 1 } },
+ { "form_id" => 3, "content" => { "name" => "Branch route form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } }, "metadata" => { "number_of_routes" => 2 } },
+ ]
+ end
+
+ describe "page title" do
+ it "matches the heading" do
+ expect(view.content_for(:title)).to eq "Live forms with routes"
+ end
+ end
+
+ it "has a back link to feature usage report" do
+ expect(view.content_for(:back_link)).to have_link("Back to feature and answer type usage", href: report_features_path)
+ end
+
+ it "has a link to download the CSV" do
+ expect(rendered).to have_link("Download data about all live forms with routes as a CSV file", href: report_forms_with_routes_path(format: :csv))
+ end
+
+ describe "questions table" do
+ it "has the correct headers" do
+ page = Capybara.string(rendered.html)
+ within(page.find(".govuk-table__head")) do
+ expect(page.find_all(".govuk-table__header"[0])).to have_text "Form name"
+ expect(page.find_all(".govuk-table__header"[1])).to have_text "Organisation"
+ expect(page.find_all(".govuk-table__header"[2])).to have_text "Number of routes"
+ end
+ end
+
+ it "has rows for each question" do
+ page = Capybara.string(rendered.html)
+ within(page.find_all(".govuk-table__row")[1]) do
+ expect(page.find_all(".govuk-table__cell"[0])).to have_text "All question types form"
+ expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
+ expect(page.find_all(".govuk-table__cell"[2])).to have_text "1"
+ end
+ within(page.find_all(".govuk-table__row")[2]) do
+ expect(page.find_all(".govuk-table__cell"[0])).to have_text "Branch route form"
+ expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
+ expect(page.find_all(".govuk-table__cell"[2])).to have_text "2"
+ end
+ end
+ end
+ end
+
+ context "with questions_with_add_another_answer report" do
+ let(:report) { "questions_with_add_another_answer" }
+ let(:records) do
+ [
+ { "type" => "question_page", "data" => { "question_text" => "email address" }, "form" => { "form_id" => 1, "content" => { "name" => "all question types form" }, "group" => { "organisation" => { "name" => "government digital service" } } } },
+ { "type" => "question_page", "data" => { "question_text" => "what’s your email address?" }, "form" => { "form_id" => 3, "content" => { "name" => "branch route form" }, "group" => { "organisation" => { "name" => "government digital service" } } } },
+ ]
+ end
+
+ describe "page title" do
+ it "matches the heading" do
+ expect(view.content_for(:title)).to eq "Questions with add another answer in live forms"
+ end
+ end
+
+ it "has a back link to feature usage report" do
+ expect(view.content_for(:back_link)).to have_link("Back to feature and answer type usage", href: report_features_path)
+ end
+
+ it "has a link to download the CSV" do
+ expect(rendered).to have_link("Download all questions with add another answer in live forms as a CSV file", href: report_questions_with_add_another_answer_path(format: :csv))
+ end
+
+ describe "questions table" do
+ it "has the correct headers" do
+ page = Capybara.string(rendered.html)
+ within(page.find(".govuk-table__head")) do
+ expect(page.find_all(".govuk-table__header"[0])).to have_text "Form name"
+ expect(page.find_all(".govuk-table__header"[1])).to have_text "Organisation"
+ expect(page.find_all(".govuk-table__header"[2])).to have_text "Question text"
+ end
+ end
+
+ it "has rows for each question" do
+ page = Capybara.string(rendered.html)
+ within(page.find_all(".govuk-table__row")[1]) do
+ expect(page.find_all(".govuk-table__cell"[0])).to have_text "All question types form"
+ expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
+ expect(page.find_all(".govuk-table__cell"[2])).to have_text "Email address"
+ end
+ within(page.find_all(".govuk-table__row")[2]) do
+ expect(page.find_all(".govuk-table__cell"[0])).to have_text "Branch route form"
+ expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
+ expect(page.find_all(".govuk-table__cell"[2])).to have_text "What's your email address?"
+ end
+ end
+ end
+ end
+end
diff --git a/spec/views/reports/features.html.erb_spec.rb b/spec/views/reports/features.html.erb_spec.rb
index 7942960d0..e1976b111 100644
--- a/spec/views/reports/features.html.erb_spec.rb
+++ b/spec/views/reports/features.html.erb_spec.rb
@@ -3,8 +3,8 @@
describe "reports/features.html.erb" do
let(:report) do
{
- total_live_forms: 3,
- live_forms_with_answer_type: {
+ total_forms: 3,
+ forms_with_answer_type: {
address: 1,
date: 1,
email: 1,
@@ -16,7 +16,7 @@
selection: 3,
text: 3,
}.with_indifferent_access,
- live_steps_with_answer_type: {
+ steps_with_answer_type: {
address: 1,
date: 1,
email: 1,
@@ -28,10 +28,10 @@
selection: 4,
text: 5,
}.with_indifferent_access,
- live_forms_with_payment: 1,
- live_forms_with_routing: 2,
- live_forms_with_add_another_answer: 3,
- live_forms_with_csv_submission_enabled: 2,
+ forms_with_payment: 1,
+ forms_with_routing: 2,
+ forms_with_add_another_answer: 3,
+ forms_with_csv_submission_enabled: 2,
}
end
@@ -54,7 +54,7 @@
end
it "includes the number of total live forms" do
- expect(rendered).to have_css(".govuk-summary-list__row", text: "Total live forms#{report[:total_live_forms]}")
+ expect(rendered).to have_css(".govuk-summary-list__row", text: "Total live forms#{report[:total_forms]}")
end
Page::ANSWER_TYPES.map(&:to_sym).each do |answer_type|
@@ -63,49 +63,49 @@
end
it "includes the number of live forms with #{answer_type}" do
- expect(rendered).to have_css("[data-live-forms-with-answer-type-#{answer_type.to_s.dasherize}]", text: report[:live_forms_with_answer_type][answer_type].to_s)
+ expect(rendered).to have_css("[data-live-forms-with-answer-type-#{answer_type.to_s.dasherize}]", text: report[:forms_with_answer_type][answer_type].to_s)
end
it "includes the number of live pages with #{answer_type}" do
- expect(rendered).to have_css("[data-live-pages-with-answer-type-#{answer_type.to_s.dasherize}]", text: report[:live_steps_with_answer_type][answer_type].to_s)
+ expect(rendered).to have_css("[data-live-pages-with-answer-type-#{answer_type.to_s.dasherize}]", text: report[:steps_with_answer_type][answer_type].to_s)
end
end
context "when an answer type is missing from the data" do
let(:report) do
{
- total_live_forms: 3,
- live_forms_with_answer_type: { address: 1 },
- live_steps_with_answer_type: { address: 1 },
- live_forms_with_payment: 1,
- live_forms_with_routing: 2,
- live_forms_with_add_another_answer: 3,
- live_forms_with_csv_submission_enabled: 2,
+ total_forms: 3,
+ forms_with_answer_type: { address: 1 },
+ steps_with_answer_type: { address: 1 },
+ forms_with_payment: 1,
+ forms_with_routing: 2,
+ forms_with_add_another_answer: 3,
+ forms_with_csv_submission_enabled: 2,
}
end
- it "displays 0 for live_forms_with_answer_type" do
+ it "displays 0 for forms_with_answer_type" do
expect(rendered).to have_css("[data-live-forms-with-answer-type-number]", text: "0")
end
- it "displays 0 for live_steps_with_answer_type" do
+ it "displays 0 for steps_with_answer_type" do
expect(rendered).to have_css("[data-live-pages-with-answer-type-number]", text: "0")
end
end
it "includes the number of live forms with routes" do
- expect(rendered).to have_css(".govuk-summary-list__row", text: "Live forms with routes#{report[:live_forms_with_routing]}")
+ expect(rendered).to have_css(".govuk-summary-list__row", text: "Live forms with routes#{report[:forms_with_routing]}")
end
it "includes the number of live forms with payments" do
- expect(rendered).to have_css(".govuk-summary-list__row", text: "Live forms with payments#{report[:live_forms_with_payment]}")
+ expect(rendered).to have_css(".govuk-summary-list__row", text: "Live forms with payments#{report[:forms_with_payment]}")
end
it "includes the number of live forms with add another answer" do
- expect(rendered).to have_css(".govuk-summary-list__row", text: "Live forms with add another answer#{report[:live_forms_with_add_another_answer]}")
+ expect(rendered).to have_css(".govuk-summary-list__row", text: "Live forms with add another answer#{report[:forms_with_add_another_answer]}")
end
it "includes the number of live forms with CSV submission enabled" do
- expect(rendered).to have_css(".govuk-summary-list__row", text: "Live forms with CSV submission enabled#{report[:live_forms_with_csv_submission_enabled]}")
+ expect(rendered).to have_css(".govuk-summary-list__row", text: "Live forms with CSV submission enabled#{report[:forms_with_csv_submission_enabled]}")
end
end
diff --git a/spec/views/reports/forms_with_csv_submission_enabled.html.erb_spec.rb b/spec/views/reports/forms_with_csv_submission_enabled.html.erb_spec.rb
deleted file mode 100644
index 05c7fa133..000000000
--- a/spec/views/reports/forms_with_csv_submission_enabled.html.erb_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require "rails_helper"
-
-describe "reports/forms_with_csv_submission_enabled.html.erb" do
- let(:forms) do
- [
- {
- form_name: "All question types form",
- form_id: 1,
- organisation_name: "Government Digital Service",
- },
- {
- form_name: "Branch route form",
- form_id: 3,
- organisation_name: "Government Digital Service",
- },
- ]
- end
-
- before do
- render template: "reports/forms_with_csv_submission_enabled", locals: { forms: }
- end
-
- describe "page title" do
- it "matches the heading" do
- expect(view.content_for(:title)).to eq "Live forms with CSV submission enabled"
- end
- end
-
- it "has a back link to feature usage report" do
- expect(view.content_for(:back_link)).to have_link("Back to feature and answer type usage", href: report_features_path)
- end
-
- it "has a link to download the CSV" do
- expect(rendered).to have_link("Download data about all live forms with CSV submission enabled as a CSV file", href: report_live_forms_with_csv_submission_enabled_csv_path)
- end
-
- describe "questions table" do
- it "has the correct headers" do
- page = Capybara.string(rendered.html)
- within(page.find(".govuk-table__head")) do
- expect(page.find_all(".govuk-table__header"[0])).to have_text "Form name"
- expect(page.find_all(".govuk-table__header"[1])).to have_text "Organisation"
- end
- end
-
- it "has rows for each question" do
- page = Capybara.string(rendered.html)
- within(page.find_all(".govuk-table__row")[1]) do
- expect(page.find_all(".govuk-table__cell"[0])).to have_text "All question types form"
- expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
- end
- within(page.find_all(".govuk-table__row")[2]) do
- expect(page.find_all(".govuk-table__cell"[0])).to have_text "Branch route form"
- expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
- end
- end
- end
-end
diff --git a/spec/views/reports/forms_with_payments.html.erb_spec.rb b/spec/views/reports/forms_with_payments.html.erb_spec.rb
deleted file mode 100644
index e8fec4211..000000000
--- a/spec/views/reports/forms_with_payments.html.erb_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require "rails_helper"
-
-describe "reports/forms_with_payments.html.erb" do
- let(:forms) do
- [
- {
- form_name: "All question types form",
- form_id: 1,
- organisation_name: "Government Digital Service",
- },
- {
- form_name: "Branch route form",
- form_id: 3,
- organisation_name: "Government Digital Service",
- },
- ]
- end
-
- before do
- render template: "reports/forms_with_payments", locals: { forms: }
- end
-
- describe "page title" do
- it "matches the heading" do
- expect(view.content_for(:title)).to eq "Live forms with payments"
- end
- end
-
- it "has a back link to feature usage report" do
- expect(view.content_for(:back_link)).to have_link("Back to feature and answer type usage", href: report_features_path)
- end
-
- it "has a link to download the CSV" do
- expect(rendered).to have_link("Download data about all live forms with payments as a CSV file", href: report_live_forms_with_payments_csv_path)
- end
-
- describe "questions table" do
- it "has the correct headers" do
- page = Capybara.string(rendered.html)
- within(page.find(".govuk-table__head")) do
- expect(page.find_all(".govuk-table__header"[0])).to have_text "Form name"
- expect(page.find_all(".govuk-table__header"[1])).to have_text "Organisation"
- end
- end
-
- it "has rows for each question" do
- page = Capybara.string(rendered.html)
- within(page.find_all(".govuk-table__row")[1]) do
- expect(page.find_all(".govuk-table__cell"[0])).to have_text "All question types form"
- expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
- end
- within(page.find_all(".govuk-table__row")[2]) do
- expect(page.find_all(".govuk-table__cell"[0])).to have_text "Branch route form"
- expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
- end
- end
- end
-end
diff --git a/spec/views/reports/forms_with_routes.html.erb_spec.rb b/spec/views/reports/forms_with_routes.html.erb_spec.rb
deleted file mode 100644
index 7e1947a4d..000000000
--- a/spec/views/reports/forms_with_routes.html.erb_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-require "rails_helper"
-
-describe "reports/forms_with_routes.html.erb" do
- let(:forms) do
- [
- {
- form_name: "All question types form",
- form_id: 1,
- organisation_name: "Government Digital Service",
- number_of_routes: 1,
- },
- {
- form_name: "Branch route form",
- form_id: 3,
- organisation_name: "Government Digital Service",
- number_of_routes: 2,
- },
- ]
- end
-
- before do
- render template: "reports/forms_with_routes", locals: { forms: }
- end
-
- describe "page title" do
- it "matches the heading" do
- expect(view.content_for(:title)).to eq "Live forms with routes"
- end
- end
-
- it "has a back link to feature usage report" do
- expect(view.content_for(:back_link)).to have_link("Back to feature and answer type usage", href: report_features_path)
- end
-
- it "has a link to download the CSV" do
- expect(rendered).to have_link("Download data about all live forms with routes as a CSV file", href: report_live_forms_with_routes_csv_path)
- end
-
- describe "questions table" do
- it "has the correct headers" do
- page = Capybara.string(rendered.html)
- within(page.find(".govuk-table__head")) do
- expect(page.find_all(".govuk-table__header"[0])).to have_text "Form name"
- expect(page.find_all(".govuk-table__header"[1])).to have_text "Organisation"
- expect(page.find_all(".govuk-table__header"[2])).to have_text "Number of routes"
- end
- end
-
- it "has rows for each question" do
- page = Capybara.string(rendered.html)
- within(page.find_all(".govuk-table__row")[1]) do
- expect(page.find_all(".govuk-table__cell"[0])).to have_text "All question types form"
- expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
- expect(page.find_all(".govuk-table__cell"[2])).to have_text "1"
- end
- within(page.find_all(".govuk-table__row")[2]) do
- expect(page.find_all(".govuk-table__cell"[0])).to have_text "Branch route form"
- expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
- expect(page.find_all(".govuk-table__cell"[2])).to have_text "2"
- end
- end
- end
-end
diff --git a/spec/views/reports/questions_with_add_another_answer.html.erb_spec.rb b/spec/views/reports/questions_with_add_another_answer.html.erb_spec.rb
deleted file mode 100644
index a19bfcd0e..000000000
--- a/spec/views/reports/questions_with_add_another_answer.html.erb_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-require "rails_helper"
-
-describe "reports/questions_with_add_another_answer.html.erb" do
- let(:questions) do
- [
- {
- form_name: "All question types form",
- form_id: 1,
- organisation_name: "Government Digital Service",
- question_text: "Email address",
- },
- {
- form_name: "Branch route form",
- form_id: 3,
- organisation_name: "Government Digital Service",
- question_text: "What’s your email address?",
- },
- ]
- end
-
- before do
- render template: "reports/questions_with_add_another_answer", locals: { answer_type: "email", questions: }
- end
-
- describe "page title" do
- it "matches the heading" do
- expect(view.content_for(:title)).to eq "Questions with add another answer in live forms"
- end
- end
-
- it "has a back link to feature usage report" do
- expect(view.content_for(:back_link)).to have_link("Back to feature and answer type usage", href: report_features_path)
- end
-
- it "has a link to download the CSV" do
- expect(rendered).to have_link("Download all questions with add another answer in live forms as a CSV file", href: report_live_questions_with_add_another_answer_csv_path)
- end
-
- describe "questions table" do
- it "has the correct headers" do
- page = Capybara.string(rendered.html)
- within(page.find(".govuk-table__head")) do
- expect(page.find_all(".govuk-table__header"[0])).to have_text "Form name"
- expect(page.find_all(".govuk-table__header"[1])).to have_text "Organisation"
- expect(page.find_all(".govuk-table__header"[2])).to have_text "Question text"
- end
- end
-
- it "has rows for each question" do
- page = Capybara.string(rendered.html)
- within(page.find_all(".govuk-table__row")[1]) do
- expect(page.find_all(".govuk-table__cell"[0])).to have_text "All question types form"
- expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
- expect(page.find_all(".govuk-table__cell"[2])).to have_text "Email address"
- end
- within(page.find_all(".govuk-table__row")[2]) do
- expect(page.find_all(".govuk-table__cell"[0])).to have_text "Branch route form"
- expect(page.find_all(".govuk-table__cell"[1])).to have_text "Government Digital Service"
- expect(page.find_all(".govuk-table__cell"[2])).to have_text "What's your email address?"
- end
- end
- end
-end
diff --git a/spec/views/reports/questions_with_answer_type.html.erb_spec.rb b/spec/views/reports/questions_with_answer_type.html.erb_spec.rb
index 09f2b7ca7..1c72a4add 100644
--- a/spec/views/reports/questions_with_answer_type.html.erb_spec.rb
+++ b/spec/views/reports/questions_with_answer_type.html.erb_spec.rb
@@ -1,25 +1,15 @@
require "rails_helper"
-describe "reports/questions_with_answer_type.html.erb" do
+describe "reports/questions_with_answer_type" do
let(:questions) do
[
- {
- form_name: "All question types form",
- form_id: 1,
- organisation_name: "Government Digital Service",
- question_text: "Email address",
- },
- {
- form_name: "Branch route form",
- form_id: 3,
- organisation_name: "Government Digital Service",
- question_text: "What’s your email address?",
- },
+ { "data" => { "question_text" => "Email address" }, "form" => { "form_id" => 1, "content" => { "name" => "All question types form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } } } },
+ { "data" => { "question_text" => "What’s your email address?" }, "form" => { "form_id" => 3, "content" => { "name" => "Branch route form" }, "group" => { "organisation" => { "name" => "Government Digital Service" } } } },
]
end
before do
- render template: "reports/questions_with_answer_type", locals: { answer_type: "email", questions: }
+ render locals: { answer_type: "email", questions: }
end
describe "page title" do