diff --git a/Gemfile.lock b/Gemfile.lock index 30c8fd5975..d316bc75e3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -628,6 +628,7 @@ PLATFORMS arm64-darwin-23 x86_64-darwin-21 x86_64-darwin-22 + x86_64-darwin-23 x86_64-linux DEPENDENCIES diff --git a/app/controllers/teacher_interface/registration_number_controller.rb b/app/controllers/teacher_interface/registration_number_controller.rb index b36330dfe1..8433e14d38 100644 --- a/app/controllers/teacher_interface/registration_number_controller.rb +++ b/app/controllers/teacher_interface/registration_number_controller.rb @@ -10,24 +10,51 @@ class RegistrationNumberController < BaseController def edit @form = - RegistrationNumberForm.new( + form_class.new( application_form:, registration_number: application_form.registration_number, ) + + render view_name end def update - @form = RegistrationNumberForm.new(form_params.merge(application_form:)) + @form = form_class.new(form_params.merge(application_form:)) - handle_application_form_section(form: @form) + handle_application_form_section( + form: @form, + if_failure_then_render: view_name, + ) end private + def ghana? + CountryCode.ghana?(application_form.country.code) + end + def form_params - params.require(:teacher_interface_registration_number_form).permit( - :registration_number, - ) + if ghana? + params.require( + :teacher_interface_ghana_registration_number_form, + ).permit( + :license_number_part_one, + :license_number_part_two, + :license_number_part_three, + ) + else + params.require(:teacher_interface_registration_number_form).permit( + :registration_number, + ) + end + end + + def view_name + ghana? ? :edit_ghana : :edit + end + + def form_class + ghana? ? GhanaRegistrationNumberForm : RegistrationNumberForm end end end diff --git a/app/forms/teacher_interface/ghana_registration_number_form.rb b/app/forms/teacher_interface/ghana_registration_number_form.rb new file mode 100644 index 0000000000..4b69169da8 --- /dev/null +++ b/app/forms/teacher_interface/ghana_registration_number_form.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module TeacherInterface + class GhanaRegistrationNumberForm < BaseForm + attr_accessor :application_form + + attribute :registration_number, :string + attribute :license_number_part_one, :string + attribute :license_number_part_two, :string + attribute :license_number_part_three, :string + + validates :application_form, presence: true + + validate :license_number_parts_valid + + def initialize(attributes = {}) + super + + if attributes[:registration_number].present? + license_number_parts = attributes[:registration_number].split("/") + + self.license_number_part_one = license_number_parts[0] + self.license_number_part_two = license_number_parts[1] + self.license_number_part_three = license_number_parts[2] + end + end + + def update_model + application_form.update!(registration_number:) + end + + def registration_number_validator + @registration_number_validator ||= + RegistrationNumberValidators::Ghana.new(registration_number:) + end + + private + + def license_number_parts_valid + return if registration_number_validator.valid? + + errors.add(:registration_number, :invalid) + end + + def registration_number + registration_number_parts = [ + license_number_part_one, + license_number_part_two, + license_number_part_three, + ].map(&:strip) + + return nil if registration_number_parts.compact_blank.empty? + + registration_number_parts.join("/") + end + end +end diff --git a/app/lib/application_form_section_status_updater.rb b/app/lib/application_form_section_status_updater.rb index 513c726482..756517560e 100644 --- a/app/lib/application_form_section_status_updater.rb +++ b/app/lib/application_form_section_status_updater.rb @@ -170,7 +170,17 @@ def work_history_status end def registration_number_status - registration_number.nil? ? :not_started : :completed + if CountryCode.ghana?(application_form.country.code) + if registration_number.present? + registration_number_validator = + RegistrationNumberValidators::Ghana.new(registration_number:) + registration_number_validator.valid? ? :completed : :in_progress + else + :not_started + end + else + registration_number.nil? ? :not_started : :completed + end end def written_statement_status diff --git a/app/lib/country_code.rb b/app/lib/country_code.rb index c263a95152..8b17e4ec98 100644 --- a/app/lib/country_code.rb +++ b/app/lib/country_code.rb @@ -26,6 +26,10 @@ def northern_ireland?(code) code == "GB-NIR" end + def ghana?(code) + code == "GH" + end + def european_economic_area?(code) Country::CODES_IN_EUROPEAN_ECONOMIC_AREA.include?(code) end diff --git a/app/lib/registration_number_validators/ghana.rb b/app/lib/registration_number_validators/ghana.rb new file mode 100644 index 0000000000..522bc2d242 --- /dev/null +++ b/app/lib/registration_number_validators/ghana.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module RegistrationNumberValidators + class Ghana + def initialize(registration_number:) + @registration_number = registration_number + + registration_number_parts = registration_number.to_s.split("/") + + @license_number_part_one = registration_number_parts[0] + @license_number_part_two = registration_number_parts[1] + @license_number_part_three = registration_number_parts[2] + end + + def valid? + license_number_part_one_valid? && license_number_part_two_valid? && + license_number_part_three_valid? + end + + def license_number_part_one_valid? + license_number_part_one.to_s.length == 2 && + license_number_part_one.match?(/\A[a-zA-Z]*\z/) + end + + def license_number_part_two_valid? + license_number_part_two.to_s.length == 6 && + license_number_part_two.match?(/^[0-9]+$/) + end + + def license_number_part_three_valid? + license_number_part_three.to_s.length == 4 && + license_number_part_three.match?(/^[0-9]+$/) + end + + private + + attr_reader :license_number_part_one, + :license_number_part_two, + :license_number_part_three + end +end diff --git a/app/view_objects/teacher_interface/application_form_view_object.rb b/app/view_objects/teacher_interface/application_form_view_object.rb index 94fd199c05..269e84168c 100644 --- a/app/view_objects/teacher_interface/application_form_view_object.rb +++ b/app/view_objects/teacher_interface/application_form_view_object.rb @@ -206,6 +206,12 @@ def task_list_item_name(key) else I18n.t("application_form.tasks.items.written_statement.upload") end + elsif key == :registration_number + if CountryCode.ghana?(application_form.country.code) + I18n.t("application_form.tasks.items.registration_number.ghana") + else + I18n.t("application_form.tasks.items.registration_number.other") + end else I18n.t("application_form.tasks.items.#{key}") end diff --git a/app/views/shared/application_form/_registration_number_summary.html.erb b/app/views/shared/application_form/_registration_number_summary.html.erb index 923d07d86c..2bd9f1323f 100644 --- a/app/views/shared/application_form/_registration_number_summary.html.erb +++ b/app/views/shared/application_form/_registration_number_summary.html.erb @@ -1,7 +1,7 @@ <%= render(CheckYourAnswersSummary::Component.new( id: "registration-number", model: application_form, - title: I18n.t("application_form.tasks.items.registration_number"), + title: t(CountryCode.ghana?(application_form.country.code) ? "ghana" : "other", scope: %i[application_form tasks items registration_number]), fields: { registration_number: { href: %i[registration_number teacher_interface application_form] diff --git a/app/views/teacher_interface/registration_number/edit.html.erb b/app/views/teacher_interface/registration_number/edit.html.erb index 6f4f3b21e8..e6eaf63261 100644 --- a/app/views/teacher_interface/registration_number/edit.html.erb +++ b/app/views/teacher_interface/registration_number/edit.html.erb @@ -1,4 +1,4 @@ -<% content_for :page_title, title_with_error_prefix(t("application_form.tasks.items.registration_number"), error: @form.errors.any?) %> +<% content_for :page_title, title_with_error_prefix(t("application_form.tasks.items.registration_number.other"), error: @form.errors.any?) %> <% content_for :back_link_url, back_history_path(default: teacher_interface_application_form_path) %> <%= form_with model: @form, url: %i[registration_number teacher_interface application_form] do |f| %> diff --git a/app/views/teacher_interface/registration_number/edit_ghana.html.erb b/app/views/teacher_interface/registration_number/edit_ghana.html.erb new file mode 100644 index 0000000000..d44e19a5bf --- /dev/null +++ b/app/views/teacher_interface/registration_number/edit_ghana.html.erb @@ -0,0 +1,77 @@ +<% content_for :page_title, title_with_error_prefix(t("application_form.tasks.items.registration_number.ghana"), error: @form.errors.any?) %> +<% content_for :back_link_url, back_history_path(default: teacher_interface_application_form_path) %> + +<%= form_with model: @form, url: %i[registration_number teacher_interface application_form] do |f| %> + <%= f.govuk_error_summary %> + +
+ You can get your teacher license number by signing in to your <%= link_to "Teacher Portal Ghana account", "https://tpg.ntc.gov.gh/account/login/teacher" %>.
+You can check your number on the <%= link_to "Teacher Portal Ghana website", "https://tpg.ntc.gov.gh/public/teacher/verify-license" %>.
+If you cannot find your teaching license number, contact the <%= link_to "Ghana National Teaching Council", "https://ntc.gov.gh/" %>.
+ + + ++ We use this number to verify you are registered to teach in Ghana. + If you do not have one, we will not be able to verify you. This means your application will be declined. We do not accept other registration or license numbers. +
+ + + + <%= render "shared/save_submit_buttons", f: %> +<% end %> diff --git a/config/locales/helpers.en.yml b/config/locales/helpers.en.yml index be3155555e..6052a9e38f 100644 --- a/config/locales/helpers.en.yml +++ b/config/locales/helpers.en.yml @@ -48,6 +48,8 @@ en: provider: The test must have been taken within the last 2 years. We cannot accept English language tests from a provider that does not appear on the list. teacher_interface_further_information_request_item_text_form: response: Use the text box below to respond to the assessor’s question. + teacher_interface_ghana_registration_number_form: + registration_number: Your teacher license number is made up of 2 letters, 6 numbers and a final 4 numbers. For example, PT/123456/1234. teacher_interface_has_work_history_form: has_work_history: If you’re a recent university graduate, or you've just completed your teaching qualification, you may not have held a professional teaching position yet. teacher_interface_new_session_form: @@ -223,6 +225,8 @@ en: provider: Upload proof of a Secure English Language Test (SELT) at B2 level, from one of the approved providers. teacher_interface_further_information_request_item_text_form: response: Enter your response + teacher_interface_ghana_registration_number_form: + registration_number: Enter your teacher license number teacher_interface_has_work_history_form: has_work_history_options: true: "Yes" diff --git a/config/locales/teacher_interface.en.yml b/config/locales/teacher_interface.en.yml index 15997003cb..78109336dc 100644 --- a/config/locales/teacher_interface.en.yml +++ b/config/locales/teacher_interface.en.yml @@ -128,7 +128,9 @@ en: subjects: Enter the subjects you can teach english_language: Verify your English language proficiency work_history: Add your work history - registration_number: Enter your registration number + registration_number: + ghana: Enter your teacher license number + other: Enter your registration number written_statement: upload: Upload your written statement provide: Provide your written statement @@ -319,6 +321,10 @@ en: attributes: downloaded: blank: Confirm that you have downloaded the consent document. + teacher_interface/ghana_registration_number_form: + attributes: + registration_number: + invalid: Enter your teacher license number. It is made up of 2 letters, 6 numbers and a final 4 numbers. For example, PT/123456/1234. teacher_interface/reference_request_children_response_form: attributes: children_response: diff --git a/spec/forms/teacher_interface/ghana_registration_number_form_spec.rb b/spec/forms/teacher_interface/ghana_registration_number_form_spec.rb new file mode 100644 index 0000000000..609bbadb78 --- /dev/null +++ b/spec/forms/teacher_interface/ghana_registration_number_form_spec.rb @@ -0,0 +1,132 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe TeacherInterface::GhanaRegistrationNumberForm, type: :model do + subject(:form) do + described_class.new( + application_form:, + license_number_part_one:, + license_number_part_two:, + license_number_part_three:, + ) + end + + let(:application_form) { build(:application_form) } + + describe "validations" do + let(:license_number_part_one) { "" } + let(:license_number_part_two) { "" } + let(:license_number_part_three) { "" } + + it { is_expected.to validate_presence_of(:application_form) } + + context "with empty parts" do + it { is_expected.to be_invalid } + end + + context "with license number parts having invalid number of characters" do + let(:license_number_part_one) { "A" } + let(:license_number_part_two) { "1" } + let(:license_number_part_three) { "244444444" } + + it { is_expected.to be_invalid } + end + + context "with license number parts having invalid format of characters" do + let(:license_number_part_one) { "11" } + let(:license_number_part_two) { "AB&DSE" } + let(:license_number_part_three) { "1%NB" } + + it { is_expected.to be_invalid } + end + + context "with license number parts having spaces in the end" do + let(:license_number_part_one) { " PT " } + let(:license_number_part_two) { "123456 " } + let(:license_number_part_three) { " 1234" } + + it { is_expected.to be_valid } + end + + context "with valid parts" do + let(:license_number_part_one) { "PT" } + let(:license_number_part_two) { "123456" } + let(:license_number_part_three) { "1234" } + + it { is_expected.to be_valid } + end + end + + describe "#save" do + context "with valid parts" do + subject(:save) { form.save(validate: true) } + + let(:license_number_part_one) { "PT" } + let(:license_number_part_two) { "123456" } + let(:license_number_part_three) { "1234" } + + it "sets the registration number to the string" do + expect { save }.to change(application_form, :registration_number).to( + "PT/123456/1234", + ) + end + end + + context "with license number parts having spaces in the end" do + subject(:save) { form.save(validate: true) } + + let(:license_number_part_one) { " PT " } + let(:license_number_part_two) { "123456 " } + let(:license_number_part_three) { " 1234" } + + it "sets the registration number to the string without spaces" do + expect { save }.to change(application_form, :registration_number).to( + "PT/123456/1234", + ) + end + end + + context "with invalid parts" do + subject(:save) { form.save(validate: false) } + + let(:license_number_part_one) { "P2" } + let(:license_number_part_two) { "123%56" } + let(:license_number_part_three) { "1^634" } + + it "sets the registration number to the string" do + expect { save }.to change(application_form, :registration_number).to( + "P2/123%56/1^634", + ) + end + + context "when all parts are empty" do + let(:license_number_part_one) { "" } + let(:license_number_part_two) { "" } + let(:license_number_part_three) { "" } + + before do + application_form.update(registration_number: "PT/123456/1234") + end + + it "sets the registration number to nil" do + expect { save }.to change(application_form, :registration_number).to( + nil, + ) + end + end + + context "when some parts are empty" do + let(:license_number_part_one) { "PT" } + let(:license_number_part_two) { "" } + let(:license_number_part_three) { "111" } + + it "sets the registration number to nil" do + expect { save }.to change(application_form, :registration_number).to( + "PT//111", + ) + end + end + end + end +end diff --git a/spec/lib/application_form_section_status_updater_spec.rb b/spec/lib/application_form_section_status_updater_spec.rb index 7dd9f377e7..ebb86f323a 100644 --- a/spec/lib/application_form_section_status_updater_spec.rb +++ b/spec/lib/application_form_section_status_updater_spec.rb @@ -406,6 +406,37 @@ it { is_expected.to eq("completed") } end + + context "when the application form is from Ghana" do + let(:application_form) do + create( + :application_form, + registration_number:, + region: ghana_country.regions.first, + ) + end + let(:ghana_country) do + create(:country, :with_national_region, code: "GH") + end + + context "without a registration/license number" do + let(:registration_number) { nil } + + it { is_expected.to eq("not_started") } + end + + context "without a valid registration/license number" do + let(:registration_number) { "P/12%3/44N" } + + it { is_expected.to eq("in_progress") } + end + + context "with a valid registration/license number" do + let(:registration_number) { "PT/123456/1234" } + + it { is_expected.to eq("completed") } + end + end end end end diff --git a/spec/lib/registration_number_validators/ghana_spec.rb b/spec/lib/registration_number_validators/ghana_spec.rb new file mode 100644 index 0000000000..d6aad1ba1d --- /dev/null +++ b/spec/lib/registration_number_validators/ghana_spec.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe RegistrationNumberValidators::Ghana, type: :model do + subject(:validator) { described_class.new(registration_number:) } + + let(:registration_number) { "PT/123456/1234" } + + describe "#valid?" do + context "when all three parts of the license number are valid" do + it "returns true" do + expect(validator.valid?).to be(true) + end + end + + context "when the first part of the license number is invalid" do + let(:registration_number) { "P/123456/1234" } + + it "returns false" do + expect(validator.valid?).to be(false) + end + end + + context "when the second part of the license number is invalid" do + let(:registration_number) { "PT/123&56/1234" } + + it "returns false" do + expect(validator.valid?).to be(false) + end + end + + context "when the third part of the license number is invalid" do + let(:registration_number) { "PT/123&56/1N34" } + + it "returns false" do + expect(validator.valid?).to be(false) + end + end + end + + describe "#license_number_part_one_valid?" do + context "when the first part of the license number is valid" do + it "returns true" do + expect(validator.license_number_part_one_valid?).to be(true) + end + end + + context "when the first part of the license number has invalid number of characters" do + let(:registration_number) { "PPT/123456/1234" } + + it "returns false" do + expect(validator.license_number_part_one_valid?).to be(false) + end + end + + context "when the first part of the license number has incorrect format" do + let(:registration_number) { "1$/123456/1234" } + + it "returns false" do + expect(validator.license_number_part_one_valid?).to be(false) + end + end + end + + describe "#license_number_part_two_valid?" do + context "when the second part of the license number is valid" do + it "returns true" do + expect(validator.license_number_part_two_valid?).to be(true) + end + end + + context "when the second part of the license number has invalid number of characters" do + let(:registration_number) { "PT/123456789/1234" } + + it "returns false" do + expect(validator.license_number_part_two_valid?).to be(false) + end + end + + context "when the second part of the license number has incorrect format" do + let(:registration_number) { "PT/12 B.5$/1234" } + + it "returns false" do + expect(validator.license_number_part_two_valid?).to be(false) + end + end + end + + describe "#license_number_part_three_valid?" do + context "when the third part of the license number is valid" do + it "returns true" do + expect(validator.license_number_part_three_valid?).to be(true) + end + end + + context "when the third part of the license number has invalid number of characters" do + let(:registration_number) { "PT/123456/122222" } + + it "returns false" do + expect(validator.license_number_part_three_valid?).to be(false) + end + end + + context "when the third part of the license number has incorrect format" do + let(:registration_number) { "PT/123456/123B" } + + it "returns false" do + expect(validator.license_number_part_three_valid?).to be(false) + end + end + end +end diff --git a/spec/support/autoload/page_objects/teacher_interface/application.rb b/spec/support/autoload/page_objects/teacher_interface/application.rb index 20b2cd765f..99196a514d 100644 --- a/spec/support/autoload/page_objects/teacher_interface/application.rb +++ b/spec/support/autoload/page_objects/teacher_interface/application.rb @@ -47,6 +47,10 @@ def registration_number_task_item find_task_list_item("Enter your registration number") end + def ghana_registration_number_task_item + find_task_list_item("Enter your teacher license number") + end + def upload_written_statement_task_item find_task_list_item("Upload your written statement") end diff --git a/spec/support/autoload/page_objects/teacher_interface/ghana_registration_number.rb b/spec/support/autoload/page_objects/teacher_interface/ghana_registration_number.rb new file mode 100644 index 0000000000..d0ab5c8803 --- /dev/null +++ b/spec/support/autoload/page_objects/teacher_interface/ghana_registration_number.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module PageObjects + module TeacherInterface + class GhanaRegistrationNumber < SitePrism::Page + set_url "/teacher/application/registration_number" + + element :heading, "h1" + + section :form, "form" do + element :license_number_part_one_field, + "#teacher-interface-ghana-registration-number-form-license-number-part-one" + element :license_number_part_two_field, + "#teacher-interface-ghana-registration-number-form-license-number-part-two" + element :license_number_part_three_field, + "#teacher-interface-ghana-registration-number-form-license-number-part-three" + element :license_number_error_message, + "#teacher-interface-ghana-registration-number-form-registration-number-field-error" + element :continue_button, ".govuk-button:not(.govuk-button--secondary)" + end + end + end +end diff --git a/spec/support/page_helpers.rb b/spec/support/page_helpers.rb index b9621caa49..9a8ad94290 100644 --- a/spec/support/page_helpers.rb +++ b/spec/support/page_helpers.rb @@ -661,6 +661,11 @@ def teacher_registration_number_page PageObjects::TeacherInterface::RegistrationNumber.new end + def teacher_ghana_registration_number_page + @teacher_registration_number_page = + PageObjects::TeacherInterface::GhanaRegistrationNumber.new + end + def teacher_signed_out_page @teacher_signed_out_page = PageObjects::TeacherInterface::SignedOut.new end diff --git a/spec/system/teacher_interface/registration_numbers/ghana_registration_number_spec.rb b/spec/system/teacher_interface/registration_numbers/ghana_registration_number_spec.rb new file mode 100644 index 0000000000..74525fe9a0 --- /dev/null +++ b/spec/system/teacher_interface/registration_numbers/ghana_registration_number_spec.rb @@ -0,0 +1,114 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe "Teacher Ghana registration number", type: :system do + before do + given_i_am_authorized_as_a_user(teacher) + given_an_application_form_exists + end + + it "records" do + when_i_visit_the(:teacher_application_page) + then_i_see_the(:teacher_application_page) + and_i_see_the_registration_number_task + + when_i_click_the_registration_number_task + then_i_see_the(:teacher_ghana_registration_number_page) + + when_i_fill_in_the_registration_number + then_i_see_the(:teacher_application_page) + and_i_see_the_completed_registration_number_task + end + + context "when the registration number fails validation" do + it "does not record" do + when_i_visit_the(:teacher_application_page) + then_i_see_the(:teacher_application_page) + and_i_see_the_registration_number_task + + when_i_click_the_registration_number_task + then_i_see_the(:teacher_ghana_registration_number_page) + + when_i_fill_in_the_registration_number_incorrectly + then_i_see_the(:teacher_ghana_registration_number_page) + and_i_see_the_validation_failures + end + end + + private + + def given_an_application_form_exists + application_form + end + + def and_i_see_the_registration_number_task + expect( + teacher_application_page.ghana_registration_number_task_item, + ).not_to be_nil + end + + def when_i_click_the_registration_number_task + teacher_application_page.ghana_registration_number_task_item.click + end + + def when_i_fill_in_the_registration_number + teacher_ghana_registration_number_page.form.license_number_part_one_field.fill_in with: + "PT" + teacher_ghana_registration_number_page.form.license_number_part_two_field.fill_in with: + "123456" + teacher_ghana_registration_number_page.form.license_number_part_three_field.fill_in with: + "1234" + teacher_ghana_registration_number_page.form.continue_button.click + end + + def when_i_fill_in_the_registration_number_incorrectly + teacher_ghana_registration_number_page.form.license_number_part_one_field.fill_in with: + "P" + teacher_ghana_registration_number_page.form.license_number_part_two_field.fill_in with: + "1" + teacher_ghana_registration_number_page.form.license_number_part_three_field.fill_in with: + "1" + teacher_ghana_registration_number_page.form.continue_button.click + end + + def and_i_see_the_completed_registration_number_task + expect( + teacher_application_page + .ghana_registration_number_task_item + .status_tag + .text, + ).to eq("Completed") + end + + def and_i_see_the_validation_failures + expect( + teacher_ghana_registration_number_page + .form + .license_number_error_message + .text, + ).to include( + "Enter your teacher license number. " \ + "It is made up of 2 letters, 6 numbers and a final 4 numbers. " \ + "For example, PT/123456/1234.", + ) + end + + def teacher + @teacher ||= create(:teacher) + end + + def application_form + @application_form ||= + create( + :application_form, + teacher:, + region: ghana_country.regions.first, + needs_registration_number: true, + ) + end + + def ghana_country + @ghana_country ||= create(:country, :with_national_region, code: "GH") + end +end