From 92a587ce91b3598c2822d96ad1e8acac7f193073 Mon Sep 17 00:00:00 2001 From: Raul Gracia Date: Fri, 8 Sep 2023 21:13:04 +0100 Subject: [PATCH 1/2] Adding passport validation --- app/models/applicants/personal_detail.rb | 18 ++++++++++- .../models/applicants/personal_detail_spec.rb | 30 +++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/app/models/applicants/personal_detail.rb b/app/models/applicants/personal_detail.rb index 2f381a3d..418abf59 100644 --- a/app/models/applicants/personal_detail.rb +++ b/app/models/applicants/personal_detail.rb @@ -19,7 +19,8 @@ class PersonalDetail validate :age_less_than_maximum validate :minimum_age validates :sex, presence: true, inclusion: { in: SEX_OPTIONS } - validates :passport_number, presence: true + validates :passport_number, presence: true, length: { maximum: 20, message: "passport number is invalid" } + validate :valid_passport_number validates :nationality, presence: true, inclusion: { in: NATIONALITIES } validates :address_line_1, presence: true validates :city, presence: true @@ -91,6 +92,21 @@ def age_less_than_maximum errors.add(:date_of_birth) end + def valid_passport_number + return if passport_number.blank? + + # Reject if it contains any characters other than alphanumeric + unless passport_number =~ /\A[a-zA-Z0-9]+\z/ + errors.add(:passport_number, 'passport number is invalid') + return + end + + # Reject if it doesn't contain at least one number + unless passport_number =~ /\d/ + errors.add(:passport_number, 'passport number is invalid') + end + end + def minimum_age # rubocop:disable Rails/Blank return unless date_of_birth.present? diff --git a/spec/models/applicants/personal_detail_spec.rb b/spec/models/applicants/personal_detail_spec.rb index 14d719b0..d8621985 100644 --- a/spec/models/applicants/personal_detail_spec.rb +++ b/spec/models/applicants/personal_detail_spec.rb @@ -190,6 +190,36 @@ module Applicants end end end + + describe "passport_number validations" do + subject(:model) { described_class.new(passport_number:) } + + before { model.valid? } + + context "when passport_number is valid" do + valid_passports = %w[PAT34566 A345667 JS3445 434455AFT 2453454] + + valid_passports.each do |valid_passport| + let(:passport_number) { valid_passport } + + it "#{valid_passport} should be valid" do + expect(model.errors.messages_for(:passport_number)).to be_blank + end + end + end + + context "when passport_number is invalid" do + invalid_passports = ["asdfasdf", "nil", "%$^%%^%"] + + invalid_passports.each do |invalid_passport| + let(:passport_number) { invalid_passport } + + it "#{invalid_passport} should be invalid" do + expect(model.errors.messages_for(:passport_number)).to include("passport number is invalid") + end + end + end + end end end end From cacd195deacacc05d36ee8ee6a93a0f674e0e0bc Mon Sep 17 00:00:00 2001 From: Raul Gracia Date: Sat, 9 Sep 2023 02:16:39 +0100 Subject: [PATCH 2/2] Adding passport validation to the steps file too --- app/models/applicants/personal_detail.rb | 10 ++-- app/steps/personal_details_step.rb | 17 +++++++ spec/steps/personal_details_step_spec.rb | 59 ++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/app/models/applicants/personal_detail.rb b/app/models/applicants/personal_detail.rb index 418abf59..5ac761de 100644 --- a/app/models/applicants/personal_detail.rb +++ b/app/models/applicants/personal_detail.rb @@ -19,7 +19,7 @@ class PersonalDetail validate :age_less_than_maximum validate :minimum_age validates :sex, presence: true, inclusion: { in: SEX_OPTIONS } - validates :passport_number, presence: true, length: { maximum: 20, message: "passport number is invalid" } + validates :passport_number, presence: true, length: { maximum: 20, message: "passport number is invalid" } # rubocop:disable Rails/I18nLocaleTexts validate :valid_passport_number validates :nationality, presence: true, inclusion: { in: NATIONALITIES } validates :address_line_1, presence: true @@ -96,14 +96,14 @@ def valid_passport_number return if passport_number.blank? # Reject if it contains any characters other than alphanumeric - unless passport_number =~ /\A[a-zA-Z0-9]+\z/ - errors.add(:passport_number, 'passport number is invalid') + unless /\A[a-zA-Z0-9]+\z/.match?(passport_number) + errors.add(:passport_number, "passport number is invalid") return end # Reject if it doesn't contain at least one number - unless passport_number =~ /\d/ - errors.add(:passport_number, 'passport number is invalid') + unless /\d/.match?(passport_number) + errors.add(:passport_number, "passport number is invalid") end end diff --git a/app/steps/personal_details_step.rb b/app/steps/personal_details_step.rb index c3e7c27d..e6057717 100644 --- a/app/steps/personal_details_step.rb +++ b/app/steps/personal_details_step.rb @@ -27,6 +27,8 @@ class PersonalDetailsStep < BaseStep validates :nationality, inclusion: { in: NATIONALITIES } validates :sex, inclusion: { in: SEX_OPTIONS } validates :postcode, postcode: true + validates :passport_number, length: { maximum: 20, message: "passport number is invalid" } # rubocop:disable Rails/I18nLocaleTexts + validate :valid_passport_number validate :date_of_birth_not_in_future validate :age_less_than_maximum validate :minimum_age @@ -60,6 +62,21 @@ def age_less_than_maximum errors.add(:date_of_birth) end + def valid_passport_number + return if passport_number.blank? + + # Reject if it contains any characters other than alphanumeric + unless /\A[a-zA-Z0-9]+\z/.match?(passport_number) + errors.add(:passport_number, "passport number is invalid") + return + end + + # Reject if it doesn't contain at least one number + unless /\d/.match?(passport_number) + errors.add(:passport_number, "passport number is invalid") + end + end + def minimum_age # rubocop:disable Rails/Blank return unless date_of_birth.present? diff --git a/spec/steps/personal_details_step_spec.rb b/spec/steps/personal_details_step_spec.rb index c6fc6d9c..6e7bd6e0 100644 --- a/spec/steps/personal_details_step_spec.rb +++ b/spec/steps/personal_details_step_spec.rb @@ -133,5 +133,64 @@ it { expect(error).to be_blank } end end + + describe "passport_number" do + let(:form) { build(:form, passport_number:) } + let(:error) { step.errors.messages_for(:passport_number) } + + before { step.valid? } + + context "numbers and letters" do + let(:passport_number) { "AB1234567" } + + it "is valid" do + expect(error).to be_blank + end + end + + context "only numbers" do + let(:passport_number) { "123456789" } + + it "is valid" do + expect(error).to be_blank + end + end + + context "only letters" do + let(:passport_number) { "ABCDEFGHI" } + + it "is invalid" do + expect(error).to be_present + end + end + + context "other characters" do + let(:passport_number) { "&^%$£*()" } + + it "is invalid" do + expect(error).to be_present + end + end + + context "length > 20" do + let(:passport_number) { "ABCDEEFGHIJKLMNOPQRSTUVWXYZ1234567890" } + + it "is invalid" do + expect(error).to be_present + end + end + end + + describe "private constants" do + it "has MAX_AGE set to 80" do + max_age = described_class.send(:const_get, :MAX_AGE) + expect(max_age).to eq(80) + end + + it "has MIN_AGE set to 22" do + min_age = described_class.send(:const_get, :MIN_AGE) + expect(min_age).to eq(22) + end + end end end